From 20c8675ba46bda97330a4117c459a59a9f1c465e Mon Sep 17 00:00:00 2001 From: Alberto Gonzalez Iniesta Date: Mon, 21 Nov 2016 09:37:33 +0100 Subject: New upstream version 2.4~beta1 --- src/Makefile.in | 49 +- src/compat/Makefile.am | 1 + src/compat/Makefile.in | 60 +- src/compat/compat-gettimeofday.c | 4 +- src/compat/compat-inet_ntop.c | 2 +- src/compat/compat-inet_pton.c | 2 +- src/compat/compat-lz4.c | 1524 ++++++++++++++++++++++++++ src/compat/compat-lz4.h | 360 ++++++ src/compat/compat.vcxproj | 3 +- src/compat/compat.vcxproj.filters | 3 + src/openvpn/Makefile.am | 24 +- src/openvpn/Makefile.in | 214 ++-- src/openvpn/argv.c | 315 ++++++ src/openvpn/argv.h | 70 ++ src/openvpn/base64.c | 6 - src/openvpn/base64.h | 4 - src/openvpn/block_dns.c | 332 ++++++ src/openvpn/block_dns.h | 40 + src/openvpn/buffer.c | 69 +- src/openvpn/buffer.h | 38 +- src/openvpn/clinat.c | 4 - src/openvpn/clinat.h | 2 +- src/openvpn/common.h | 2 +- src/openvpn/comp-lz4.c | 307 ++++++ src/openvpn/comp-lz4.h | 42 + src/openvpn/comp.c | 167 +++ src/openvpn/comp.h | 198 ++++ src/openvpn/compstub.c | 169 +++ src/openvpn/console.c | 228 +--- src/openvpn/console.h | 89 +- src/openvpn/console_builtin.c | 261 +++++ src/openvpn/console_systemd.c | 122 +++ src/openvpn/crypto.c | 821 ++++++++------ src/openvpn/crypto.h | 162 ++- src/openvpn/crypto_backend.h | 112 +- src/openvpn/crypto_mbedtls.c | 785 ++++++++++++++ src/openvpn/crypto_mbedtls.h | 146 +++ src/openvpn/crypto_openssl.c | 282 +++-- src/openvpn/crypto_openssl.h | 7 + src/openvpn/crypto_polarssl.c | 712 ------------ src/openvpn/crypto_polarssl.h | 143 --- src/openvpn/cryptoapi.c | 2 +- src/openvpn/errlevel.h | 4 +- src/openvpn/error.c | 44 +- src/openvpn/error.h | 33 +- src/openvpn/event.c | 8 +- src/openvpn/event.h | 4 +- src/openvpn/fdmisc.c | 4 +- src/openvpn/fdmisc.h | 2 +- src/openvpn/forward-inline.h | 6 +- src/openvpn/forward.c | 304 ++++-- src/openvpn/forward.h | 32 +- src/openvpn/helper.c | 2 - src/openvpn/init.c | 1125 +++++++++++-------- src/openvpn/init.h | 4 +- src/openvpn/interval.h | 9 + src/openvpn/lzo.c | 151 +-- src/openvpn/lzo.h | 222 +--- src/openvpn/manage.c | 449 ++++++-- src/openvpn/manage.h | 39 +- src/openvpn/misc.c | 728 +++---------- src/openvpn/misc.h | 73 +- src/openvpn/mroute.c | 143 +-- src/openvpn/mroute.h | 57 +- src/openvpn/mtcp.c | 34 +- src/openvpn/mtu.c | 44 +- src/openvpn/mtu.h | 8 +- src/openvpn/mudp.c | 104 +- src/openvpn/mudp.h | 2 +- src/openvpn/multi.c | 410 ++++++- src/openvpn/multi.h | 65 +- src/openvpn/occ.c | 2 +- src/openvpn/openvpn.c | 8 +- src/openvpn/openvpn.h | 71 +- src/openvpn/openvpn.vcxproj | 12 +- src/openvpn/openvpn.vcxproj.filters | 18 +- src/openvpn/options.c | 2026 +++++++++++++++++++--------------- src/openvpn/options.h | 130 ++- src/openvpn/otime.h | 8 +- src/openvpn/packet_id.c | 7 +- src/openvpn/packet_id.h | 8 +- src/openvpn/pf.c | 2 +- src/openvpn/pkcs11.c | 7 +- src/openvpn/pkcs11_mbedtls.c | 129 +++ src/openvpn/pkcs11_polarssl.c | 128 --- src/openvpn/platform.c | 20 +- src/openvpn/platform.h | 2 +- src/openvpn/plugin.c | 37 +- src/openvpn/plugin.h | 12 +- src/openvpn/proto.h | 39 + src/openvpn/proxy.c | 131 ++- src/openvpn/proxy.h | 21 +- src/openvpn/ps.c | 59 +- src/openvpn/ps.h | 2 +- src/openvpn/push.c | 444 ++++++-- src/openvpn/push.h | 8 +- src/openvpn/reliable.c | 4 +- src/openvpn/reliable.h | 4 +- src/openvpn/route.c | 1517 ++++++++++++++++++-------- src/openvpn/route.h | 115 +- src/openvpn/session_id.c | 4 +- src/openvpn/session_id.h | 4 +- src/openvpn/shaper.h | 2 +- src/openvpn/sig.c | 67 +- src/openvpn/sig.h | 13 +- src/openvpn/socket.c | 2048 +++++++++++++++++++---------------- src/openvpn/socket.h | 297 +++-- src/openvpn/socks.c | 48 +- src/openvpn/socks.h | 13 +- src/openvpn/ssl.c | 1132 ++++++++++++------- src/openvpn/ssl.h | 102 +- src/openvpn/ssl_backend.h | 67 +- src/openvpn/ssl_common.h | 84 +- src/openvpn/ssl_mbedtls.c | 1226 +++++++++++++++++++++ src/openvpn/ssl_mbedtls.h | 93 ++ src/openvpn/ssl_openssl.c | 301 +++-- src/openvpn/ssl_openssl.h | 7 +- src/openvpn/ssl_polarssl.c | 1160 -------------------- src/openvpn/ssl_polarssl.h | 87 -- src/openvpn/ssl_verify.c | 209 ++-- src/openvpn/ssl_verify.h | 47 +- src/openvpn/ssl_verify_backend.h | 51 +- src/openvpn/ssl_verify_mbedtls.c | 511 +++++++++ src/openvpn/ssl_verify_mbedtls.h | 78 ++ src/openvpn/ssl_verify_openssl.c | 263 +++-- src/openvpn/ssl_verify_polarssl.c | 397 ------- src/openvpn/ssl_verify_polarssl.h | 80 -- src/openvpn/syshead.h | 105 +- src/openvpn/tls_crypt.c | 254 +++++ src/openvpn/tls_crypt.h | 144 +++ src/openvpn/tun.c | 830 +++++++++----- src/openvpn/tun.h | 43 +- src/openvpn/win32.c | 285 ++--- src/openvpn/win32.h | 7 +- src/openvpn/win32_wfp.h | 359 ------ src/openvpnserv/Makefile.am | 16 +- src/openvpnserv/Makefile.in | 181 +++- src/openvpnserv/automatic.c | 415 +++++++ src/openvpnserv/common.c | 218 ++++ src/openvpnserv/interactive.c | 1652 ++++++++++++++++++++++++++++ src/openvpnserv/openvpnserv.c | 534 --------- src/openvpnserv/openvpnserv.vcxproj | 2 +- src/openvpnserv/service.c | 861 ++++----------- src/openvpnserv/service.h | 215 ++-- src/openvpnserv/validate.c | 249 +++++ src/openvpnserv/validate.h | 48 + src/plugins/Makefile.in | 49 +- src/plugins/auth-pam/Makefile.am | 1 + src/plugins/auth-pam/Makefile.in | 61 +- src/plugins/auth-pam/auth-pam.c | 91 +- src/plugins/auth-pam/utils.c | 113 ++ src/plugins/auth-pam/utils.h | 66 ++ src/plugins/down-root/Makefile.in | 57 +- 153 files changed, 20771 insertions(+), 11440 deletions(-) create mode 100644 src/compat/compat-lz4.c create mode 100644 src/compat/compat-lz4.h create mode 100644 src/openvpn/argv.c create mode 100644 src/openvpn/argv.h create mode 100644 src/openvpn/block_dns.c create mode 100644 src/openvpn/block_dns.h create mode 100644 src/openvpn/comp-lz4.c create mode 100644 src/openvpn/comp-lz4.h create mode 100644 src/openvpn/comp.c create mode 100644 src/openvpn/comp.h create mode 100644 src/openvpn/compstub.c create mode 100644 src/openvpn/console_builtin.c create mode 100644 src/openvpn/console_systemd.c create mode 100644 src/openvpn/crypto_mbedtls.c create mode 100644 src/openvpn/crypto_mbedtls.h delete mode 100644 src/openvpn/crypto_polarssl.c delete mode 100644 src/openvpn/crypto_polarssl.h create mode 100644 src/openvpn/pkcs11_mbedtls.c delete mode 100644 src/openvpn/pkcs11_polarssl.c create mode 100644 src/openvpn/ssl_mbedtls.c create mode 100644 src/openvpn/ssl_mbedtls.h delete mode 100644 src/openvpn/ssl_polarssl.c delete mode 100644 src/openvpn/ssl_polarssl.h create mode 100644 src/openvpn/ssl_verify_mbedtls.c create mode 100644 src/openvpn/ssl_verify_mbedtls.h delete mode 100644 src/openvpn/ssl_verify_polarssl.c delete mode 100644 src/openvpn/ssl_verify_polarssl.h create mode 100644 src/openvpn/tls_crypt.c create mode 100644 src/openvpn/tls_crypt.h delete mode 100644 src/openvpn/win32_wfp.h create mode 100644 src/openvpnserv/automatic.c create mode 100644 src/openvpnserv/common.c create mode 100644 src/openvpnserv/interactive.c delete mode 100755 src/openvpnserv/openvpnserv.c create mode 100644 src/openvpnserv/validate.c create mode 100644 src/openvpnserv/validate.h create mode 100644 src/plugins/auth-pam/utils.c create mode 100644 src/plugins/auth-pam/utils.h (limited to 'src') diff --git a/src/Makefile.in b/src/Makefile.in index a90f015..8aeb7f5 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -25,17 +25,7 @@ # Copyright (C) 2006-2012 Alon Bar-Lev # VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -99,6 +89,7 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/m4/ax_socklen_t.m4 \ @@ -109,9 +100,9 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) @@ -169,7 +160,6 @@ am__define_uniq_tagged_files = \ ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -208,6 +198,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -242,25 +233,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -286,8 +283,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -302,6 +297,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -378,6 +378,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -673,8 +674,6 @@ uninstall-am: mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am -.PRECIOUS: Makefile - # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am index 273389e..06bab5c 100644 --- a/src/compat/Makefile.am +++ b/src/compat/Makefile.am @@ -27,4 +27,5 @@ libcompat_la_SOURCES = \ compat-daemon.c \ compat-inet_ntop.c \ compat-inet_pton.c \ + compat-lz4.c compat-lz4.h \ compat-versionhelpers.h diff --git a/src/compat/Makefile.in b/src/compat/Makefile.in index 52970a7..c7236ed 100644 --- a/src/compat/Makefile.in +++ b/src/compat/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -26,17 +26,7 @@ # VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -100,6 +90,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/compat +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/m4/ax_socklen_t.m4 \ @@ -110,16 +102,16 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libcompat_la_LIBADD = am_libcompat_la_OBJECTS = compat-dirname.lo compat-basename.lo \ compat-gettimeofday.lo compat-daemon.lo compat-inet_ntop.lo \ - compat-inet_pton.lo + compat-inet_pton.lo compat-lz4.lo libcompat_la_OBJECTS = $(am_libcompat_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -137,7 +129,7 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -185,7 +177,6 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -199,6 +190,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -233,25 +225,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -277,8 +275,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -293,6 +289,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -367,6 +368,7 @@ libcompat_la_SOURCES = \ compat-daemon.c \ compat-inet_ntop.c \ compat-inet_pton.c \ + compat-lz4.c compat-lz4.h \ compat-versionhelpers.h all: all-am @@ -385,6 +387,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/compat/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/compat/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -429,20 +432,21 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-gettimeofday.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-inet_ntop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-inet_pton.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-lz4.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -661,8 +665,6 @@ uninstall-am: mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am -.PRECIOUS: Makefile - # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/compat/compat-gettimeofday.c b/src/compat/compat-gettimeofday.c index 0f32d5d..19feaae 100644 --- a/src/compat/compat-gettimeofday.c +++ b/src/compat/compat-gettimeofday.c @@ -32,7 +32,7 @@ #include "compat.h" -#ifdef WIN32 +#ifdef _WIN32 /* * NOTICE: mingw has much faster gettimeofday! * autoconf will set HAVE_GETTIMEOFDAY @@ -126,6 +126,6 @@ gettimeofday (struct timeval *tv, void *tz) return 0; } -#endif /* WIN32 */ +#endif /* _WIN32 */ #endif /* HAVE_GETTIMEOFDAY */ diff --git a/src/compat/compat-inet_ntop.c b/src/compat/compat-inet_ntop.c index 0d52142..786c973 100644 --- a/src/compat/compat-inet_ntop.c +++ b/src/compat/compat-inet_ntop.c @@ -32,7 +32,7 @@ #include "compat.h" -#ifdef WIN32 +#ifdef _WIN32 #include diff --git a/src/compat/compat-inet_pton.c b/src/compat/compat-inet_pton.c index cdc8d4b..5965f0d 100644 --- a/src/compat/compat-inet_pton.c +++ b/src/compat/compat-inet_pton.c @@ -32,7 +32,7 @@ #include "compat.h" -#ifdef WIN32 +#ifdef _WIN32 #include #include diff --git a/src/compat/compat-lz4.c b/src/compat/compat-lz4.c new file mode 100644 index 0000000..5855ca1 --- /dev/null +++ b/src/compat/compat-lz4.c @@ -0,0 +1,1524 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2015, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/Cyan4973/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#ifdef NEED_COMPAT_LZ4 + +/************************************** +* Tuning parameters +**************************************/ +/* + * HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#define HEAPMODE 0 + +/* + * ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define ACCELERATION_DEFAULT 1 + + +/************************************** +* CPU Feature Detection +**************************************/ +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + +/************************************** +* Includes +**************************************/ +#include "compat-lz4.h" + + +/************************************** +* Compiler Options +**************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# define FORCE_INLINE static __forceinline +# include +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ +#else +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ +# if defined(__GNUC__) || defined(__clang__) +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif /* _MSC_VER */ + +/* LZ4_GCC_VERSION is defined into lz4.h */ +#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + + +/************************************** +* Memory routines +**************************************/ +#include /* malloc, calloc, free */ +#define ALLOCATOR(n,s) calloc(n,s) +#define FREEMEM free +#include /* memset, memcpy */ +#define MEM_INIT memset + + +/************************************** +* Basic Types +**************************************/ +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +#endif + + +/************************************** +* Reading and writing into memory +**************************************/ +#define STEPSIZE sizeof(size_t) + +static unsigned LZ4_64bits(void) { return sizeof(void*)==8; } + +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val16; + memcpy(&val16, memPtr, 2); + return val16; +} + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) + { + return LZ4_read16(memPtr); + } + else + { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] + (p[1]<<8)); + } +} + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) + { + memcpy(memPtr, &value, 2); + } + else + { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val32; + memcpy(&val32, memPtr, 4); + return val32; +} + +static U64 LZ4_read64(const void* memPtr) +{ + U64 val64; + memcpy(&val64, memPtr, 8); + return val64; +} + +static size_t LZ4_read_ARCH(const void* p) +{ + if (LZ4_64bits()) + return (size_t)LZ4_read64(p); + else + return (size_t)LZ4_read32(p); +} + + +static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); } + +static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); } + +/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ +static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* e = (BYTE*)dstEnd; + do { LZ4_copy8(d,s); d+=8; s+=8; } while (d>3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctzll((U64)val) >> 3); +# else + static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; + return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; +# endif + } + else /* 32 bits */ + { +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward( &r, (U32)val ); + return (int)(r>>3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctz((U32)val) >> 3); +# else + static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; + return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; +# endif + } + } + else /* Big Endian CPU */ + { + if (LZ4_64bits()) + { +# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse64( &r, val ); + return (unsigned)(r>>3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clzll((U64)val) >> 3); +# else + unsigned r; + if (!(val>>32)) { r=4; } else { r=0; val>>=32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +# endif + } + else /* 32 bits */ + { +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse( &r, (unsigned long)val ); + return (unsigned)(r>>3); +# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clz((U32)val) >> 3); +# else + unsigned r; + if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } + r += (!val); + return r; +# endif + } + } +} + +static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + while (likely(pIn compression run slower on incompressible data */ + + +/************************************** +* Local Structures and types +**************************************/ +typedef struct { + U32 hashTable[HASH_SIZE_U32]; + U32 currentOffset; + U32 initCheck; + const BYTE* dictionary; + BYTE* bufferStart; /* obsolete, used for slideInputBuffer */ + U32 dictSize; +} LZ4_stream_t_internal; + +typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive; +typedef enum { byPtr, byU32, byU16 } tableType_t; + +typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + +typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; +typedef enum { full = 0, partial = 1 } earlyEnd_directive; + + +/************************************** +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState() { return LZ4_STREAMSIZE; } + + + +/******************************** +* Compression functions +********************************/ + +static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); + else + return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); +} + +static const U64 prime5bytes = 889523592379ULL; +static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + const U32 hashMask = (1<> (40 - hashLog)) & hashMask; +} + +static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType) +{ + if (LZ4_64bits()) + return LZ4_hashSequence64(sequence, tableType); + return LZ4_hashSequence((U32)sequence, tableType); +} + +static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); } + +static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) +{ + switch (tableType) + { + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } + } +} + +static void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; } + if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; } + { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ +} + +static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); +} + +FORCE_INLINE int LZ4_compress_generic( + void* const ctx, + const char* const source, + char* const dest, + const int inputSize, + const int maxOutputSize, + const limitedOutput_directive outputLimited, + const tableType_t tableType, + const dict_directive dict, + const dictIssue_directive dictIssue, + const U32 acceleration) +{ + LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx; + + const BYTE* ip = (const BYTE*) source; + const BYTE* base; + const BYTE* lowLimit; + const BYTE* const lowRefLimit = ip - dictPtr->dictSize; + const BYTE* const dictionary = dictPtr->dictionary; + const BYTE* const dictEnd = dictionary + dictPtr->dictSize; + const size_t dictDelta = dictEnd - (const BYTE*)source; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 forwardH; + size_t refDelta=0; + + /* Init conditions */ + if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + switch(dict) + { + case noDict: + default: + base = (const BYTE*)source; + lowLimit = (const BYTE*)source; + break; + case withPrefix64k: + base = (const BYTE*)source - dictPtr->currentOffset; + lowLimit = (const BYTE*)source - dictPtr->dictSize; + break; + case usingExtDict: + base = (const BYTE*)source - dictPtr->currentOffset; + lowLimit = (const BYTE*)source; + break; + } + if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ + if (inputSize> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx, tableType, base); + if (dict==usingExtDict) + { + if (match<(const BYTE*)source) + { + refDelta = dictDelta; + lowLimit = dictionary; + } + else + { + refDelta = 0; + lowLimit = (const BYTE*)source; + } + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx, tableType, base); + + } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0) + || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) + || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) ); + } + + /* Catch up */ + while ((ip>anchor) && (match+refDelta > lowLimit) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; } + + { + /* Encode Literal length */ + unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit))) + return 0; /* Check output limit */ + if (litLength>=RUN_MASK) + { + int len = (int)litLength-RUN_MASK; + *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< matchlimit) limit = matchlimit; + matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += MINMATCH + matchLength; + if (ip==limit) + { + unsigned more = LZ4_count(ip, (const BYTE*)source, matchlimit); + matchLength += more; + ip += more; + } + } + else + { + matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += MINMATCH + matchLength; + } + + if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength>>8) > olimit))) + return 0; /* Check output limit */ + if (matchLength>=ML_MASK) + { + *token += ML_MASK; + matchLength -= ML_MASK; + for (; matchLength >= 510 ; matchLength-=510) { *op++ = 255; *op++ = 255; } + if (matchLength >= 255) { matchLength-=255; *op++ = 255; } + *op++ = (BYTE)matchLength; + } + else *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of chunk */ + if (ip > mflimit) break; + + /* Fill table */ + LZ4_putPosition(ip-2, ctx, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx, tableType, base); + if (dict==usingExtDict) + { + if (match<(const BYTE*)source) + { + refDelta = dictDelta; + lowLimit = dictionary; + } + else + { + refDelta = 0; + lowLimit = (const BYTE*)source; + } + } + LZ4_putPosition(ip, ctx, tableType, base); + if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1) + && (match+MAX_DISTANCE>=ip) + && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { + const size_t lastRun = (size_t)(iend - anchor); + if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) + return 0; /* Check output limit */ + if (lastRun >= RUN_MASK) + { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } + else + { + *op++ = (BYTE)(lastRun<= LZ4_compressBound(inputSize)) + { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } + else + { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ +#if (HEAPMODE) + void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctx; + void* ctxPtr = &ctx; +#endif + + int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); +} + + +/* hidden debug function */ +/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ +int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t ctx; + + LZ4_resetStream(&ctx); + + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); +} + + +/******************************** +* destSize variant +********************************/ + +static int LZ4_compress_destSize_generic( + void* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + const int targetDstSize, + const tableType_t tableType) +{ + const BYTE* ip = (const BYTE*) src; + const BYTE* base = (const BYTE*) src; + const BYTE* lowLimit = (const BYTE*) src; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + targetDstSize; + BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; + BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); + BYTE* const oMaxSeq = oMaxLit - 1 /* token */; + + U32 forwardH; + + + /* Init conditions */ + if (targetDstSize < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ + if (*srcSizePtr> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) + goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx, tableType, base); + + } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + } + + /* Catch up */ + while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + { + /* Encode Literal length */ + unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if (op + ((litLength+240)/255) + litLength > oMaxLit) + { + /* Not enough space for a last match */ + op--; + goto _last_literals; + } + if (litLength>=RUN_MASK) + { + unsigned len = litLength - RUN_MASK; + *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< oMaxMatch) + { + /* Match description too long : reduce it */ + matchLength = (15-1) + (oMaxMatch-op) * 255; + } + //printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH); + ip += MINMATCH + matchLength; + + if (matchLength>=ML_MASK) + { + *token += ML_MASK; + matchLength -= ML_MASK; + while (matchLength >= 255) { matchLength-=255; *op++ = 255; } + *op++ = (BYTE)matchLength; + } + else *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of block */ + if (ip > mflimit) break; + if (op > oMaxSeq) break; + + /* Fill table */ + LZ4_putPosition(ip-2, ctx, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx, tableType, base); + LZ4_putPosition(ip, ctx, tableType, base); + if ( (match+MAX_DISTANCE>=ip) + && (LZ4_read32(match)==LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { + size_t lastRunSize = (size_t)(iend - anchor); + if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) + { + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (oend-op) - 1; + lastRunSize -= (lastRunSize+240)/255; + } + ip = anchor + lastRunSize; + + if (lastRunSize >= RUN_MASK) + { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } + else + { + *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */ + { + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } + else + { + if (*srcSizePtr < LZ4_64Klimit) + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16); + else + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr); + } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (HEAPMODE) + void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctxBody; + void* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/******************************** +* Streaming functions +********************************/ + +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64); + LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ + LZ4_resetStream(lz4s); + return lz4s; +} + +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); +} + +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + FREEMEM(LZ4_stream); + return (0); +} + + +#define HASH_UNIT sizeof(size_t) +int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + const BYTE* base; + + if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ + LZ4_resetStream(LZ4_dict); + + if (dictSize < (int)HASH_UNIT) + { + dict->dictionary = NULL; + dict->dictSize = 0; + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + dict->currentOffset += 64 KB; + base = p - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->currentOffset += dict->dictSize; + + while (p <= dictEnd-HASH_UNIT) + { + LZ4_putPosition(p, dict->hashTable, byU32, base); + p+=3; + } + + return dict->dictSize; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src) +{ + if ((LZ4_dict->currentOffset > 0x80000000) || + ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */ + { + /* rescale hash table */ + U32 delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream; + const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE* smallest = (const BYTE*) source; + if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ + if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd; + LZ4_renormDictT(streamPtr, smallest); + if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + + /* Check overlapping input/dictionary space */ + { + const BYTE* sourceEnd = (const BYTE*) source + inputSize; + if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) + { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == (const BYTE*)source) + { + int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration); + else + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration); + streamPtr->dictSize += (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } + + /* external dictionary mode */ + { + int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration); + else + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration); + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize) +{ + LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict; + int result; + const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE* smallest = dictEnd; + if (smallest > (const BYTE*) source) smallest = (const BYTE*) source; + LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest); + + result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + + return result; +} + + +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict; + const BYTE* previousDictEnd = dict->dictionary + dict->dictSize; + + if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize; + + memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/******************************* +* Decompression functions +*******************************/ +/* + * This generic decompression function cover all use cases. + * It shall be instantiated several times, using different sets of directives + * Note that it is essential this generic function is really inlined, + * in order to remove useless branches during compilation optimization. + */ +FORCE_INLINE int LZ4_decompress_generic( + const char* const source, + char* const dest, + int inputSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ + + int endOnInput, /* endOnOutputSize, endOnInputSize */ + int partialDecoding, /* full, partial */ + int targetOutputSize, /* only used if partialDecoding==partial */ + int dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* == dest if dict == noDict */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + /* Local Variables */ + const BYTE* ip = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + outputSize; + BYTE* cpy; + BYTE* oexit = op + targetOutputSize; + const BYTE* const lowLimit = lowPrefix - dictSize; + + const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; + const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4}; + const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; + + const int safeDecode = (endOnInput==endOnInputSize); + const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + + + /* Special cases */ + if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ + if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ + if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); + + + /* Main Loop */ + while (1) + { + unsigned token; + size_t length; + const BYTE* match; + + /* get literal length */ + token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) + { + unsigned s; + do + { + s = *ip++; + length += s; + } + while (likely((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-COPYLENGTH))) + { + if (partialDecoding) + { + if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ + if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ + } + else + { + if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ + } + memcpy(op, ip, length); + ip += length; + op += length; + break; /* Necessarily EOF, due to parsing restrictions */ + } + LZ4_wildCopy(op, ip, cpy); + ip += length; op = cpy; + + /* get offset */ + match = cpy - LZ4_readLE16(ip); ip+=2; + if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */ + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) + { + unsigned s; + do + { + if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; + s = *ip++; + length += s; + } while (s==255); + if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + + /* check external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) + { + if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ + + if (length <= (size_t)(lowPrefix-match)) + { + /* match can be copied as a single segment from external dictionary */ + match = dictEnd - (lowPrefix-match); + memmove(op, match, length); op += length; + } + else + { + /* match encompass external dictionary and current segment */ + size_t copySize = (size_t)(lowPrefix-match); + memcpy(op, dictEnd - copySize, copySize); + op += copySize; + copySize = length - copySize; + if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */ + { + BYTE* const endOfMatch = op + copySize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } + else + { + memcpy(op, lowPrefix, copySize); + op += copySize; + } + } + continue; + } + + /* copy repeated sequence */ + cpy = op + length; + if (unlikely((op-match)<8)) + { + const size_t dec64 = dec64table[op-match]; + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += dec32table[op-match]; + LZ4_copy4(op+4, match); + op += 8; match -= dec64; + } else { LZ4_copy8(op, match); op+=8; match+=8; } + + if (unlikely(cpy>oend-12)) + { + if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */ + if (op < oend-8) + { + LZ4_wildCopy(op, match, oend-8); + match += (oend-8) - op; + op = oend-8; + } + while (opprefixSize = (size_t) dictSize; + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; + int result; + + if (lz4sd->prefixEnd == (BYTE*)dest) + { + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, full, 0, + usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += result; + lz4sd->prefixEnd += result; + } + else + { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, full, 0, + usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode; + int result; + + if (lz4sd->prefixEnd == (BYTE*)dest) + { + result = LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, full, 0, + usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += originalSize; + lz4sd->prefixEnd += originalSize; + } + else + { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, full, 0, + usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0); + if (dictStart+dictSize == dest) + { + if (dictSize >= (int)(64 KB - 1)) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); + } + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); +} + +/* debug function */ +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + + +/*************************************************** +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } +int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } + +/* +These function names are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } + + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } + +static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base) +{ + MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE); + lz4ds->bufferStart = base; +} + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ + LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer); + return 0; +} + +void* LZ4_create (char* inputBuffer) +{ + void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64); + LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer); + return lz4ds; +} + +char* LZ4_slideInputBuffer (void* LZ4_Data) +{ + LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data; + int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB); + return (char*)(ctx->bufferStart + dictSize); +} + +/* Obsolete streaming decompression functions */ + +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); +} + +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); +} + +#endif /* LZ4_COMMONDEFS_ONLY */ + +#endif /* NEED_COMPAT_LZ4 */ diff --git a/src/compat/compat-lz4.h b/src/compat/compat-lz4.h new file mode 100644 index 0000000..3e74002 --- /dev/null +++ b/src/compat/compat-lz4.h @@ -0,0 +1,360 @@ +/* + LZ4 - Fast LZ compression algorithm + Header File + Copyright (C) 2011-2015, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/Cyan4973/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + +/* + * lz4.h provides block compression functions, and gives full buffer control to programmer. + * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), + * and can let the library handle its own memory, please use lz4frame.h instead. +*/ + +/************************************** +* Version +**************************************/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) +int LZ4_versionNumber (void); + +/************************************** +* Tuning parameter +**************************************/ +/* + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio + * Reduced memory usage can improve speed, due to cache effect + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#define LZ4_MEMORY_USAGE 14 + + +/************************************** +* Simple Functions +**************************************/ + +int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); +int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); + +/* +LZ4_compress_default() : + Compresses 'sourceSize' bytes from buffer 'source' + into already allocated 'dest' buffer of size 'maxDestSize'. + Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). + It also runs faster, so it's a recommended setting. + If the function cannot compress 'source' into a more limited 'dest' budget, + compression stops *immediately*, and the function result is zero. + As a consequence, 'dest' content is not valid. + This function never writes outside 'dest' buffer, nor read outside 'source' buffer. + sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE + maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) + return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) + or 0 if compression fails + +LZ4_decompress_safe() : + compressedSize : is the precise full size of the compressed block. + maxDecompressedSize : is the size of destination buffer, which must be already allocated. + return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) + If destination buffer is not large enough, decoding will stop and output an error code (<0). + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function is protected against buffer overflow exploits, including malicious data packets. + It never writes outside output buffer, nor reads outside input buffer. +*/ + + +/************************************** +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/* +LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) +*/ +int LZ4_compressBound(int inputSize); + +/* +LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows to select an "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. +*/ +int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); + + +/* +LZ4_compress_fast_extState() : + Same compression function, just using an externally allocated memory space to store compression state. + Use LZ4_sizeofState() to know how much memory must be allocated, + and allocate it on 8-bytes boundaries (using malloc() typically). + Then, provide it as 'void* state' to compression function. +*/ +int LZ4_sizeofState(void); +int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); + + +/* +LZ4_compress_destSize() : + Reverse the logic, by compressing as much data as possible from 'source' buffer + into already allocated buffer 'dest' of size 'targetDestSize'. + This function either compresses the entire 'source' content into 'dest' if it's large enough, + or fill 'dest' buffer completely with as much data as possible from 'source'. + *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. + New value is necessarily <= old value. + return : Nb bytes written into 'dest' (necessarily <= targetDestSize) + or 0 if compression fails +*/ +int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); + + +/* +LZ4_decompress_fast() : + originalSize : is the original and therefore uncompressed size + return : the number of bytes read from the source buffer (in other words, the compressed size) + If the source stream is detected malformed, the function will stop decoding and return a negative result. + Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. + note : This function fully respect memory boundaries for properly formed compressed data. + It is a bit faster than LZ4_decompress_safe(). + However, it does not provide any protection against intentionally modified data stream (malicious input). + Use this function in trusted environment only (data to decode comes from a trusted source). +*/ +int LZ4_decompress_fast (const char* source, char* dest, int originalSize); + +/* +LZ4_decompress_safe_partial() : + This function decompress a compressed block of size 'compressedSize' at position 'source' + into destination buffer 'dest' of size 'maxDecompressedSize'. + The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, + reducing decompression time. + return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) + Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. + Always control how many bytes were decoded. + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets +*/ +int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize); + + +/*********************************************** +* Streaming Compression Functions +***********************************************/ +#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) +#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) +/* + * LZ4_stream_t + * information structure to track an LZ4 stream. + * important : init this structure content before first use ! + * note : only allocated directly the structure if you are statically linking LZ4 + * If you are using liblz4 as a DLL, please use below construction methods instead. + */ +typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t; + +/* + * LZ4_resetStream + * Use this function to init an allocated LZ4_stream_t structure + */ +void LZ4_resetStream (LZ4_stream_t* streamPtr); + +/* + * LZ4_createStream will allocate and initialize an LZ4_stream_t structure + * LZ4_freeStream releases its memory. + * In the context of a DLL (liblz4), please use these methods rather than the static struct. + * They are more future proof, in case of a change of LZ4_stream_t size. + */ +LZ4_stream_t* LZ4_createStream(void); +int LZ4_freeStream (LZ4_stream_t* streamPtr); + +/* + * LZ4_loadDict + * Use this function to load a static dictionary into LZ4_stream. + * Any previous data will be forgotten, only 'dictionary' will remain in memory. + * Loading a size of 0 is allowed. + * Return : dictionary size, in bytes (necessarily <= 64 KB) + */ +int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/* + * LZ4_compress_fast_continue + * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. + * Important : Previous data blocks are assumed to still be present and unmodified ! + * 'dst' buffer must be already allocated. + * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. + */ +int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration); + +/* + * LZ4_saveDict + * If previously compressed data block is not guaranteed to remain available at its memory location + * save it into a safer place (char* safeBuffer) + * Note : you don't need to call LZ4_loadDict() afterwards, + * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() + * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error + */ +int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); + + +/************************************************ +* Streaming Decompression Functions +************************************************/ + +#define LZ4_STREAMDECODESIZE_U64 4 +#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) +typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t; +/* + * LZ4_streamDecode_t + * information structure to track an LZ4 stream. + * init this structure content using LZ4_setStreamDecode or memset() before first use ! + * + * In the context of a DLL (liblz4) please prefer usage of construction methods below. + * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future. + * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure + * LZ4_freeStreamDecode releases its memory. + */ +LZ4_streamDecode_t* LZ4_createStreamDecode(void); +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); + +/* + * LZ4_setStreamDecode + * Use this function to instruct where to find the dictionary. + * Setting a size of 0 is allowed (same effect as reset). + * Return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) + In the case of a ring buffers, decoding buffer must be either : + - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) + In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). + - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. + maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including small ones ( < 64 KB). + - _At least_ 64 KB + 8 bytes + maxBlockSize. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including larger than decoding buffer. + Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, + and indicate where it is saved using LZ4_setStreamDecode() +*/ +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); +int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as + a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() + They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. +*/ +int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); +int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); + + + +/************************************** +* Obsolete Functions +**************************************/ +/* Deprecate Warnings */ +/* Should these warnings messages be a problem, + it is generally possible to disable them, + with -Wno-deprecated-declarations for gcc + or _CRT_SECURE_NO_WARNINGS in Visual for example. + You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ +#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK +# define LZ4_DEPRECATE_WARNING_DEFBLOCK +# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# if (LZ4_GCC_VERSION >= 405) || defined(__clang__) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (LZ4_GCC_VERSION >= 301) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") +# define LZ4_DEPRECATED(message) +# endif +#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ + +/* Obsolete compression functions */ +/* These functions are planned to start generate warnings by r131 approximately */ +int LZ4_compress (const char* source, char* dest, int sourceSize); +int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); +int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/* Obsolete decompression functions */ +/* These function names are completely deprecated and must no longer be used. + They are only provided here for compatibility with older programs. + - LZ4_uncompress is the same as LZ4_decompress_fast + - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe + These function prototypes are now disabled; uncomment them only if you really need them. + It is highly recommended to stop using these prototypes and migrate to maintained ones */ +/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ +/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ + +/* Obsolete streaming functions; use new streaming interface whenever possible */ +LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); + +/* Obsolete streaming decoding functions */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + + +#if defined (__cplusplus) +} +#endif diff --git a/src/compat/compat.vcxproj b/src/compat/compat.vcxproj index 1dedb33..d2695e6 100644 --- a/src/compat/compat.vcxproj +++ b/src/compat/compat.vcxproj @@ -79,6 +79,7 @@ + @@ -86,4 +87,4 @@ - \ No newline at end of file + diff --git a/src/compat/compat.vcxproj.filters b/src/compat/compat.vcxproj.filters index 00bb0ff..0f78e86 100644 --- a/src/compat/compat.vcxproj.filters +++ b/src/compat/compat.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 6d02fea..4c18449 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -18,7 +18,7 @@ EXTRA_DIST = \ openvpn.vcxproj \ openvpn.vcxproj.filters -INCLUDES = \ +AM_CPPFLAGS = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/src/compat @@ -26,6 +26,7 @@ AM_CFLAGS = \ $(TAP_CFLAGS) \ $(OPTIONAL_CRYPTO_CFLAGS) \ $(OPTIONAL_LZO_CFLAGS) \ + $(OPTIONAL_LZ4_CFLAGS) \ $(OPTIONAL_PKCS11_HELPER_CFLAGS) if WIN32 # we want unicode entry point but not the macro @@ -35,15 +36,18 @@ endif sbin_PROGRAMS = openvpn openvpn_SOURCES = \ + argv.c argv.h \ base64.c base64.h \ basic.h \ buffer.c buffer.h \ circ_list.h \ clinat.c clinat.h \ common.h \ + comp.c comp.h compstub.c \ + comp-lz4.c comp-lz4.h \ crypto.c crypto.h crypto_backend.h \ crypto_openssl.c crypto_openssl.h \ - crypto_polarssl.c crypto_polarssl.h \ + crypto_mbedtls.c crypto_mbedtls.h \ dhcp.c dhcp.h \ errlevel.h \ error.c error.h \ @@ -65,7 +69,7 @@ openvpn_SOURCES = \ memdbg.h \ misc.c misc.h \ platform.c platform.h \ - console.c console.h \ + console.c console.h console_builtin.c console_systemd.c \ mroute.c mroute.h \ mss.c mss.h \ mstats.c mstats.h \ @@ -77,7 +81,7 @@ openvpn_SOURCES = \ occ.c occ.h occ-inline.h \ pkcs11.c pkcs11.h pkcs11_backend.h \ pkcs11_openssl.c \ - pkcs11_polarssl.c \ + pkcs11_mbedtls.c \ openvpn.c openvpn.h \ options.c options.h \ otime.c otime.h \ @@ -102,26 +106,28 @@ openvpn_SOURCES = \ socks.c socks.h \ ssl.c ssl.h ssl_backend.h \ ssl_openssl.c ssl_openssl.h \ - ssl_polarssl.c ssl_polarssl.h \ + ssl_mbedtls.c ssl_mbedtls.h \ ssl_common.h \ ssl_verify.c ssl_verify.h ssl_verify_backend.h \ ssl_verify_openssl.c ssl_verify_openssl.h \ - ssl_verify_polarssl.c ssl_verify_polarssl.h \ + ssl_verify_mbedtls.c ssl_verify_mbedtls.h \ status.c status.h \ syshead.h \ + tls_crypt.c tls_crypt.h \ tun.c tun.h \ - win32.h win32_wfp.h win32.c \ + win32.h win32.c \ cryptoapi.h cryptoapi.c openvpn_LDADD = \ $(top_builddir)/src/compat/libcompat.la \ $(SOCKETS_LIBS) \ $(OPTIONAL_LZO_LIBS) \ + $(OPTIONAL_LZ4_LIBS) \ $(OPTIONAL_PKCS11_HELPER_LIBS) \ $(OPTIONAL_CRYPTO_LIBS) \ $(OPTIONAL_SELINUX_LIBS) \ $(OPTIONAL_SYSTEMD_LIBS) \ $(OPTIONAL_DL_LIBS) if WIN32 -openvpn_SOURCES += openvpn_win32_resources.rc -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lrpcrt4 -lwinmm +openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 endif diff --git a/src/openvpn/Makefile.in b/src/openvpn/Makefile.in index 3691c96..ff15d6d 100644 --- a/src/openvpn/Makefile.in +++ b/src/openvpn/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -37,17 +37,7 @@ # Required to build Windows resource file VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -110,11 +100,13 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +DIST_COMMON = $(top_srcdir)/build/ltrc.inc $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/depcomp # we want unicode entry point but not the macro @WIN32_TRUE@am__append_1 = -municode -UUNICODE sbin_PROGRAMS = openvpn$(EXEEXT) -@WIN32_TRUE@am__append_2 = openvpn_win32_resources.rc -@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lrpcrt4 -lwinmm +@WIN32_TRUE@am__append_2 = openvpn_win32_resources.rc block_dns.c block_dns.h +@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 subdir = src/openvpn ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ @@ -126,63 +118,68 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" PROGRAMS = $(sbin_PROGRAMS) -am__openvpn_SOURCES_DIST = base64.c base64.h basic.h buffer.c buffer.h \ - circ_list.h clinat.c clinat.h common.h crypto.c crypto.h \ - crypto_backend.h crypto_openssl.c crypto_openssl.h \ - crypto_polarssl.c crypto_polarssl.h dhcp.c dhcp.h errlevel.h \ +am__openvpn_SOURCES_DIST = argv.c argv.h base64.c base64.h basic.h \ + buffer.c buffer.h circ_list.h clinat.c clinat.h common.h \ + comp.c comp.h compstub.c comp-lz4.c comp-lz4.h crypto.c \ + crypto.h crypto_backend.h crypto_openssl.c crypto_openssl.h \ + crypto_mbedtls.c crypto_mbedtls.h dhcp.c dhcp.h errlevel.h \ error.c error.h event.c event.h fdmisc.c fdmisc.h forward.c \ forward.h forward-inline.h fragment.c fragment.h gremlin.c \ gremlin.h helper.c helper.h httpdigest.c httpdigest.h lladdr.c \ lladdr.h init.c init.h integer.h interval.c interval.h list.c \ list.h lzo.c lzo.h manage.c manage.h mbuf.c mbuf.h memdbg.h \ misc.c misc.h platform.c platform.h console.c console.h \ - mroute.c mroute.h mss.c mss.h mstats.c mstats.h mtcp.c mtcp.h \ - mtu.c mtu.h mudp.c mudp.h multi.c multi.h ntlm.c ntlm.h occ.c \ - occ.h occ-inline.h pkcs11.c pkcs11.h pkcs11_backend.h \ - pkcs11_openssl.c pkcs11_polarssl.c openvpn.c openvpn.h \ - options.c options.h otime.c otime.h packet_id.c packet_id.h \ - perf.c perf.h pf.c pf.h pf-inline.h ping.c ping.h \ - ping-inline.h plugin.c plugin.h pool.c pool.h proto.c proto.h \ - proxy.c proxy.h ps.c ps.h push.c push.h pushlist.h reliable.c \ - reliable.h route.c route.h schedule.c schedule.h session_id.c \ - session_id.h shaper.c shaper.h sig.c sig.h socket.c socket.h \ - socks.c socks.h ssl.c ssl.h ssl_backend.h ssl_openssl.c \ - ssl_openssl.h ssl_polarssl.c ssl_polarssl.h ssl_common.h \ - ssl_verify.c ssl_verify.h ssl_verify_backend.h \ - ssl_verify_openssl.c ssl_verify_openssl.h \ - ssl_verify_polarssl.c ssl_verify_polarssl.h status.c status.h \ - syshead.h tun.c tun.h win32.h win32_wfp.h win32.c cryptoapi.h \ - cryptoapi.c openvpn_win32_resources.rc -@WIN32_TRUE@am__objects_1 = openvpn_win32_resources.$(OBJEXT) -am_openvpn_OBJECTS = base64.$(OBJEXT) buffer.$(OBJEXT) \ - clinat.$(OBJEXT) crypto.$(OBJEXT) crypto_openssl.$(OBJEXT) \ - crypto_polarssl.$(OBJEXT) dhcp.$(OBJEXT) error.$(OBJEXT) \ + console_builtin.c console_systemd.c mroute.c mroute.h mss.c \ + mss.h mstats.c mstats.h mtcp.c mtcp.h mtu.c mtu.h mudp.c \ + mudp.h multi.c multi.h ntlm.c ntlm.h occ.c occ.h occ-inline.h \ + pkcs11.c pkcs11.h pkcs11_backend.h pkcs11_openssl.c \ + pkcs11_mbedtls.c openvpn.c openvpn.h options.c options.h \ + otime.c otime.h packet_id.c packet_id.h perf.c perf.h pf.c \ + pf.h pf-inline.h ping.c ping.h ping-inline.h plugin.c plugin.h \ + pool.c pool.h proto.c proto.h proxy.c proxy.h ps.c ps.h push.c \ + push.h pushlist.h reliable.c reliable.h route.c route.h \ + schedule.c schedule.h session_id.c session_id.h shaper.c \ + shaper.h sig.c sig.h socket.c socket.h socks.c socks.h ssl.c \ + ssl.h ssl_backend.h ssl_openssl.c ssl_openssl.h ssl_mbedtls.c \ + ssl_mbedtls.h ssl_common.h ssl_verify.c ssl_verify.h \ + ssl_verify_backend.h ssl_verify_openssl.c ssl_verify_openssl.h \ + ssl_verify_mbedtls.c ssl_verify_mbedtls.h status.c status.h \ + syshead.h tls_crypt.c tls_crypt.h tun.c tun.h win32.h win32.c \ + cryptoapi.h cryptoapi.c openvpn_win32_resources.rc block_dns.c \ + block_dns.h +@WIN32_TRUE@am__objects_1 = openvpn_win32_resources.$(OBJEXT) \ +@WIN32_TRUE@ block_dns.$(OBJEXT) +am_openvpn_OBJECTS = argv.$(OBJEXT) base64.$(OBJEXT) buffer.$(OBJEXT) \ + clinat.$(OBJEXT) comp.$(OBJEXT) compstub.$(OBJEXT) \ + comp-lz4.$(OBJEXT) crypto.$(OBJEXT) crypto_openssl.$(OBJEXT) \ + crypto_mbedtls.$(OBJEXT) dhcp.$(OBJEXT) error.$(OBJEXT) \ event.$(OBJEXT) fdmisc.$(OBJEXT) forward.$(OBJEXT) \ fragment.$(OBJEXT) gremlin.$(OBJEXT) helper.$(OBJEXT) \ httpdigest.$(OBJEXT) lladdr.$(OBJEXT) init.$(OBJEXT) \ interval.$(OBJEXT) list.$(OBJEXT) lzo.$(OBJEXT) \ manage.$(OBJEXT) mbuf.$(OBJEXT) misc.$(OBJEXT) \ - platform.$(OBJEXT) console.$(OBJEXT) mroute.$(OBJEXT) \ - mss.$(OBJEXT) mstats.$(OBJEXT) mtcp.$(OBJEXT) mtu.$(OBJEXT) \ - mudp.$(OBJEXT) multi.$(OBJEXT) ntlm.$(OBJEXT) occ.$(OBJEXT) \ - pkcs11.$(OBJEXT) pkcs11_openssl.$(OBJEXT) \ - pkcs11_polarssl.$(OBJEXT) openvpn.$(OBJEXT) options.$(OBJEXT) \ - otime.$(OBJEXT) packet_id.$(OBJEXT) perf.$(OBJEXT) \ - pf.$(OBJEXT) ping.$(OBJEXT) plugin.$(OBJEXT) pool.$(OBJEXT) \ - proto.$(OBJEXT) proxy.$(OBJEXT) ps.$(OBJEXT) push.$(OBJEXT) \ - reliable.$(OBJEXT) route.$(OBJEXT) schedule.$(OBJEXT) \ - session_id.$(OBJEXT) shaper.$(OBJEXT) sig.$(OBJEXT) \ - socket.$(OBJEXT) socks.$(OBJEXT) ssl.$(OBJEXT) \ - ssl_openssl.$(OBJEXT) ssl_polarssl.$(OBJEXT) \ - ssl_verify.$(OBJEXT) ssl_verify_openssl.$(OBJEXT) \ - ssl_verify_polarssl.$(OBJEXT) status.$(OBJEXT) tun.$(OBJEXT) \ + platform.$(OBJEXT) console.$(OBJEXT) console_builtin.$(OBJEXT) \ + console_systemd.$(OBJEXT) mroute.$(OBJEXT) mss.$(OBJEXT) \ + mstats.$(OBJEXT) mtcp.$(OBJEXT) mtu.$(OBJEXT) mudp.$(OBJEXT) \ + multi.$(OBJEXT) ntlm.$(OBJEXT) occ.$(OBJEXT) pkcs11.$(OBJEXT) \ + pkcs11_openssl.$(OBJEXT) pkcs11_mbedtls.$(OBJEXT) \ + openvpn.$(OBJEXT) options.$(OBJEXT) otime.$(OBJEXT) \ + packet_id.$(OBJEXT) perf.$(OBJEXT) pf.$(OBJEXT) ping.$(OBJEXT) \ + plugin.$(OBJEXT) pool.$(OBJEXT) proto.$(OBJEXT) \ + proxy.$(OBJEXT) ps.$(OBJEXT) push.$(OBJEXT) reliable.$(OBJEXT) \ + route.$(OBJEXT) schedule.$(OBJEXT) session_id.$(OBJEXT) \ + shaper.$(OBJEXT) sig.$(OBJEXT) socket.$(OBJEXT) \ + socks.$(OBJEXT) ssl.$(OBJEXT) ssl_openssl.$(OBJEXT) \ + ssl_mbedtls.$(OBJEXT) ssl_verify.$(OBJEXT) \ + ssl_verify_openssl.$(OBJEXT) ssl_verify_mbedtls.$(OBJEXT) \ + status.$(OBJEXT) tls_crypt.$(OBJEXT) tun.$(OBJEXT) \ win32.$(OBJEXT) cryptoapi.$(OBJEXT) $(am__objects_1) openvpn_OBJECTS = $(am_openvpn_OBJECTS) am__DEPENDENCIES_1 = @@ -190,7 +187,8 @@ openvpn_DEPENDENCIES = $(top_builddir)/src/compat/libcompat.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent @@ -207,7 +205,7 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -255,8 +253,6 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/ltrc.inc \ - $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -270,6 +266,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -304,25 +301,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -348,8 +351,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -364,6 +365,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -432,42 +438,43 @@ EXTRA_DIST = \ openvpn.vcxproj \ openvpn.vcxproj.filters -INCLUDES = \ +AM_CPPFLAGS = \ -I$(top_srcdir)/include \ -I$(top_srcdir)/src/compat AM_CFLAGS = $(TAP_CFLAGS) $(OPTIONAL_CRYPTO_CFLAGS) \ - $(OPTIONAL_LZO_CFLAGS) $(OPTIONAL_PKCS11_HELPER_CFLAGS) \ - $(am__append_1) -openvpn_SOURCES = base64.c base64.h basic.h buffer.c buffer.h \ - circ_list.h clinat.c clinat.h common.h crypto.c crypto.h \ + $(OPTIONAL_LZO_CFLAGS) $(OPTIONAL_LZ4_CFLAGS) \ + $(OPTIONAL_PKCS11_HELPER_CFLAGS) $(am__append_1) +openvpn_SOURCES = argv.c argv.h base64.c base64.h basic.h buffer.c \ + buffer.h circ_list.h clinat.c clinat.h common.h comp.c comp.h \ + compstub.c comp-lz4.c comp-lz4.h crypto.c crypto.h \ crypto_backend.h crypto_openssl.c crypto_openssl.h \ - crypto_polarssl.c crypto_polarssl.h dhcp.c dhcp.h errlevel.h \ + crypto_mbedtls.c crypto_mbedtls.h dhcp.c dhcp.h errlevel.h \ error.c error.h event.c event.h fdmisc.c fdmisc.h forward.c \ forward.h forward-inline.h fragment.c fragment.h gremlin.c \ gremlin.h helper.c helper.h httpdigest.c httpdigest.h lladdr.c \ lladdr.h init.c init.h integer.h interval.c interval.h list.c \ list.h lzo.c lzo.h manage.c manage.h mbuf.c mbuf.h memdbg.h \ misc.c misc.h platform.c platform.h console.c console.h \ - mroute.c mroute.h mss.c mss.h mstats.c mstats.h mtcp.c mtcp.h \ - mtu.c mtu.h mudp.c mudp.h multi.c multi.h ntlm.c ntlm.h occ.c \ - occ.h occ-inline.h pkcs11.c pkcs11.h pkcs11_backend.h \ - pkcs11_openssl.c pkcs11_polarssl.c openvpn.c openvpn.h \ - options.c options.h otime.c otime.h packet_id.c packet_id.h \ - perf.c perf.h pf.c pf.h pf-inline.h ping.c ping.h \ - ping-inline.h plugin.c plugin.h pool.c pool.h proto.c proto.h \ - proxy.c proxy.h ps.c ps.h push.c push.h pushlist.h reliable.c \ - reliable.h route.c route.h schedule.c schedule.h session_id.c \ - session_id.h shaper.c shaper.h sig.c sig.h socket.c socket.h \ - socks.c socks.h ssl.c ssl.h ssl_backend.h ssl_openssl.c \ - ssl_openssl.h ssl_polarssl.c ssl_polarssl.h ssl_common.h \ - ssl_verify.c ssl_verify.h ssl_verify_backend.h \ - ssl_verify_openssl.c ssl_verify_openssl.h \ - ssl_verify_polarssl.c ssl_verify_polarssl.h status.c status.h \ - syshead.h tun.c tun.h win32.h win32_wfp.h win32.c cryptoapi.h \ - cryptoapi.c $(am__append_2) + console_builtin.c console_systemd.c mroute.c mroute.h mss.c \ + mss.h mstats.c mstats.h mtcp.c mtcp.h mtu.c mtu.h mudp.c \ + mudp.h multi.c multi.h ntlm.c ntlm.h occ.c occ.h occ-inline.h \ + pkcs11.c pkcs11.h pkcs11_backend.h pkcs11_openssl.c \ + pkcs11_mbedtls.c openvpn.c openvpn.h options.c options.h \ + otime.c otime.h packet_id.c packet_id.h perf.c perf.h pf.c \ + pf.h pf-inline.h ping.c ping.h ping-inline.h plugin.c plugin.h \ + pool.c pool.h proto.c proto.h proxy.c proxy.h ps.c ps.h push.c \ + push.h pushlist.h reliable.c reliable.h route.c route.h \ + schedule.c schedule.h session_id.c session_id.h shaper.c \ + shaper.h sig.c sig.h socket.c socket.h socks.c socks.h ssl.c \ + ssl.h ssl_backend.h ssl_openssl.c ssl_openssl.h ssl_mbedtls.c \ + ssl_mbedtls.h ssl_common.h ssl_verify.c ssl_verify.h \ + ssl_verify_backend.h ssl_verify_openssl.c ssl_verify_openssl.h \ + ssl_verify_mbedtls.c ssl_verify_mbedtls.h status.c status.h \ + syshead.h tls_crypt.c tls_crypt.h tun.c tun.h win32.h win32.c \ + cryptoapi.h cryptoapi.c $(am__append_2) openvpn_LDADD = $(top_builddir)/src/compat/libcompat.la \ - $(SOCKETS_LIBS) $(OPTIONAL_LZO_LIBS) \ + $(SOCKETS_LIBS) $(OPTIONAL_LZO_LIBS) $(OPTIONAL_LZ4_LIBS) \ $(OPTIONAL_PKCS11_HELPER_LIBS) $(OPTIONAL_CRYPTO_LIBS) \ $(OPTIONAL_SELINUX_LIBS) $(OPTIONAL_SYSTEMD_LIBS) \ $(OPTIONAL_DL_LIBS) $(am__append_3) @@ -487,6 +494,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am_ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpn/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/openvpn/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -495,7 +503,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/ltrc.inc $(am__empty): +$(top_srcdir)/build/ltrc.inc: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -565,13 +573,20 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/block_dns.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clinat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp-lz4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compstub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console_builtin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console_systemd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_mbedtls.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_openssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_polarssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoapi.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ @@ -607,8 +622,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ping.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_mbedtls.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_openssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_polarssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool.Po@am__quote@ @@ -625,12 +640,13 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_mbedtls.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_openssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_polarssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify_mbedtls.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify_openssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify_polarssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tun.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32.Po@am__quote@ @@ -639,14 +655,14 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -868,8 +884,6 @@ uninstall-am: uninstall-sbinPROGRAMS mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS -.PRECIOUS: Makefile - .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c new file mode 100644 index 0000000..596e59c --- /dev/null +++ b/src/openvpn/argv.c @@ -0,0 +1,315 @@ +/* + * 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. + * + * 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 + * + * + * A printf-like function (that only recognizes a subset of standard printf + * format operators) that prints arguments to an argv list instead + * of a standard string. This is used to build up argv arrays for passing + * to execve. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "argv.h" +#include "options.h" + +static void +argv_init (struct argv *a) +{ + a->capacity = 0; + a->argc = 0; + a->argv = NULL; +} + +struct argv +argv_new (void) +{ + struct argv ret; + argv_init (&ret); + return ret; +} + +void +argv_reset (struct argv *a) +{ + size_t i; + for (i = 0; i < a->argc; ++i) + free (a->argv[i]); + free (a->argv); + argv_init (a); +} + +static void +argv_extend (struct argv *a, const size_t newcap) +{ + if (newcap > a->capacity) + { + char **newargv; + size_t i; + ALLOC_ARRAY_CLEAR (newargv, char *, newcap); + for (i = 0; i < a->argc; ++i) + newargv[i] = a->argv[i]; + free (a->argv); + a->argv = newargv; + a->capacity = newcap; + } +} + +static void +argv_grow (struct argv *a, const size_t add) +{ + const size_t newargc = a->argc + add + 1; + ASSERT (newargc > a->argc); + argv_extend (a, adjust_power_of_2 (newargc)); +} + +static void +argv_append (struct argv *a, char *str) /* str must have been malloced or be NULL */ +{ + argv_grow (a, 1); + a->argv[a->argc++] = str; +} + +static struct argv +argv_clone (const struct argv *a, const size_t headroom) +{ + struct argv r; + size_t i; + + argv_init (&r); + for (i = 0; i < headroom; ++i) + argv_append (&r, NULL); + if (a) + { + for (i = 0; i < a->argc; ++i) + argv_append (&r, string_alloc (a->argv[i], NULL)); + } + return r; +} + +struct argv +argv_insert_head (const struct argv *a, const char *head) +{ + struct argv r; + r = argv_clone (a, 1); + r.argv[0] = string_alloc (head, NULL); + return r; +} + +static char * +argv_term (const char **f) +{ + const char *p = *f; + const char *term = NULL; + size_t termlen = 0; + + if (*p == '\0') + return NULL; + + while (true) + { + const int c = *p; + if (c == '\0') + break; + if (term) + { + if (!isspace (c)) + ++termlen; + else + break; + } + else + { + if (!isspace (c)) + { + term = p; + termlen = 1; + } + } + ++p; + } + *f = p; + + if (term) + { + char *ret; + ASSERT (termlen > 0); + ret = malloc (termlen + 1); + check_malloc_return (ret); + memcpy (ret, term, termlen); + ret[termlen] = '\0'; + return ret; + } + else + return NULL; +} + +const char * +argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags) +{ + if (a->argv) + return print_argv ((const char **)a->argv, gc, flags); + else + return ""; +} + +void +argv_msg (const int msglev, const struct argv *a) +{ + struct gc_arena gc = gc_new (); + msg (msglev, "%s", argv_str (a, &gc, 0)); + gc_free (&gc); +} + +void +argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix) +{ + struct gc_arena gc = gc_new (); + msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0)); + gc_free (&gc); +} + +static void +argv_printf_arglist (struct argv *a, const char *format, va_list arglist) +{ + char *term; + const char *f = format; + + argv_extend (a, 1); /* ensure trailing NULL */ + + while ((term = argv_term (&f)) != NULL) + { + if (term[0] == '%') + { + if (!strcmp (term, "%s")) + { + char *s = va_arg (arglist, char *); + if (!s) + s = ""; + argv_append (a, string_alloc (s, NULL)); + } + else if (!strcmp (term, "%d")) + { + char numstr[64]; + openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int)); + argv_append (a, string_alloc (numstr, NULL)); + } + else if (!strcmp (term, "%u")) + { + char numstr[64]; + openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int)); + argv_append (a, string_alloc (numstr, NULL)); + } + else if (!strcmp (term, "%s/%d")) + { + char numstr[64]; + char *s = va_arg (arglist, char *); + + if (!s) + s = ""; + + openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int)); + + { + const size_t len = strlen(s) + strlen(numstr) + 2; + char *combined = (char *) malloc (len); + check_malloc_return (combined); + + strcpy (combined, s); + strcat (combined, "/"); + strcat (combined, numstr); + argv_append (a, combined); + } + } + else if (!strcmp (term, "%s%sc")) + { + char *s1 = va_arg (arglist, char *); + char *s2 = va_arg (arglist, char *); + char *combined; + + if (!s1) s1 = ""; + if (!s2) s2 = ""; + combined = (char *) malloc (strlen(s1) + strlen(s2) + 1); + check_malloc_return (combined); + strcpy (combined, s1); + strcat (combined, s2); + argv_append (a, combined); + } + else + ASSERT (0); + free (term); + } + else + { + argv_append (a, term); + } + } +} + +void +argv_printf (struct argv *a, const char *format, ...) +{ + va_list arglist; + argv_reset (a); + va_start (arglist, format); + argv_printf_arglist (a, format, arglist); + va_end (arglist); + } + +void +argv_printf_cat (struct argv *a, const char *format, ...) +{ + va_list arglist; + va_start (arglist, format); + argv_printf_arglist (a, format, arglist); + va_end (arglist); +} + +void +argv_parse_cmd (struct argv *a, const char *s) +{ + int nparms; + char *parms[MAX_PARMS + 1]; + struct gc_arena gc = gc_new (); + + argv_reset (a); + argv_extend (a, 1); /* ensure trailing NULL */ + + nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc); + if (nparms) + { + int i; + for (i = 0; i < nparms; ++i) + argv_append (a, string_alloc (parms[i], NULL)); + } + else + argv_append (a, string_alloc (s, NULL)); + + gc_free (&gc); +} diff --git a/src/openvpn/argv.h b/src/openvpn/argv.h new file mode 100644 index 0000000..9aee641 --- /dev/null +++ b/src/openvpn/argv.h @@ -0,0 +1,70 @@ +/* + * 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. + * + * 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 + * + * + * A printf-like function (that only recognizes a subset of standard printf + * format operators) that prints arguments to an argv list instead + * of a standard string. This is used to build up argv arrays for passing + * to execve. + */ + +#ifndef ARGV_H +#define ARGV_H + +#include "buffer.h" + +struct argv { + size_t capacity; + size_t argc; + char **argv; +}; + +struct argv argv_new (void); +void argv_reset (struct argv *a); +const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags); +struct argv argv_insert_head (const struct argv *a, const char *head); +void argv_msg (const int msglev, const struct argv *a); +void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix); +void argv_parse_cmd (struct argv *a, const char *s); + +void argv_printf (struct argv *a, const char *format, ...) +#ifdef __GNUC__ +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format (gnu_printf, 2, 3))) +#else + __attribute__ ((format (__printf__, 2, 3))) +#endif +#endif + ; + +void argv_printf_cat (struct argv *a, const char *format, ...) +#ifdef __GNUC__ +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format (gnu_printf, 2, 3))) +#else + __attribute__ ((format (__printf__, 2, 3))) +#endif +#endif + ; + +#endif diff --git a/src/openvpn/base64.c b/src/openvpn/base64.c index 7dccec2..258b258 100644 --- a/src/openvpn/base64.c +++ b/src/openvpn/base64.c @@ -39,8 +39,6 @@ #include "syshead.h" -#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) || defined(MANAGMENT_EXTERNAL_KEY) - #include "base64.h" #include "memdbg.h" @@ -163,7 +161,3 @@ openvpn_base64_decode(const char *str, void *data, int size) } return q - (unsigned char *) data; } - -#else -static void dummy(void) {} -#endif /* ENABLE_HTTP_PROXY, ENABLE_PKCS11, ENABLE_CLIENT_CR */ diff --git a/src/openvpn/base64.h b/src/openvpn/base64.h index 28a9677..92a195a 100644 --- a/src/openvpn/base64.h +++ b/src/openvpn/base64.h @@ -34,11 +34,7 @@ #ifndef _BASE64_H_ #define _BASE64_H_ -#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) || defined(MANAGMENT_EXTERNAL_KEY) - int openvpn_base64_encode(const void *data, int size, char **str); int openvpn_base64_decode(const char *str, void *data, int size); #endif - -#endif diff --git a/src/openvpn/block_dns.c b/src/openvpn/block_dns.c new file mode 100644 index 0000000..cb3ce88 --- /dev/null +++ b/src/openvpn/block_dns.c @@ -0,0 +1,332 @@ +/* + * 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. + * 2015-2016 + * 2016 Selva Nair + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif +#ifdef HAVE_CONFIG_VERSION_H +#include "config-version.h" +#endif + +#include "syshead.h" + +#ifdef _WIN32 + +#include +#include +#include +#include +#include +#include +#include "block_dns.h" + +/* + * WFP-related defines and GUIDs not in mingw32 + */ + +#ifndef FWPM_SESSION_FLAG_DYNAMIC +#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001 +#endif + +// c38d57d1-05a7-4c33-904f-7fbceee60e82 +DEFINE_GUID( + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + 0xc38d57d1, + 0x05a7, + 0x4c33, + 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82 +); + +// 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 +DEFINE_GUID( + FWPM_LAYER_ALE_AUTH_CONNECT_V6, + 0x4a72393b, + 0x319f, + 0x44bc, + 0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4 +); + +// d78e1e87-8644-4ea5-9437-d809ecefc971 +DEFINE_GUID( + FWPM_CONDITION_ALE_APP_ID, + 0xd78e1e87, + 0x8644, + 0x4ea5, + 0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71 +); + +// c35a604d-d22b-4e1a-91b4-68f674ee674b +DEFINE_GUID( + FWPM_CONDITION_IP_REMOTE_PORT, + 0xc35a604d, + 0xd22b, + 0x4e1a, + 0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b +); + +// 4cd62a49-59c3-4969-b7f3-bda5d32890a4 +DEFINE_GUID( + FWPM_CONDITION_IP_LOCAL_INTERFACE, + 0x4cd62a49, + 0x59c3, + 0x4969, + 0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4 +); + +/* UUID of WFP sublayer used by all instances of openvpn + 2f660d7e-6a37-11e6-a181-001e8c6e04a2 */ +DEFINE_GUID( + OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, + 0x2f660d7e, + 0x6a37, + 0x11e6, + 0xa1, 0x81, 0x00, 0x1e, 0x8c, 0x6e, 0x04, 0xa2 +); + +static WCHAR *FIREWALL_NAME = L"OpenVPN"; + +/* + * Default msg handler does nothing + */ +static inline void +default_msg_handler (DWORD err, const char *msg) +{ + return; +} + +#define CHECK_ERROR(err, msg) \ + if (err) { msg_handler (err, msg); goto out; } + +/* + * Add a persistent sublayer with specified uuid. + */ +static DWORD +add_sublayer (GUID uuid) +{ + FWPM_SESSION0 session; + HANDLE engine = NULL; + DWORD err = 0; + FWPM_SUBLAYER0 sublayer; + + memset (&session, 0, sizeof(session)); + memset (&sublayer, 0, sizeof(sublayer)); + + err = FwpmEngineOpen0 (NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine); + if (err != ERROR_SUCCESS) + goto out; + + sublayer.subLayerKey = uuid; + sublayer.displayData.name = FIREWALL_NAME; + sublayer.displayData.description = FIREWALL_NAME; + sublayer.flags = 0; + sublayer.weight = 0x100; + + /* Add sublayer to the session */ + err = FwpmSubLayerAdd0 (engine, &sublayer, NULL); + +out: + if (engine) + FwpmEngineClose0 (engine); + return err; +} + +/* + * Block outgoing port 53 traffic except for + * (i) adapter with the specified index + * OR + * (ii) processes with the specified executable path + * The firewall filters added here are automatically removed when the process exits or + * on calling delete_block_dns_filters(). + * Arguments: + * engine_handle : On successful return contains the handle for a newly opened fwp session + * in which the filters are added. + * May be closed by passing to delete_block_dns_filters to remove the filters. + * index : The index of adapter for which traffic is permitted. + * exe_path : Path of executable for which traffic is permitted. + * msg_handler : An optional callback function for error reporting. + * Returns 0 on success, a non-zero status code of the last failed action on failure. + */ + +DWORD +add_block_dns_filters (HANDLE *engine_handle, + int index, + const WCHAR *exe_path, + block_dns_msg_handler_t msg_handler + ) +{ + FWPM_SESSION0 session = {0}; + FWPM_SUBLAYER0 *sublayer_ptr = NULL; + NET_LUID tapluid; + UINT64 filterid; + FWP_BYTE_BLOB *openvpnblob = NULL; + FWPM_FILTER0 Filter = {0}; + FWPM_FILTER_CONDITION0 Condition[2] = {0}; + DWORD err = 0; + + if (!msg_handler) + msg_handler = default_msg_handler; + + /* Add temporary filters which don't survive reboots or crashes. */ + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + *engine_handle = NULL; + + err = FwpmEngineOpen0 (NULL, RPC_C_AUTHN_WINNT, NULL, &session, engine_handle); + CHECK_ERROR (err, "FwpEngineOpen: open fwp session failed"); + msg_handler (0, "Block_DNS: WFP engine opened"); + + /* Check sublayer exists and add one if it does not. */ + if (FwpmSubLayerGetByKey0 (*engine_handle, &OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, &sublayer_ptr) + == ERROR_SUCCESS) + { + msg_handler (0, "Block_DNS: Using existing sublayer"); + FwpmFreeMemory0 ((void **)&sublayer_ptr); + } + else + { /* Add a new sublayer -- as another process may add it in the meantime, + do not treat "already exists" as an error */ + err = add_sublayer (OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER); + + if (err == FWP_E_ALREADY_EXISTS || err == ERROR_SUCCESS) + msg_handler (0, "Block_DNS: Added a persistent sublayer with pre-defined UUID"); + else + CHECK_ERROR (err, "add_sublayer: failed to add persistent sublayer"); + } + + err = ConvertInterfaceIndexToLuid (index, &tapluid); + CHECK_ERROR (err, "Convert interface index to luid failed"); + + err = FwpmGetAppIdFromFileName0 (exe_path, &openvpnblob); + CHECK_ERROR (err, "Get byte blob for openvpn executable name failed"); + + /* Prepare filter. */ + Filter.subLayerKey = OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER; + Filter.displayData.name = FIREWALL_NAME; + Filter.weight.type = FWP_UINT8; + Filter.weight.uint8 = 0xF; + Filter.filterCondition = Condition; + Filter.numFilterConditions = 2; + + /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_PERMIT; + + Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + Condition[0].matchType = FWP_MATCH_EQUAL; + Condition[0].conditionValue.type = FWP_UINT16; + Condition[0].conditionValue.uint16 = 53; + + Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID; + Condition[1].matchType = FWP_MATCH_EQUAL; + Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE; + Condition[1].conditionValue.byteBlob = openvpnblob; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to permit IPv4 port 53 traffic from OpenVPN failed"); + + /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to permit IPv6 port 53 traffic from OpenVPN failed"); + + msg_handler (0, "Block_DNS: Added permit filters for exe_path"); + + /* Third filter. Block all IPv4 DNS queries. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_BLOCK; + Filter.weight.type = FWP_EMPTY; + Filter.numFilterConditions = 1; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to block IPv4 DNS traffic failed"); + + /* Forth filter. Block all IPv6 DNS queries. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to block IPv6 DNS traffic failed"); + + msg_handler (0, "Block_DNS: Added block filters for all interfaces"); + + /* Fifth filter. Permit IPv4 DNS queries from TAP. + * Use a non-zero weight so that the permit filters get higher priority + * over the block filter added with automatic weighting */ + + Filter.weight.type = FWP_UINT8; + Filter.weight.uint8 = 0xE; + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_PERMIT; + Filter.numFilterConditions = 2; + + Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE; + Condition[1].matchType = FWP_MATCH_EQUAL; + Condition[1].conditionValue.type = FWP_UINT64; + Condition[1].conditionValue.uint64 = &tapluid.Value; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to permit IPv4 DNS traffic through TAP failed"); + + /* Sixth filter. Permit IPv6 DNS queries from TAP. + * Use same weight as IPv4 filter */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); + CHECK_ERROR (err, "Add filter to permit IPv6 DNS traffic through TAP failed"); + + msg_handler (0, "Block_DNS: Added permit filters for TAP interface"); + +out: + + if (openvpnblob) + FwpmFreeMemory0 ((void **)&openvpnblob); + + if (err && *engine_handle) + { + FwpmEngineClose0 (*engine_handle); + *engine_handle = NULL; + } + + return err; +} + +DWORD +delete_block_dns_filters (HANDLE engine_handle) +{ + DWORD err = 0; + /* + * For dynamic sessions closing the engine removes all filters added in the session + */ + if (engine_handle) + { + err = FwpmEngineClose0(engine_handle); + } + return err; +} + +#endif diff --git a/src/openvpn/block_dns.h b/src/openvpn/block_dns.h new file mode 100644 index 0000000..f8b6d4f --- /dev/null +++ b/src/openvpn/block_dns.h @@ -0,0 +1,40 @@ +/* + * 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) 2016 Selva Nair + * + * 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 + */ + +#ifdef _WIN32 + +#ifndef OPENVPN_BLOCK_DNS_H +#define OPENVPN_BLOCK_DNS_H + +typedef void (*block_dns_msg_handler_t) (DWORD err, const char *msg); + +DWORD +delete_block_dns_filters (HANDLE engine); + +DWORD +add_block_dns_filters (HANDLE *engine, int iface_index, const WCHAR *exe_path, + block_dns_msg_handler_t msg_handler_callback); + +#endif +#endif diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c index fb3b52d..52c6ab9 100644 --- a/src/openvpn/buffer.c +++ b/src/openvpn/buffer.c @@ -254,7 +254,7 @@ buf_puts(struct buffer *buf, const char *str) * * Return false on overflow. * - * This function is duplicated into service-win32/openvpnserv.c + * This functionality is duplicated in src/openvpnserv/common.c * Any modifications here should be done to the other place as well. */ @@ -371,6 +371,44 @@ x_gc_free (struct gc_arena *a) } } +/* + * Functions to handle special objects in gc_entries + */ + +void +x_gc_freespecial (struct gc_arena *a) +{ + struct gc_entry_special *e; + e = a->list_special; + a->list_special = NULL; + + while (e != NULL) + { + struct gc_entry_special *next = e->next; + e->free_fnc (e->addr); + free(e); + e = next; + } +} + +void gc_addspecial (void *addr, void (free_function)(void*), struct gc_arena *a) +{ + ASSERT(a); + struct gc_entry_special *e; +#ifdef DMALLOC + e = (struct gc_entry_special *) openvpn_dmalloc (file, line, sizeof (struct gc_entry_special)); +#else + e = (struct gc_entry_special *) malloc (sizeof (struct gc_entry_special)); +#endif + check_malloc_return (e); + e->free_fnc = free_function; + e->addr = addr; + + e->next = a->list_special; + a->list_special = e; +} + + /* * Transfer src arena to dest, resetting src to an empty arena. */ @@ -397,18 +435,21 @@ gc_transfer (struct gc_arena *dest, struct gc_arena *src) char * format_hex_ex (const uint8_t *data, int size, int maxoutput, - int space_break, const char* separator, + unsigned int space_break_flags, const char* separator, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (maxoutput ? maxoutput : - ((size * 2) + (size / space_break) * (int) strlen (separator) + 2), + ((size * 2) + (size / (space_break_flags & FHE_SPACE_BREAK_MASK)) * (int) strlen (separator) + 2), gc); int i; for (i = 0; i < size; ++i) { - if (separator && i && !(i % space_break)) + if (separator && i && !(i % (space_break_flags & FHE_SPACE_BREAK_MASK))) buf_printf (&out, "%s", separator); - buf_printf (&out, "%02x", data[i]); + if (space_break_flags & FHE_CAPS) + buf_printf (&out, "%02X", data[i]); + else + buf_printf (&out, "%02x", data[i]); } buf_catrunc (&out, "[more...]"); return (char *)out.data; @@ -938,9 +979,6 @@ valign4 (const struct buffer *buf, const char *file, const int line) /* * struct buffer_list */ - -#ifdef ENABLE_BUFFER_LIST - struct buffer_list * buffer_list_new (const int max_size) { @@ -1031,8 +1069,10 @@ buffer_list_peek (struct buffer_list *ol) } void -buffer_list_aggregate (struct buffer_list *bl, const size_t max) +buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep) { + int sep_len = strlen(sep); + if (bl->head) { struct buffer_entry *more = bl->head; @@ -1040,7 +1080,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max) int count = 0; for (count = 0; more && size <= max; ++count) { - size += BLEN(&more->buf); + size += BLEN(&more->buf) + sep_len; more = more->next; } @@ -1057,6 +1097,7 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max) { struct buffer_entry *next = e->next; buf_copy (&f->buf, &e->buf); + buf_write(&f->buf, sep, sep_len); free_buf (&e->buf); free (e); e = next; @@ -1069,6 +1110,12 @@ buffer_list_aggregate (struct buffer_list *bl, const size_t max) } } +void +buffer_list_aggregate (struct buffer_list *bl, const size_t max) +{ + buffer_list_aggregate_separator(bl, max, ""); +} + void buffer_list_pop (struct buffer_list *ol) { @@ -1116,5 +1163,3 @@ buffer_list_file (const char *fn, int max_line_len) } return bl; } - -#endif diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h index 58f0601..8070439 100644 --- a/src/openvpn/buffer.h +++ b/src/openvpn/buffer.h @@ -91,6 +91,18 @@ struct gc_entry * linked list. */ }; +/** + * Gargabe collection entry for a specially allocated structure that needs + * a custom free function to be freed like struct addrinfo + * + */ +struct gc_entry_special +{ + struct gc_entry_special *next; + void (*free_fnc)(void*); + void *addr; +}; + /** * Garbage collection arena used to keep track of dynamically allocated @@ -106,6 +118,7 @@ struct gc_arena { struct gc_entry *list; /**< First element of the linked list of * \c gc_entry structures. */ + struct gc_entry_special *list_special; }; @@ -163,6 +176,9 @@ struct buffer string_alloc_buf (const char *str, struct gc_arena *gc); #endif +void gc_addspecial (void *addr, void (*free_function)(void*), struct gc_arena *a); + + #ifdef BUF_INIT_TRACKING #define buf_init(buf, offset) buf_init_debug (buf, offset, __FILE__, __LINE__) bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line); @@ -172,6 +188,11 @@ bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line) /* inline functions */ +inline static void +gc_freeaddrinfo_callback (void *addr) +{ + freeaddrinfo((struct addrinfo*) addr); +} static inline bool buf_defined (const struct buffer *buf) @@ -382,9 +403,11 @@ bool buf_parse (struct buffer *buf, const int delim, char *line, const int size) /* * Hex dump -- Output a binary buffer to a hex string and return it. */ +#define FHE_SPACE_BREAK_MASK 0xFF /* space_break parameter in lower 8 bits */ +#define FHE_CAPS 0x100 /* output hex in caps */ char * format_hex_ex (const uint8_t *data, int size, int maxoutput, - int space_break, const char* separator, + unsigned int space_break_flags, const char* separator, struct gc_arena *gc); static inline char * @@ -781,6 +804,7 @@ void character_class_debug (void); void gc_transfer (struct gc_arena *dest, struct gc_arena *src); void x_gc_free (struct gc_arena *a); +void x_gc_freespecial (struct gc_arena *a); static inline bool gc_defined (struct gc_arena *a) @@ -792,6 +816,7 @@ static inline void gc_init (struct gc_arena *a) { a->list = NULL; + a->list_special = NULL; } static inline void @@ -804,7 +829,7 @@ static inline struct gc_arena gc_new (void) { struct gc_arena ret; - ret.list = NULL; + gc_init (&ret); return ret; } @@ -813,6 +838,8 @@ gc_free (struct gc_arena *a) { if (a->list) x_gc_free (a); + if (a->list_special) + x_gc_freespecial(a); } static inline void @@ -882,9 +909,6 @@ check_malloc_return (const void *p) /* * Manage lists of buffers */ - -#ifdef ENABLE_BUFFER_LIST - struct buffer_entry { struct buffer buf; @@ -912,9 +936,7 @@ void buffer_list_advance (struct buffer_list *ol, int n); void buffer_list_pop (struct buffer_list *ol); void buffer_list_aggregate (struct buffer_list *bl, const size_t max); +void buffer_list_aggregate_separator (struct buffer_list *bl, const size_t max, const char *sep); struct buffer_list *buffer_list_file (const char *fn, int max_line_len); - -#endif - #endif /* BUFFER_H */ diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c index af75fc9..ddefe12 100644 --- a/src/openvpn/clinat.c +++ b/src/openvpn/clinat.c @@ -30,8 +30,6 @@ #include "syshead.h" -#if defined(ENABLE_CLIENT_NAT) - #include "clinat.h" #include "proto.h" #include "socket.h" @@ -265,5 +263,3 @@ client_nat_transform (const struct client_nat_option_list *list, } } } - -#endif diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h index d55a727..a5779e1 100644 --- a/src/openvpn/clinat.h +++ b/src/openvpn/clinat.h @@ -22,7 +22,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#if !defined(CLINAT_H) && defined(ENABLE_CLIENT_NAT) +#if !defined(CLINAT_H) #define CLINAT_H #include "buffer.h" diff --git a/src/openvpn/common.h b/src/openvpn/common.h index 2f85bec..1134101 100644 --- a/src/openvpn/common.h +++ b/src/openvpn/common.h @@ -30,7 +30,7 @@ */ #ifdef USE_64_BIT_COUNTERS typedef unsigned long long int counter_type; -# ifdef WIN32 +# ifdef _WIN32 # define counter_format "%I64u" # else # define counter_format "%llu" diff --git a/src/openvpn/comp-lz4.c b/src/openvpn/comp-lz4.c new file mode 100644 index 0000000..395f3d2 --- /dev/null +++ b/src/openvpn/comp-lz4.c @@ -0,0 +1,307 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2012 OpenVPN Technologies, Inc. + * Copyright (C) 2013 Gert Doering + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_LZ4) + +#if defined(NEED_COMPAT_LZ4) +#include "compat-lz4.h" +#else +#include "lz4.h" +#endif + +#include "comp.h" +#include "error.h" + +#include "memdbg.h" + +static void +lz4_compress_init (struct compress_context *compctx) +{ + msg (D_INIT_MEDIUM, "LZ4 compression initializing"); + ASSERT(compctx->flags & COMP_F_SWAP); +} + +static void +lz4v2_compress_init (struct compress_context *compctx) +{ + msg (D_INIT_MEDIUM, "LZ4v2 compression initializing"); +} + +static void +lz4_compress_uninit (struct compress_context *compctx) +{ +} + +static bool +do_lz4_compress (struct buffer *buf, + struct buffer *work, + struct compress_context *compctx, + const struct frame* frame) +{ + /* + * In order to attempt compression, length must be at least COMPRESS_THRESHOLD. + */ + if (buf->len >= COMPRESS_THRESHOLD) + { + const size_t ps = PAYLOAD_SIZE (frame); + int zlen_max = ps + COMP_EXTRA_BUFFER (ps); + int zlen; + + ASSERT (buf_init (work, FRAME_HEADROOM (frame))); + ASSERT (buf_safe (work, zlen_max)); + + if (buf->len > ps) + { + dmsg (D_COMP_ERRORS, "LZ4 compression buffer overflow"); + buf->len = 0; + return false; + } + + zlen = LZ4_compress_limitedOutput((const char *)BPTR(buf), (char *)BPTR(work), BLEN(buf), zlen_max ); + + if (zlen <= 0) + { + dmsg (D_COMP_ERRORS, "LZ4 compression error"); + buf->len = 0; + return false; + } + + ASSERT (buf_safe (work, zlen)); + work->len = zlen; + + + dmsg (D_COMP, "LZ4 compress %d -> %d", buf->len, work->len); + compctx->pre_compress += buf->len; + compctx->post_compress += work->len; + return true; + } + return false; +} + + +static void +lz4_compress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + bool compressed; + if (buf->len <= 0) + return; + + compressed = do_lz4_compress(buf, &work, compctx, frame); + + /* On error do_lz4_compress sets buf len to zero, just return */ + if (buf->len == 0) + return; + + /* did compression save us anything? */ + { + uint8_t comp_head_byte = NO_COMPRESS_BYTE_SWAP; + if (compressed && work.len < buf->len) + { + *buf = work; + comp_head_byte = LZ4_COMPRESS_BYTE; + } + + { + uint8_t *head = BPTR (buf); + uint8_t *tail = BEND (buf); + ASSERT (buf_safe (buf, 1)); + ++buf->len; + + /* move head byte of payload to tail */ + *tail = *head; + *head = comp_head_byte; + } + } +} + + +static void +lz4v2_compress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + bool compressed; + if (buf->len <= 0) + return; + + compressed = do_lz4_compress(buf, &work, compctx, frame); + + /* On Error just return */ + if (buf->len == 0) + return; + + /* did compression save us anything? Include 2 byte compression header + in calculation */ + if (compressed && work.len + 2 < buf->len) + { + ASSERT(buf_prepend(&work, 2)); + uint8_t *head = BPTR (&work); + head[0] = COMP_ALGV2_INDICATOR_BYTE; + head[1] = COMP_ALGV2_LZ4_BYTE; + *buf = work; + } + else + { + compv2_escape_data_ifneeded(buf); + } +} + +void +do_lz4_decompress(size_t zlen_max, + struct buffer *work, + struct buffer *buf, + struct compress_context *compctx) +{ + int uncomp_len; + ASSERT (buf_safe (work, zlen_max)); + uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(work), (size_t)BLEN(buf), zlen_max); + if (uncomp_len <= 0) + { + dmsg (D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len); + buf->len = 0; + return; + } + + ASSERT (buf_safe (work, uncomp_len)); + work->len = uncomp_len; + + dmsg (D_COMP, "LZ4 decompress %d -> %d", buf->len, work->len); + compctx->pre_decompress += buf->len; + compctx->post_decompress += work->len; + + *buf = *work; +} + +static void +lz4_decompress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + size_t zlen_max = EXPANDED_SIZE (frame); + uint8_t c; /* flag indicating whether or not our peer compressed */ + + if (buf->len <= 0) + return; + + ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); + + /* do unframing/swap (assumes buf->len > 0) */ + { + uint8_t *head = BPTR (buf); + c = *head; + --buf->len; + *head = *BEND (buf); + } + + if (c == LZ4_COMPRESS_BYTE) /* packet was compressed */ + { + do_lz4_decompress(zlen_max, &work, buf, compctx); + } + else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */ + { + ; + } + else + { + dmsg (D_COMP_ERRORS, "Bad LZ4 decompression header byte: %d", c); + buf->len = 0; + } +} + +static void +lz4v2_decompress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + size_t zlen_max = EXPANDED_SIZE (frame); + uint8_t c; /* flag indicating whether or not our peer compressed */ + + if (buf->len <= 0) + return; + + ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); + + /* do unframing/swap (assumes buf->len > 0) */ + uint8_t *head = BPTR (buf); + c = *head; + + /* Not compressed */ + if (c != COMP_ALGV2_INDICATOR_BYTE) { + return; + } + + /* Packet to short to make sense */ + if (buf->len <= 1) + { + buf->len=0; + return; + } + + c = head[1]; + if (c == COMP_ALGV2_LZ4_BYTE) /* packet was compressed */ + { + buf_advance(buf,2); + do_lz4_decompress(zlen_max, &work, buf, compctx); + } + else if (c == COMP_ALGV2_UNCOMPRESSED_BYTE) + { + buf_advance(buf,2); + } + else + { + dmsg (D_COMP_ERRORS, "Bad LZ4v2 decompression header byte: %d", c); + buf->len = 0; + } +} + +const struct compress_alg lz4_alg = { + "lz4", + lz4_compress_init, + lz4_compress_uninit, + lz4_compress, + lz4_decompress +}; + +const struct compress_alg lz4v2_alg = { + "lz4v2", + lz4v2_compress_init, + lz4_compress_uninit, + lz4v2_compress, + lz4v2_decompress +}; + +#else +static void dummy(void) {} +#endif /* ENABLE_LZ4 */ diff --git a/src/openvpn/comp-lz4.h b/src/openvpn/comp-lz4.h new file mode 100644 index 0000000..7774ca5 --- /dev/null +++ b/src/openvpn/comp-lz4.h @@ -0,0 +1,42 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2012 OpenVPN Technologies, Inc. + * Copyright (C) 2013 Gert Doering + * + * 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 + */ + +#ifndef OPENVPN_COMP_LZ4_H +#define OPENVPN_COMP_LZ4_H + +#if defined(ENABLE_LZ4) + +#include "buffer.h" + +extern const struct compress_alg lz4_alg; +extern const struct compress_alg lz4v2_alg; + +struct lz4_workspace +{ + int dummy; +}; + +#endif /* ENABLE_LZ4 */ +#endif diff --git a/src/openvpn/comp.c b/src/openvpn/comp.c new file mode 100644 index 0000000..499fef9 --- /dev/null +++ b/src/openvpn/comp.c @@ -0,0 +1,167 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2012 OpenVPN Technologies, Inc. + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#ifdef USE_COMP + +#include "comp.h" +#include "error.h" +#include "otime.h" + +#include "memdbg.h" + +struct compress_context * +comp_init(const struct compress_options *opt) +{ + struct compress_context *compctx = NULL; + switch (opt->alg) + { + case COMP_ALG_STUB: + ALLOC_OBJ_CLEAR (compctx, struct compress_context); + compctx->flags = opt->flags; + compctx->alg = comp_stub_alg; + break; + case COMP_ALGV2_UNCOMPRESSED: + ALLOC_OBJ_CLEAR (compctx, struct compress_context); + compctx->flags = opt->flags; + compctx->alg = compv2_stub_alg; + break; +#ifdef ENABLE_LZO + case COMP_ALG_LZO: + ALLOC_OBJ_CLEAR (compctx, struct compress_context); + compctx->flags = opt->flags; + compctx->alg = lzo_alg; + break; +#endif +#ifdef ENABLE_LZ4 + case COMP_ALG_LZ4: + ALLOC_OBJ_CLEAR (compctx, struct compress_context); + compctx->flags = opt->flags; + compctx->alg = lz4_alg; + break; + case COMP_ALGV2_LZ4: + ALLOC_OBJ_CLEAR (compctx, struct compress_context); + compctx->flags = opt->flags; + compctx->alg = lz4v2_alg; + break; +#endif + } + if (compctx) + (*compctx->alg.compress_init)(compctx); + + return compctx; +} + +/* In the v2 compression schemes, an uncompressed packet has + * has no opcode in front, unless the first byte is 0x50. In this + * case the packet needs to be escaped */ +void +compv2_escape_data_ifneeded (struct buffer *buf) +{ + uint8_t *head = BPTR (buf); + if (head[0] != COMP_ALGV2_INDICATOR_BYTE) + return; + + /* Header is 0x50 */ + ASSERT(buf_prepend(buf, 2)); + + head = BPTR (buf); + head[0] = COMP_ALGV2_INDICATOR_BYTE; + head[1] = COMP_ALGV2_UNCOMPRESSED; +} + + +void +comp_uninit(struct compress_context *compctx) +{ + if (compctx) + { + (*compctx->alg.compress_uninit)(compctx); + free(compctx); + } +} + +void +comp_add_to_extra_frame(struct frame *frame) +{ + /* Leave room for our one-byte compressed/didn't-compress prefix byte. */ + frame_add_to_extra_frame (frame, COMP_PREFIX_LEN); +} + +void +comp_add_to_extra_buffer(struct frame *frame) +{ + /* Leave room for compression buffer to expand in worst case scenario + where data is totally uncompressible */ + frame_add_to_extra_buffer (frame, COMP_EXTRA_BUFFER (EXPANDED_SIZE(frame))); +} + +void +comp_print_stats (const struct compress_context *compctx, struct status_output *so) +{ + if (compctx) + { + status_printf (so, "pre-compress bytes," counter_format, compctx->pre_compress); + status_printf (so, "post-compress bytes," counter_format, compctx->post_compress); + status_printf (so, "pre-decompress bytes," counter_format, compctx->pre_decompress); + status_printf (so, "post-decompress bytes," counter_format, compctx->post_decompress); + } +} + +/* + * Tell our peer which compression algorithms we support. + */ +void +comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out) +{ + if (opt) + { + bool lzo_avail = false; + if (!(opt->flags & COMP_F_ADVERTISE_STUBS_ONLY)) + { +#if defined(ENABLE_LZ4) + buf_printf (out, "IV_LZ4=1\n"); + buf_printf (out, "IV_LZ4v2=1\n"); +#endif +#if defined(ENABLE_LZO) + buf_printf (out, "IV_LZO=1\n"); + lzo_avail = true; +#endif + } + if (!lzo_avail) + buf_printf (out, "IV_LZO_STUB=1\n"); + buf_printf (out, "IV_COMP_STUB=1\n"); + buf_printf (out, "IV_COMP_STUBv2=1\n"); + buf_printf (out, "IV_TCPNL=1\n"); + } +} + +#endif /* USE_COMP */ diff --git a/src/openvpn/comp.h b/src/openvpn/comp.h new file mode 100644 index 0000000..9ed9532 --- /dev/null +++ b/src/openvpn/comp.h @@ -0,0 +1,198 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2012 OpenVPN Technologies, Inc. + * + * 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 + */ + +/* + * Generic compression support. Currently we support + * LZO 2 and LZ4. + */ +#ifndef OPENVPN_COMP_H +#define OPENVPN_COMP_H + +#ifdef USE_COMP + +#include "buffer.h" +#include "mtu.h" +#include "common.h" +#include "status.h" + +/* algorithms */ +#define COMP_ALG_UNDEF 0 +#define COMP_ALG_STUB 1 /* support compression command byte and framing without actual compression */ +#define COMP_ALG_LZO 2 /* LZO algorithm */ +#define COMP_ALG_SNAPPY 3 /* Snappy algorithm (no longer supported) */ +#define COMP_ALG_LZ4 4 /* LZ4 algorithm */ + + +/* algorithm v2 */ +#define COMP_ALGV2_UNCOMPRESSED 10 +#define COMP_ALGV2_LZ4 11 +/* +#define COMP_ALGV2_LZO 12 +#define COMP_ALGV2_SNAPPY 13 +*/ + +/* Compression flags */ +#define COMP_F_ADAPTIVE (1<<0) /* COMP_ALG_LZO only */ +#define COMP_F_ASYM (1<<1) /* only downlink is compressed, not uplink */ +#define COMP_F_SWAP (1<<2) /* initial command byte is swapped with last byte in buffer to preserve payload alignment */ +#define COMP_F_ADVERTISE_STUBS_ONLY (1<<3) /* tell server that we only support compression stubs */ + + +/* + * Length of prepended prefix on compressed packets + */ +#define COMP_PREFIX_LEN 1 + +/* + * Prefix bytes + */ + +/* V1 on wire codes */ +/* Initial command byte to tell our peer if we compressed */ +#define LZO_COMPRESS_BYTE 0x66 +#define LZ4_COMPRESS_BYTE 0x69 +#define NO_COMPRESS_BYTE 0xFA +#define NO_COMPRESS_BYTE_SWAP 0xFB /* to maintain payload alignment, replace this byte with last byte of packet */ + +/* V2 on wire code */ +#define COMP_ALGV2_INDICATOR_BYTE 0x50 +#define COMP_ALGV2_UNCOMPRESSED_BYTE 0 +#define COMP_ALGV2_LZ4_BYTE 1 +#define COMP_ALGV2_LZO_BYTE 2 +#define COMP_ALGV2_SNAPPY_BYTE 3 + +/* + * Compress worst case size expansion (for any algorithm) + * + * LZO: len + len/8 + 128 + 3 + * Snappy: len + len/6 + 32 + * LZ4: len + len/255 + 16 (LZ4_COMPRESSBOUND(len)) + */ +#define COMP_EXTRA_BUFFER(len) ((len)/6 + 128 + 3 + COMP_PREFIX_LEN) + +/* + * Don't try to compress any packet smaller than this. + */ +#define COMPRESS_THRESHOLD 100 + +/* Forward declaration of compression context */ +struct compress_context; + +/* + * Virtual methods and other static info for each compression algorithm + */ +struct compress_alg +{ + const char *name; + void (*compress_init)(struct compress_context *compctx); + void (*compress_uninit)(struct compress_context *compctx); + void (*compress)(struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame); + + void (*decompress)(struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame); +}; + +/* + * Headers for each compression implementation + */ +#ifdef ENABLE_LZO +#include "lzo.h" +#endif + +#ifdef ENABLE_LZ4 +#include "comp-lz4.h" +#endif + +/* + * Information that basically identifies a compression + * algorithm and related flags. + */ +struct compress_options +{ + int alg; + unsigned int flags; +}; + +/* + * Workspace union of all supported compression algorithms + */ +union compress_workspace_union +{ +#ifdef ENABLE_LZO + struct lzo_compress_workspace lzo; +#endif +#ifdef ENABLE_LZ4 + struct lz4_workspace lz4; +#endif +}; + +/* + * Context for active compression session + */ +struct compress_context +{ + unsigned int flags; + struct compress_alg alg; + union compress_workspace_union wu; + + /* statistics */ + counter_type pre_decompress; + counter_type post_decompress; + counter_type pre_compress; + counter_type post_compress; +}; + +extern const struct compress_alg comp_stub_alg; +extern const struct compress_alg compv2_stub_alg; + +struct compress_context *comp_init(const struct compress_options *opt); + +void comp_uninit(struct compress_context *compctx); + +void comp_add_to_extra_frame(struct frame *frame); +void comp_add_to_extra_buffer(struct frame *frame); + +void comp_print_stats (const struct compress_context *compctx, struct status_output *so); + +void comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out); + +void compv2_escape_data_ifneeded (struct buffer *buf); + +static inline bool +comp_enabled(const struct compress_options *info) +{ + return info->alg != COMP_ALG_UNDEF; +} + +static inline bool +comp_unswapped_prefix(const struct compress_options *info) +{ + return !(info->flags & COMP_F_SWAP); +} + +#endif /* USE_COMP */ +#endif diff --git a/src/openvpn/compstub.c b/src/openvpn/compstub.c new file mode 100644 index 0000000..9c6aad2 --- /dev/null +++ b/src/openvpn/compstub.c @@ -0,0 +1,169 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2012 OpenVPN Technologies, Inc. + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(USE_COMP) + +#include "comp.h" +#include "error.h" +#include "otime.h" + +#include "memdbg.h" + +static void +stub_compress_init (struct compress_context *compctx) +{ +} + +static void +stub_compress_uninit (struct compress_context *compctx) +{ +} + +static void +stub_compress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + if (buf->len <= 0) + return; + if (compctx->flags & COMP_F_SWAP) + { + uint8_t *head = BPTR (buf); + uint8_t *tail = BEND (buf); + ASSERT (buf_safe (buf, 1)); + ++buf->len; + + /* move head byte of payload to tail */ + *tail = *head; + *head = NO_COMPRESS_BYTE_SWAP; + } + else + { + uint8_t *header = buf_prepend (buf, 1); + *header = NO_COMPRESS_BYTE; + } +} + +static void +stub_decompress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + uint8_t c; + if (buf->len <= 0) + return; + if (compctx->flags & COMP_F_SWAP) + { + uint8_t *head = BPTR (buf); + c = *head; + --buf->len; + *head = *BEND (buf); + if (c != NO_COMPRESS_BYTE_SWAP) + { + dmsg (D_COMP_ERRORS, "Bad compression stub (swap) decompression header byte: %d", c); + buf->len = 0; + } + } + else + { + c = *BPTR (buf); + ASSERT (buf_advance (buf, 1)); + if (c != NO_COMPRESS_BYTE) + { + dmsg (D_COMP_ERRORS, "Bad compression stub decompression header byte: %d", c); + buf->len = 0; + } + } +} + + +static void +stubv2_compress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + if (buf->len <= 0) + return; + + compv2_escape_data_ifneeded (buf); +} + +static void +stubv2_decompress (struct buffer *buf, struct buffer work, + struct compress_context *compctx, + const struct frame* frame) +{ + if (buf->len <= 0) + return; + + uint8_t *head = BPTR (buf); + + /* no compression or packet to short*/ + if (head[0] != COMP_ALGV2_INDICATOR_BYTE) + return; + + /* compression header (0x50) is present */ + buf_advance(buf, 1); + + /* Packet buffer too short (only 1 byte) */ + if (buf->len <= 0) + return; + + head = BPTR (buf); + buf_advance(buf, 1); + + if (head[0] != COMP_ALGV2_UNCOMPRESSED_BYTE) { + dmsg (D_COMP_ERRORS, "Bad compression stubv2 decompression header byte: %d", *head); + buf->len = 0; + return; + } +} + +const struct compress_alg compv2_stub_alg = { + "stubv2", + stub_compress_init, + stub_compress_uninit, + stubv2_compress, + stubv2_decompress +}; + +const struct compress_alg comp_stub_alg = { + "stub", + stub_compress_init, + stub_compress_uninit, + stub_compress, + stub_decompress +}; + +#else +static void dummy(void) {} +#endif /* USE_STUB */ diff --git a/src/openvpn/console.c b/src/openvpn/console.c index 86331a1..c3bb7c3 100644 --- a/src/openvpn/console.c +++ b/src/openvpn/console.c @@ -6,6 +6,8 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. + * Copyright (C) 2014-2015 David Sommerseth + * Copyright (C) 2016 David Sommerseth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -38,219 +40,43 @@ #include #endif -#ifdef WIN32 -#include "win32.h" +struct _query_user query_user[QUERY_USER_NUMSLOTS]; /* GLOBAL */ -/* - * Get input from console. - * - * Return false on input error, or if service - * exit event is signaled. - */ - -static bool -get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) -{ - HANDLE in = INVALID_HANDLE_VALUE; - HANDLE err = INVALID_HANDLE_VALUE; - DWORD len = 0; - - ASSERT (prompt); - ASSERT (input); - ASSERT (capacity > 0); - - input[0] = '\0'; - - in = GetStdHandle (STD_INPUT_HANDLE); - err = get_orig_stderr (); - - if (in != INVALID_HANDLE_VALUE - && err != INVALID_HANDLE_VALUE - && !win32_service_interrupt (&win32_signal) - && WriteFile (err, prompt, strlen (prompt), &len, NULL)) - { - bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); - DWORD flags_save = 0; - int status = 0; - WCHAR *winput; - - if (is_console) - { - if (GetConsoleMode (in, &flags_save)) - { - DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - if (echo) - flags |= ENABLE_ECHO_INPUT; - SetConsoleMode (in, flags); - } - else - is_console = 0; - } - - if (is_console) - { - winput = malloc (capacity * sizeof (WCHAR)); - if (winput == NULL) - return false; - - status = ReadConsoleW (in, winput, capacity, &len, NULL); - WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); - free (winput); - } - else - status = ReadFile (in, input, capacity, &len, NULL); - - string_null_terminate (input, (int)len, capacity); - chomp (input); - - if (!echo) - WriteFile (err, "\r\n", 2, &len, NULL); - if (is_console) - SetConsoleMode (in, flags_save); - if (status && !win32_service_interrupt (&win32_signal)) - return true; - } - - return false; -} - -#endif - -#ifdef HAVE_GETPASS - -static FILE * -open_tty (const bool write) -{ - FILE *ret; - ret = fopen ("/dev/tty", write ? "w" : "r"); - if (!ret) - ret = write ? stderr : stdin; - return ret; -} - -static void -close_tty (FILE *fp) -{ - if (fp != stderr && fp != stdin) - fclose (fp); -} - -#endif -#ifdef ENABLE_SYSTEMD - -/* - * is systemd running - */ - -static bool -check_systemd_running () +void query_user_clear() { - struct stat c; - - /* We simply test whether the systemd cgroup hierarchy is - * mounted, as well as the systemd-ask-password executable - * being available */ + int i; - return (sd_booted() > 0) - && (stat(SYSTEMD_ASK_PASSWORD_PATH, &c) == 0); - -} - -static bool -get_console_input_systemd (const char *prompt, const bool echo, char *input, const int capacity) -{ - int std_out; - bool ret = false; - struct argv argv; - - argv_init (&argv); - argv_printf (&argv, SYSTEMD_ASK_PASSWORD_PATH); - argv_printf_cat (&argv, "%s", prompt); - - if ((std_out = openvpn_popen (&argv, NULL)) < 0) { - return false; - } - - memset (input, 0, capacity); - if (read (std_out, input, capacity-1) > 0) - { - chomp (input); - ret = true; + for( i = 0; i < QUERY_USER_NUMSLOTS; i++ ) { + CLEAR(query_user[i]); } - close (std_out); - - argv_reset (&argv); - - return ret; } -#endif - -/* - * Get input from console - */ -bool -get_console_input (const char *prompt, const bool echo, char *input, const int capacity) +void query_user_add(char *prompt, size_t prompt_len, + char *resp, size_t resp_len, + bool echo) { - bool ret = false; - ASSERT (prompt); - ASSERT (input); - ASSERT (capacity > 0); - input[0] = '\0'; - -#ifdef ENABLE_SYSTEMD - if (check_systemd_running ()) - return get_console_input_systemd (prompt, echo, input, capacity); -#endif + int i; -#if defined(WIN32) - return get_console_input_win32 (prompt, echo, input, capacity); -#elif defined(HAVE_GETPASS) + /* Ensure input is sane. All these must be present otherwise it is + * a programming error. + */ + ASSERT( prompt_len > 0 && prompt != NULL && resp_len > 0 && resp != NULL ); - /* did we --daemon'ize before asking for passwords? - * (in which case neither stdin or stderr are connected to a tty and - * /dev/tty can not be open()ed anymore) - */ - if ( !isatty(0) && !isatty(2) ) - { - int fd = open( "/dev/tty", O_RDWR ); - if ( fd < 0 ) - { msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a controlling tty nor systemd - can't ask for '%s'. If you used --daemon, you need to use --askpass to make passphrase-protected keys work, and you can not use --auth-nocache.", prompt ); } - close(fd); - } - - if (echo) - { - FILE *fp; - - fp = open_tty (true); - fprintf (fp, "%s", prompt); - fflush (fp); - close_tty (fp); - - fp = open_tty (false); - if (fgets (input, capacity, fp) != NULL) - { - chomp (input); - ret = true; + /* Seek to the last unused slot */ + for (i = 0; i < QUERY_USER_NUMSLOTS; i++) { + if( query_user[i].prompt == NULL ) { + break; } - close_tty (fp); } - else - { - char *gp = getpass (prompt); - if (gp) - { - strncpynt (input, gp, capacity); - memset (gp, 0, strlen (gp)); - ret = true; - } - } -#else - msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); -#endif - return ret; + ASSERT( i < QUERY_USER_NUMSLOTS ); /* Unlikely, but we want to panic if it happens */ + + /* Save the information needed for the user interaction */ + query_user[i].prompt = prompt; + query_user[i].prompt_len = prompt_len; + query_user[i].response = resp; + query_user[i].response_len = resp_len; + query_user[i].echo = echo; } diff --git a/src/openvpn/console.h b/src/openvpn/console.h index 268f3fe..ec32cf6 100644 --- a/src/openvpn/console.h +++ b/src/openvpn/console.h @@ -6,6 +6,8 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. + * Copyright (C) 2014-2015 David Sommerseth + * Copyright (C) 2016 David Sommerseth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -27,7 +29,90 @@ #include "basic.h" -bool -get_console_input (const char *prompt, const bool echo, char *input, const int capacity); +/** + * Configuration setup for declaring what kind of information to ask a user for + */ +struct _query_user { + char *prompt; /**< Prompt to present to the user */ + size_t prompt_len; /**< Lenght of the prompt string */ + char *response; /**< The user's response */ + size_t response_len; /**< Lenght the of the user reposone */ + bool echo; /**< True: The user should see what is being typed, otherwise mask it */ +}; + +#define QUERY_USER_NUMSLOTS 10 +extern struct _query_user query_user[]; /**< Global variable, declared in console.c */ + +/** + * Wipes all data put into all of the query_user structs + * + */ +void query_user_clear (); + + +/** + * Adds an item to ask the user for + * + * @param prompt Prompt to display to the user + * @param prompt_len Length of the prompt string + * @param resp String containing the user response + * @param resp_len Lenght of the response string + * @param echo Should the user input be echoed to the user? If False, input will be masked + * + */ +void query_user_add (char *prompt, size_t prompt_len, + char *resp, size_t resp_len, + bool echo); + + +/** + * Executes a configured setup, using the built-in method for querying the user. + * This method uses the console/TTY directly. + * + * @param setup Pointer to the setup defining what to ask the user + * + * @return True if executing all the defined steps completed successfully + */ +bool query_user_exec_builtin (); + + +#if defined(ENABLE_SYSTEMD) +/** + * Executes a configured setup, using the compiled method for querying the user + * + * @param setup Pointer to the setup defining what to ask the user + * + * @return True if executing all the defined steps completed successfully + */ +bool query_user_exec (); + +#else /* ENABLE_SYSTEMD not defined*/ +/** + * Wrapper function enabling query_user_exec() if no alternative methods have + * been enabled + * + */ +static bool query_user_exec () +{ + return query_user_exec_builtin(); +} +#endif /* defined(ENABLE_SYSTEMD) */ + + +/** + * A plain "make Gert happy" wrapper. Same arguments as @query_user_add + * + * FIXME/TODO: Remove this when refactoring the complete user query process + * to be called at start-up initialization of OpenVPN. + * + */ +static inline bool query_user_SINGLE (char *prompt, size_t prompt_len, + char *resp, size_t resp_len, + bool echo) +{ + query_user_clear(); + query_user_add(prompt, prompt_len, resp, resp_len, echo); + return query_user_exec(); +} #endif diff --git a/src/openvpn/console_builtin.c b/src/openvpn/console_builtin.c new file mode 100644 index 0000000..6b0211d --- /dev/null +++ b/src/openvpn/console_builtin.c @@ -0,0 +1,261 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2016 OpenVPN Technologies, Inc. + * Copyright (C) 2014-2015 David Sommerseth + * Copyright (C) 2016 David Sommerseth + * + * 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 + */ + +/* + * These functions covers handing user input/output using the default consoles + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "console.h" +#include "error.h" +#include "buffer.h" +#include "misc.h" + +#ifdef _WIN32 + +#include "win32.h" + +/** + * Get input from a Windows console. + * + * @param prompt Prompt to display to the user + * @param echo Should the user input be displayed in the console + * @param input Pointer to the buffer the user input will be saved + * @param capacity Size of the buffer for the user input + * + * @return Return false on input error, or if service + * exit event is signaled. + */ +static bool get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) +{ + HANDLE in = INVALID_HANDLE_VALUE; + HANDLE err = INVALID_HANDLE_VALUE; + DWORD len = 0; + + ASSERT (prompt); + ASSERT (input); + ASSERT (capacity > 0); + + input[0] = '\0'; + + in = GetStdHandle (STD_INPUT_HANDLE); + err = get_orig_stderr (); + + if (in != INVALID_HANDLE_VALUE + && err != INVALID_HANDLE_VALUE + && !win32_service_interrupt (&win32_signal) + && WriteFile (err, prompt, strlen (prompt), &len, NULL)) + { + bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); + DWORD flags_save = 0; + int status = 0; + WCHAR *winput; + + if (is_console) + { + if (GetConsoleMode (in, &flags_save)) + { + DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + if (echo) + flags |= ENABLE_ECHO_INPUT; + SetConsoleMode (in, flags); + } else + is_console = 0; + } + + if (is_console) + { + winput = malloc (capacity * sizeof (WCHAR)); + if (winput == NULL) + return false; + + status = ReadConsoleW (in, winput, capacity, &len, NULL); + WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); + free (winput); + } else + status = ReadFile (in, input, capacity, &len, NULL); + + string_null_terminate (input, (int)len, capacity); + chomp (input); + + if (!echo) + WriteFile (err, "\r\n", 2, &len, NULL); + if (is_console) + SetConsoleMode (in, flags_save); + if (status && !win32_service_interrupt (&win32_signal)) + return true; + } + + return false; +} + +#endif /* _WIN32 */ + + +#ifdef HAVE_GETPASS + +/** + * Open the current console TTY for read/write operations + * + * @params write If true, the user wants to write to the console + * otherwise read from the console + * + * @returns Returns a FILE pointer to either the TTY in read or write mode + * or stdin/stderr, depending on the write flag + * + */ +static FILE * open_tty (const bool write) +{ + FILE *ret; + ret = fopen ("/dev/tty", write ? "w" : "r"); + if (!ret) + ret = write ? stderr : stdin; + return ret; +} + +/** + * Closes the TTY FILE pointer, but only if it is not a stdin/stderr FILE object. + * + * @params fp FILE pointer to close + * + */ +static void close_tty (FILE *fp) +{ + if (fp != stderr && fp != stdin) + fclose (fp); +} + +#endif /* HAVE_GETPASS */ + + +/** + * Core function for getting input from console + * + * @params prompt The prompt to present to the user + * @params echo Should the user see what is being typed + * @params input Pointer to the buffer used to save the user input + * @params capacity Size of the input buffer + * + * @returns Returns True if user input was gathered + */ +static bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity) +{ + bool ret = false; + ASSERT (prompt); + ASSERT (input); + ASSERT (capacity > 0); + input[0] = '\0'; + +#if defined(_WIN32) + return get_console_input_win32 (prompt, echo, input, capacity); +#elif defined(HAVE_GETPASS) + + /* did we --daemon'ize before asking for passwords? + * (in which case neither stdin or stderr are connected to a tty and + * /dev/tty can not be open()ed anymore) + */ + if ( !isatty(0) && !isatty(2) ) + { + int fd = open( "/dev/tty", O_RDWR ); + if ( fd < 0 ) + { + msg(M_FATAL, "neither stdin nor stderr are a tty device and you have neither a " + "controlling tty nor systemd - can't ask for '%s'. If you used --daemon, " + "you need to use --askpass to make passphrase-protected keys work, and you " + "can not use --auth-nocache.", prompt ); + } + close(fd); + } + + if (echo) + { + FILE *fp; + + fp = open_tty (true); + fprintf (fp, "%s", prompt); + fflush (fp); + close_tty (fp); + + fp = open_tty (false); + if (fgets (input, capacity, fp) != NULL) + { + chomp (input); + ret = true; + } + close_tty (fp); + } else { + char *gp = getpass (prompt); + if (gp) + { + strncpynt (input, gp, capacity); + memset (gp, 0, strlen (gp)); + ret = true; + } + } +#else + msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); +#endif + return ret; +} + + +/** + * @copydoc query_user_exec() + * + * Default method for querying user using default stdin/stdout on a console. + * This needs to be available as a backup interface for the alternative + * implementations in case they cannot query through their implementation + * specific methods. + * + * If no alternative implementation is declared, a wrapper in console.h will ensure + * query_user_exec() will call this function instead. + * + */ +bool query_user_exec_builtin() +{ + bool ret = true; /* Presume everything goes okay */ + int i; + + /* Loop through configured query_user slots */ + for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++) + { + if (!get_console_input(query_user[i].prompt, query_user[i].echo, + query_user[i].response, query_user[i].response_len) ) + { + /* Force the final result state to failed on failure */ + ret = false; + } + } + + return ret; +} diff --git a/src/openvpn/console_systemd.c b/src/openvpn/console_systemd.c new file mode 100644 index 0000000..8a953c9 --- /dev/null +++ b/src/openvpn/console_systemd.c @@ -0,0 +1,122 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2014-2015 David Sommerseth + * Copyright (C) 2016 David Sommerseth + * + * 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 + */ + +/** + * @file Alternative method to query for user input, using systemd + * + */ + +#include "config.h" + +#ifdef ENABLE_SYSTEMD +#include "syshead.h" +#include "console.h" +#include "misc.h" + +#include + +/* + * is systemd running + */ + +static bool +check_systemd_running () +{ + struct stat c; + + /* We simply test whether the systemd cgroup hierarchy is + * mounted, as well as the systemd-ask-password executable + * being available */ + + return (sd_booted() > 0) + && (stat(SYSTEMD_ASK_PASSWORD_PATH, &c) == 0); + +} + +static bool +get_console_input_systemd (const char *prompt, const bool echo, char *input, const int capacity) +{ + int std_out; + bool ret = false; + struct argv argv = argv_new (); + + argv_printf (&argv, SYSTEMD_ASK_PASSWORD_PATH); +#ifdef SYSTEMD_NEWER_THAN_216 + /* the --echo support arrived in upstream systemd 217 */ + if( echo ) + { + argv_printf_cat(&argv, "--echo"); + } +#endif + argv_printf_cat (&argv, "--icon network-vpn"); + argv_printf_cat (&argv, "%s", prompt); + + if ((std_out = openvpn_popen (&argv, NULL)) < 0) { + return false; + } + memset (input, 0, capacity); + if (read (std_out, input, capacity-1) != 0) + { + chomp (input); + ret = true; + } + close (std_out); + + argv_reset (&argv); + + return ret; +} + +/** + * Systemd aware implementation of query_user_exec(). If systemd is not running + * it will fall back to use query_user_exec_builtin() instead. + * + */ +bool query_user_exec() +{ + bool ret = true; /* Presume everything goes okay */ + int i; + + /* If systemd is not available, use the default built-in mechanism */ + if (!check_systemd_running()) + { + return query_user_exec_builtin(); + } + + /* Loop through the complete query setup and when needed, collect the information */ + for (i = 0; i < QUERY_USER_NUMSLOTS && query_user[i].response != NULL; i++) + { + if (!get_console_input_systemd(query_user[i].prompt, query_user[i].echo, + query_user[i].response, query_user[i].response_len) ) + { + /* Force the final result state to failed on failure */ + ret = false; + } + } + + return ret; +} + +#endif /* ENABLE_SYSTEMD */ diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 400bf11..05622ce 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -6,7 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. - * Copyright (C) 2010 Fox Crypto B.V. + * Copyright (C) 2010-2016 Fox Crypto B.V. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -35,7 +35,8 @@ #include "crypto.h" #include "error.h" -#include "misc.h" +#include "integer.h" +#include "platform.h" #include "memdbg.h" @@ -62,38 +63,114 @@ * happen unless the frame parameters are wrong. */ -#define CRYPT_ERROR(format) \ - do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false) +static void +openvpn_encrypt_aead (struct buffer *buf, struct buffer work, + struct crypto_options *opt) { +#ifdef HAVE_AEAD_CIPHER_MODES + struct gc_arena gc; + int outlen = 0; + const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; + uint8_t *mac_out = NULL; + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); + const int mac_len = cipher_kt_tag_size (cipher_kt); + + /* IV, packet-ID and implicit IV required for this mode. */ + ASSERT (ctx->cipher); + ASSERT (cipher_kt_mode_aead (cipher_kt)); + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); -/** - * As memcmp(), but constant-time. - * Returns 0 when data is equal, non-zero otherwise. - */ -static int -memcmp_constant_time (const void *a, const void *b, size_t size) { - const uint8_t * a1 = a; - const uint8_t * b1 = b; - int ret = 0; - size_t i; - - for (i = 0; i < size; i++) { - ret |= *a1++ ^ *b1++; + gc_init (&gc); + + /* Prepare IV */ + { + struct buffer iv_buffer; + struct packet_id_net pin; + uint8_t iv[OPENVPN_MAX_IV_LENGTH]; + const int iv_len = cipher_ctx_iv_length (ctx->cipher); + + ASSERT (iv_len >= OPENVPN_AEAD_MIN_IV_LEN && iv_len <= OPENVPN_MAX_IV_LENGTH); + + memset(iv, 0, sizeof(iv)); + buf_set_write (&iv_buffer, iv, iv_len); + + /* IV starts with packet id to make the IV unique for packet */ + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, false); + ASSERT (packet_id_write (&pin, &iv_buffer, false, false)); + + /* Remainder of IV consists of implicit part (unique per session) */ + ASSERT (buf_write (&iv_buffer, ctx->implicit_iv, ctx->implicit_iv_len)); + ASSERT (iv_buffer.len == iv_len); + + /* Write explicit part of IV to work buffer */ + ASSERT (buf_write(&work, iv, iv_len - ctx->implicit_iv_len)); + dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv, iv_len, 0, &gc)); + + /* Init cipher_ctx with IV. key & keylen are already initialized */ + ASSERT (cipher_ctx_reset(ctx->cipher, iv)); } - return ret; + /* Reserve space for authentication tag */ + mac_out = buf_write_alloc (&work, mac_len); + ASSERT (mac_out); + + dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + /* Buffer overflow check */ + if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) + { + msg (D_CRYPT_ERRORS, + "ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d", + buf->capacity, buf->offset, buf->len, work.capacity, work.offset, + work.len); + goto err; + } + + /* For AEAD ciphers, authenticate Additional Data, including opcode */ + ASSERT (cipher_ctx_update_ad (ctx->cipher, BPTR (&work), BLEN (&work) - mac_len)); + dmsg (D_PACKET_CONTENT, "ENCRYPT AD: %s", + format_hex (BPTR (&work), BLEN (&work) - mac_len, 0, &gc)); + + /* Encrypt packet ID, payload */ + ASSERT (cipher_ctx_update (ctx->cipher, BEND (&work), &outlen, BPTR (buf), BLEN (buf))); + ASSERT (buf_inc_len (&work, outlen)); + + /* Flush the encryption buffer */ + ASSERT (cipher_ctx_final (ctx->cipher, BEND (&work), &outlen)); + ASSERT (buf_inc_len (&work, outlen)); + + /* Write authentication tag */ + ASSERT (cipher_ctx_get_tag (ctx->cipher, mac_out, mac_len)); + + *buf = work; + + dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + gc_free (&gc); + return; + +err: + crypto_clear_error(); + buf->len = 0; + gc_free (&gc); + return; +#else /* HAVE_AEAD_CIPHER_MODES */ + ASSERT (0); +#endif } -void -openvpn_encrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame) +static void +openvpn_encrypt_v1 (struct buffer *buf, struct buffer work, + struct crypto_options *opt) { struct gc_arena gc; gc_init (&gc); - if (buf->len > 0 && opt->key_ctx_bi) + if (buf->len > 0 && opt) { - struct key_ctx *ctx = &opt->key_ctx_bi->encrypt; + const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; + uint8_t *mac_out = NULL; + const uint8_t *hmac_start = NULL; /* Do Encrypt from buf -> work */ if (ctx->cipher) @@ -103,6 +180,14 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); int outlen; + /* Reserve space for HMAC */ + if (ctx->hmac) + { + mac_out = buf_write_alloc (&work, hmac_ctx_size(ctx->hmac)); + ASSERT (mac_out); + hmac_start = BEND(&work); + } + if (cipher_kt_mode_cbc(cipher_kt)) { CLEAR (iv_buf); @@ -111,11 +196,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, if (opt->flags & CO_USE_IV) prng_bytes (iv_buf, iv_size); - /* Put packet ID in plaintext buffer or IV, depending on cipher mode */ - if (opt->packet_id) + /* Put packet ID in plaintext buffer */ + if (packet_id_initialized(&opt->packet_id)) { struct packet_id_net pin; - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true)); } } @@ -124,10 +209,11 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, struct packet_id_net pin; struct buffer b; - ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */ - ASSERT (opt->packet_id); /* for this mode. */ + /* IV and packet-ID required for this mode. */ + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); memset (iv_buf, 0, iv_size); buf_set_write (&b, iv_buf, iv_size); ASSERT (packet_id_write (&pin, &b, true, false)); @@ -137,12 +223,12 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, ASSERT (0); } - /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ - ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); - /* set the IV pseudo-randomly */ if (opt->flags & CO_USE_IV) - dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc)); + { + ASSERT (buf_write(&work, iv_buf, iv_size)); + dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc)); + } dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex (BPTR (buf), BLEN (buf), 80, &gc)); @@ -165,52 +251,50 @@ openvpn_encrypt (struct buffer *buf, struct buffer work, } /* Encrypt packet ID, payload */ - ASSERT (cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf))); + ASSERT (cipher_ctx_update (ctx->cipher, BEND (&work), &outlen, BPTR (buf), BLEN (buf))); ASSERT (buf_inc_len(&work, outlen)); /* Flush the encryption buffer */ - ASSERT (cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen)); + ASSERT (cipher_ctx_final(ctx->cipher, BEND (&work), &outlen)); ASSERT (buf_inc_len(&work, outlen)); /* For all CBC mode ciphers, check the last block is complete */ ASSERT (cipher_kt_mode (cipher_kt) != OPENVPN_MODE_CBC || outlen == iv_size); - - /* prepend the IV to the ciphertext */ - if (opt->flags & CO_USE_IV) - { - uint8_t *output = buf_prepend (&work, iv_size); - ASSERT (output); - memcpy (output, iv_buf, iv_size); - } - - dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", - format_hex (BPTR (&work), BLEN (&work), 80, &gc)); } else /* No Encryption */ { - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { struct packet_id_net pin; - packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)); ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true)); } + if (ctx->hmac) + { + hmac_start = BPTR(buf); + ASSERT (mac_out = buf_prepend (buf, hmac_ctx_size(ctx->hmac))); + } + if (BLEN(&work)) { + buf_write_prepend(buf, BPTR(&work), BLEN(&work)); + } work = *buf; } /* HMAC the ciphertext (or plaintext if !cipher) */ if (ctx->hmac) { - uint8_t *output = NULL; - hmac_ctx_reset (ctx->hmac); - hmac_ctx_update (ctx->hmac, BPTR(&work), BLEN(&work)); - output = buf_prepend (&work, hmac_ctx_size(ctx->hmac)); - ASSERT (output); - hmac_ctx_final (ctx->hmac, output); + hmac_ctx_update (ctx->hmac, hmac_start, BEND(&work) - hmac_start); + hmac_ctx_final (ctx->hmac, mac_out); + dmsg (D_PACKET_CONTENT, "ENCRYPT HMAC: %s", + format_hex (mac_out, hmac_ctx_size(ctx->hmac), 80, &gc)); } *buf = work; + + dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s", + format_hex (BPTR (&work), BLEN (&work), 80, &gc)); } gc_free (&gc); @@ -223,6 +307,47 @@ err: return; } +void +openvpn_encrypt (struct buffer *buf, struct buffer work, + struct crypto_options *opt) +{ + if (buf->len > 0 && opt) + { + const cipher_kt_t *cipher_kt = + cipher_ctx_get_cipher_kt(opt->key_ctx_bi.encrypt.cipher); + + if (cipher_kt_mode_aead (cipher_kt)) + openvpn_encrypt_aead(buf, work, opt); + else + openvpn_encrypt_v1(buf, work, opt); + } +} + +bool crypto_check_replay(struct crypto_options *opt, + const struct packet_id_net *pin, const char *error_prefix, + struct gc_arena *gc) { + bool ret = false; + packet_id_reap_test (&opt->packet_id.rec); + if (packet_id_test (&opt->packet_id.rec, pin)) + { + packet_id_add (&opt->packet_id.rec, pin); + if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM)) + packet_id_persist_save_obj (opt->pid_persist, &opt->packet_id); + ret = true; + } + else + { + if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS)) + { + msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- " + "see the man page entry for --no-replay and --replay-window for " + "more info or silence this warning with --mute-replay-warnings", + error_prefix, packet_id_net_print (pin, true, gc)); + } + } + return ret; +} + /* * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. * @@ -231,21 +356,169 @@ err: * On success, buf is set to point to plaintext, true * is returned. */ -bool -openvpn_decrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame) +static bool +openvpn_decrypt_aead (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame, + const uint8_t *ad_start) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + static const char error_prefix[] = "AEAD Decrypt error"; + struct packet_id_net pin = { 0 }; + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); + uint8_t *tag_ptr = NULL; + int tag_size = 0; + int outlen; + struct gc_arena gc; + + gc_init (&gc); + + ASSERT (opt); + ASSERT (frame); + ASSERT (buf->len > 0); + ASSERT (ctx->cipher); + ASSERT (cipher_kt_mode_aead (cipher_kt)); + + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", + format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + + ASSERT (ad_start >= buf->data && ad_start <= BPTR (buf)); + + ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT))); + + /* IV and Packet ID required for this mode */ + ASSERT (packet_id_initialized (&opt->packet_id)); + ASSERT (opt->flags & CO_USE_IV); + + /* Combine IV from explicit part from packet and implicit part from context */ + { + uint8_t iv[OPENVPN_MAX_IV_LENGTH] = { 0 }; + const int iv_len = cipher_ctx_iv_length (ctx->cipher); + const size_t packet_iv_len = iv_len - ctx->implicit_iv_len; + + ASSERT (ctx->implicit_iv_len <= iv_len); + if (buf->len + ctx->implicit_iv_len < iv_len) + CRYPT_ERROR ("missing IV info"); + + memcpy (iv, BPTR(buf), packet_iv_len); + memcpy (iv + packet_iv_len, ctx->implicit_iv, ctx->implicit_iv_len); + + dmsg (D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex (iv, iv_len, 0, &gc)); + + /* Load IV, ctx->cipher was already initialized with key & keylen */ + if (!cipher_ctx_reset (ctx->cipher, iv)) + { + CRYPT_ERROR ("cipher init failed"); + } + } + + /* Read packet ID from packet */ + if (!packet_id_read (&pin, buf, false)) + { + CRYPT_ERROR ("error reading packet-id"); + } + + /* keep the tag value to feed in later */ + tag_size = cipher_kt_tag_size(cipher_kt); + if (buf->len < tag_size) + { + CRYPT_ERROR ("missing tag"); + } + tag_ptr = BPTR(buf); + ASSERT (buf_advance (buf, tag_size)); + dmsg (D_PACKET_CONTENT, "DECRYPT MAC: %s", format_hex (tag_ptr, tag_size, 0, &gc)); +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10001040L + /* OpenSSL <= 1.0.1c bug requires set tag before processing ciphertext */ + if (!EVP_CIPHER_CTX_ctrl (ctx->cipher, EVP_CTRL_GCM_SET_TAG, tag_size, tag_ptr)) + { + CRYPT_ERROR ("setting tag failed"); + } +#endif + + if (buf->len < 1) + { + CRYPT_ERROR ("missing payload"); + } + + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", format_hex (BPTR(buf), BLEN(buf), 0, &gc)); + + /* Buffer overflow check (should never fail) */ + if (!buf_safe (&work, buf->len + cipher_ctx_block_size(ctx->cipher))) + { + CRYPT_ERROR ("potential buffer overflow"); + } + + { + /* feed in tag and the authenticated data */ + const int ad_size = BPTR (buf) - ad_start - tag_size; + ASSERT (cipher_ctx_update_ad (ctx->cipher, ad_start, ad_size)); + dmsg (D_PACKET_CONTENT, "DECRYPT AD: %s", + format_hex (BPTR (buf) - ad_size - tag_size, ad_size, 0, &gc)); + } + + /* Decrypt and authenticate packet */ + if (!cipher_ctx_update (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), + BLEN (buf))) + { + CRYPT_ERROR ("cipher update failed"); + } + ASSERT (buf_inc_len (&work, outlen)); + if (!cipher_ctx_final_check_tag (ctx->cipher, BPTR (&work) + outlen, + &outlen, tag_ptr, tag_size)) + { + CRYPT_ERROR ("cipher final failed"); + } + ASSERT (buf_inc_len (&work, outlen)); + + dmsg (D_PACKET_CONTENT, "DECRYPT TO: %s", + format_hex (BPTR (&work), BLEN (&work), 80, &gc)); + + if (!crypto_check_replay (opt, &pin, error_prefix, &gc)) + { + goto error_exit; + } + + *buf = work; + + gc_free (&gc); + return true; + + error_exit: + crypto_clear_error(); + buf->len = 0; + gc_free (&gc); + return false; +#else /* HAVE_AEAD_CIPHER_MODES */ + ASSERT (0); + return false; +#endif +} + +/* + * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. + * + * Set buf->len to 0 and return false on decrypt error. + * + * On success, buf is set to point to plaintext, true + * is returned. + */ +static bool +openvpn_decrypt_v1 (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame) { static const char error_prefix[] = "Authenticate/Decrypt packet error"; struct gc_arena gc; gc_init (&gc); - if (buf->len > 0 && opt->key_ctx_bi) + if (buf->len > 0 && opt) { - struct key_ctx *ctx = &opt->key_ctx_bi->decrypt; + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; struct packet_id_net pin; bool have_pin = false; + dmsg (D_PACKET_CONTENT, "DECRYPT FROM: %s", + format_hex (BPTR (buf), BLEN (buf), 80, &gc)); + /* Verify the HMAC */ if (ctx->hmac) { @@ -325,7 +598,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, { if (cipher_kt_mode_cbc(cipher_kt)) { - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM))) CRYPT_ERROR ("error reading CBC packet-id"); @@ -336,8 +609,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, { struct buffer b; - ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */ - ASSERT (opt->packet_id); /* for this mode. */ + /* IV and packet-ID required for this mode. */ + ASSERT (opt->flags & CO_USE_IV); + ASSERT (packet_id_initialized(&opt->packet_id)); buf_set_read (&b, iv_buf, iv_size); if (!packet_id_read (&pin, &b, true)) @@ -353,7 +627,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, else { work = *buf; - if (opt->packet_id) + if (packet_id_initialized(&opt->packet_id)) { if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM))) CRYPT_ERROR ("error reading packet-id"); @@ -361,22 +635,9 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, } } - if (have_pin) + if (have_pin && !crypto_check_replay(opt, &pin, error_prefix, &gc)) { - packet_id_reap_test (&opt->packet_id->rec); - if (packet_id_test (&opt->packet_id->rec, &pin)) - { - packet_id_add (&opt->packet_id->rec, &pin); - if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM)) - packet_id_persist_save_obj (opt->pid_persist, opt->packet_id); - } - else - { - if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS)) - msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings", - error_prefix, packet_id_net_print (&pin, true, &gc)); - goto error_exit; - } + goto error_exit; } *buf = work; } @@ -391,14 +652,36 @@ openvpn_decrypt (struct buffer *buf, struct buffer work, return false; } -/* - * How many bytes will we add to frame buffer for a given - * set of crypto options? - */ + +bool +openvpn_decrypt (struct buffer *buf, struct buffer work, + struct crypto_options *opt, const struct frame* frame, + const uint8_t *ad_start) +{ + bool ret = false; + + if (buf->len > 0 && opt) + { + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + if (cipher_kt_mode_aead (cipher_ctx_get_cipher_kt (ctx->cipher))) + { + ret = openvpn_decrypt_aead (buf, work, opt, frame, ad_start); + } + else + { + ret = openvpn_decrypt_v1 (buf, work, opt, frame); + } + } + else + { + ret = true; + } + return ret; +} + void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type* kt, - bool cipher_defined, bool use_iv, bool packet_id, bool packet_id_long_form) @@ -408,11 +691,14 @@ crypto_adjust_frame_parameters(struct frame *frame, if (packet_id) crypto_overhead += packet_id_size (packet_id_long_form); - if (cipher_defined) + if (kt->cipher) { if (use_iv) crypto_overhead += cipher_kt_iv_size (kt->cipher); + if (cipher_kt_mode_aead (kt->cipher)) + crypto_overhead += cipher_kt_tag_size (kt->cipher); + /* extra block required by cipher_ctx_update() */ crypto_overhead += cipher_kt_block_size (kt->cipher); } @@ -421,8 +707,16 @@ crypto_adjust_frame_parameters(struct frame *frame, frame_add_to_extra_frame (frame, crypto_overhead); - msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %zu bytes", - __func__, crypto_overhead); + msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for crypto by %u bytes", + __func__, (unsigned int) crypto_overhead); +} + +size_t +crypto_max_overhead(void) +{ + return packet_id_size(true) + OPENVPN_MAX_IV_LENGTH + + OPENVPN_MAX_CIPHER_BLOCK_SIZE + + max_int (OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH); } /* @@ -430,39 +724,55 @@ crypto_adjust_frame_parameters(struct frame *frame, */ void init_key_type (struct key_type *kt, const char *ciphername, - bool ciphername_defined, const char *authname, - bool authname_defined, int keysize, - bool cfb_ofb_allowed, bool warn) + const char *authname, int keysize, bool tls_mode, bool warn) { + bool aead_cipher = false; + + ASSERT(ciphername); + ASSERT(authname); + CLEAR (*kt); - if (ciphername && ciphername_defined) + if (strcmp (ciphername, "none") != 0) { kt->cipher = cipher_kt_get (translate_cipher_name_from_openvpn(ciphername)); + if (!kt->cipher) + { + msg (M_FATAL, "Cipher %s not supported", ciphername); + } + kt->cipher_length = cipher_kt_key_size (kt->cipher); if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH) kt->cipher_length = keysize; /* check legal cipher mode */ - { - if (!(cipher_kt_mode_cbc(kt->cipher) + aead_cipher = cipher_kt_mode_aead(kt->cipher); + if (!(cipher_kt_mode_cbc(kt->cipher) + || (tls_mode && aead_cipher) #ifdef ENABLE_OFB_CFB_MODE - || (cfb_ofb_allowed && cipher_kt_mode_ofb_cfb(kt->cipher)) + || (tls_mode && cipher_kt_mode_ofb_cfb(kt->cipher)) #endif - )) - msg (M_FATAL, "Cipher '%s' mode not supported", ciphername); - } + )) + msg (M_FATAL, "Cipher '%s' mode not supported", ciphername); + + if (OPENVPN_MAX_CIPHER_BLOCK_SIZE < cipher_kt_block_size(kt->cipher)) + msg (M_FATAL, "Cipher '%s' not allowed: block size too big.", ciphername); } else { if (warn) msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used"); } - if (authname && authname_defined) + if (strcmp (authname, "none") != 0) { - kt->digest = md_kt_get (authname); - kt->hmac_length = md_kt_size (kt->digest); + if (!aead_cipher) { /* Ignore auth for AEAD ciphers */ + kt->digest = md_kt_get (authname); + kt->hmac_length = md_kt_size (kt->digest); + + if (OPENVPN_MAX_HMAC_SIZE < kt->hmac_length) + msg (M_FATAL, "HMAC '%s' not allowed: digest size too big.", authname); + } } - else + else if (!aead_cipher) { if (warn) msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used"); @@ -486,15 +796,21 @@ init_key_ctx (struct key_ctx *ctx, struct key *key, msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", prefix, - cipher_kt_name(kt->cipher), + translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)), kt->cipher_length *8); dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, format_hex (key->cipher, kt->cipher_length, 0, &gc)); dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d", - prefix, - cipher_kt_block_size(kt->cipher), - cipher_kt_iv_size(kt->cipher)); + prefix, cipher_kt_block_size(kt->cipher), + cipher_kt_iv_size(kt->cipher)); + if (cipher_kt_block_size(kt->cipher) < 128/8) + { + msg (M_WARN, "WARNING: INSECURE cipher with block size less than 128" + " bit (%d bit). This allows attacks like SWEET32. Mitigate by " + "using a --cipher with a larger block size (e.g. AES-256-CBC).", + cipher_kt_block_size(kt->cipher)*8); + } } if (kt->digest && kt->hmac_length > 0) { @@ -532,6 +848,7 @@ free_key_ctx (struct key_ctx *ctx) free(ctx->hmac); ctx->hmac = NULL; } + ctx->implicit_iv_len = 0; } void @@ -541,7 +858,6 @@ free_key_ctx_bi (struct key_ctx_bi *ctx) free_key_ctx(&ctx->decrypt); } - static bool key_is_zero (struct key *key, const struct key_type *kt) { @@ -621,8 +937,10 @@ check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool use { ASSERT(kt); - if (cipher_kt_mode_ofb_cfb(kt->cipher) && !(packet_id && use_iv)) - msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB mode cipher"); + if (!(packet_id && use_iv) && (cipher_kt_mode_ofb_cfb(kt->cipher) || + cipher_kt_mode_aead(kt->cipher))) + msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB, OFB or " + "AEAD mode cipher"); } /* @@ -688,7 +1006,7 @@ key2_print (const struct key2* k, } void -test_crypto (const struct crypto_options *co, struct frame* frame) +test_crypto (struct crypto_options *co, struct frame* frame) { int i, j; struct gc_arena gc = gc_new (); @@ -697,10 +1015,35 @@ test_crypto (const struct crypto_options *co, struct frame* frame) struct buffer encrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer decrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer buf = clear_buf(); + void *buf_p; /* init work */ ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); +#ifdef HAVE_AEAD_CIPHER_MODES + /* init implicit IV */ + { + const cipher_kt_t *cipher = + cipher_ctx_get_cipher_kt(co->key_ctx_bi.encrypt.cipher); + + if (cipher_kt_mode_aead(cipher)) + { + size_t impl_iv_len = cipher_kt_iv_size(cipher) - sizeof(packet_id_type); + ASSERT (cipher_kt_iv_size(cipher) <= OPENVPN_MAX_IV_LENGTH); + ASSERT (cipher_kt_iv_size(cipher) >= OPENVPN_AEAD_MIN_IV_LEN); + + /* Generate dummy implicit IV */ + ASSERT (rand_bytes(co->key_ctx_bi.encrypt.implicit_iv, + OPENVPN_MAX_IV_LENGTH)); + co->key_ctx_bi.encrypt.implicit_iv_len = impl_iv_len; + + memcpy(co->key_ctx_bi.decrypt.implicit_iv, + co->key_ctx_bi.encrypt.implicit_iv, OPENVPN_MAX_IV_LENGTH); + co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len; + } + } +#endif + msg (M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode."); for (i = 1; i <= TUN_MTU_SIZE (frame); ++i) { @@ -718,13 +1061,18 @@ test_crypto (const struct crypto_options *co, struct frame* frame) /* copy source to input buf */ buf = work; - memcpy (buf_write_alloc (&buf, BLEN (&src)), BPTR (&src), BLEN (&src)); + buf_p = buf_write_alloc (&buf, BLEN (&src)); + ASSERT(buf_p); + memcpy (buf_p, BPTR (&src), BLEN (&src)); + + /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ + ASSERT (buf_init (&encrypt_workspace, FRAME_HEADROOM (frame))); /* encrypt */ - openvpn_encrypt (&buf, encrypt_workspace, co, frame); + openvpn_encrypt (&buf, encrypt_workspace, co); /* decrypt */ - openvpn_decrypt (&buf, decrypt_workspace, co, frame); + openvpn_decrypt (&buf, decrypt_workspace, co, frame, BPTR (&buf)); /* compare */ if (buf.len != src.len) @@ -741,90 +1089,47 @@ test_crypto (const struct crypto_options *co, struct frame* frame) gc_free (&gc); } -#ifdef ENABLE_SSL - void -get_tls_handshake_key (const struct key_type *key_type, - struct key_ctx_bi *ctx, - const char *passphrase_file, - const int key_direction, - const unsigned int flags) +crypto_read_openvpn_key (const struct key_type *key_type, + struct key_ctx_bi *ctx, const char *key_file, const char *key_inline, + const int key_direction, const char *key_name, const char *opt_name) { - if (passphrase_file && key_type->hmac_length) - { - struct key2 key2; - struct key_type kt = *key_type; - struct key_direction_state kds; - - /* for control channel we are only authenticating, not encrypting */ - kt.cipher_length = 0; - kt.cipher = NULL; - - if (flags & GHK_INLINE) - { - /* key was specified inline, key text is in passphrase_file */ - read_key_file (&key2, passphrase_file, RKF_INLINE|RKF_MUST_SUCCEED); - - /* succeeded? */ - if (key2.n == 2) - msg (M_INFO, "Control Channel Authentication: tls-auth using INLINE static key file"); - else - msg (M_FATAL, "INLINE tls-auth file lacks the requisite 2 keys"); - } - else - { - /* first try to parse as an OpenVPN static key file */ - read_key_file (&key2, passphrase_file, 0); - - /* succeeded? */ - if (key2.n == 2) - { - msg (M_INFO, - "Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file", - passphrase_file); - } - else - { - int hash_size; - - CLEAR (key2); - - /* failed, now try to get hash from a freeform file */ - hash_size = read_passphrase_hash (passphrase_file, - kt.digest, - key2.keys[0].hmac, - MAX_HMAC_KEY_LENGTH); - ASSERT (hash_size == kt.hmac_length); + struct key2 key2; + struct key_direction_state kds; + char log_prefix[128] = { 0 }; - /* suceeded */ - key2.n = 1; + if (key_inline) + { + read_key_file (&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE); + } + else + { + read_key_file (&key2, key_file, RKF_MUST_SUCCEED); + } - msg (M_INFO, - "Control Channel Authentication: using '%s' as a free-form passphrase file", - passphrase_file); - msg (M_WARN, "DEPRECATED OPTION: Using freeform files for tls-auth is deprecated and is not supported in OpenVPN 2.4 or newer versions"); - } - } - /* handle key direction */ + if (key2.n != 2) + { + msg (M_ERR, "File '%s' does not have OpenVPN Static Key format. Using " + "free-form passphrase file is not supported anymore.", key_file); + } - key_direction_state_init (&kds, key_direction); - must_have_n_keys (passphrase_file, "tls-auth", &key2, kds.need_keys); + /* check for and fix highly unlikely key problems */ + verify_fix_key2 (&key2, key_type, key_file); - /* initialize hmac key in both directions */ + /* handle key direction */ + key_direction_state_init (&kds, key_direction); + must_have_n_keys (key_file, opt_name, &key2, kds.need_keys); - init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, OPENVPN_OP_ENCRYPT, - "Outgoing Control Channel Authentication"); - init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, OPENVPN_OP_DECRYPT, - "Incoming Control Channel Authentication"); + /* initialize key in both directions */ + openvpn_snprintf (log_prefix, sizeof (log_prefix), "Outgoing %s", key_name); + init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], key_type, + OPENVPN_OP_ENCRYPT, log_prefix); + openvpn_snprintf (log_prefix, sizeof (log_prefix), "Incoming %s", key_name); + init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], key_type, + OPENVPN_OP_DECRYPT, log_prefix); - CLEAR (key2); - } - else - { - CLEAR (*ctx); - } + CLEAR (key2); } -#endif /* header and footer for static key file */ static const char static_key_head[] = "-----BEGIN OpenVPN Static key V1-----"; @@ -1002,9 +1307,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags) if (!(flags & RKF_INLINE)) buf_clear (&in); - if (key2->n) - warn_if_group_others_accessible (error_filename); - #if 0 /* DEBUGGING */ { @@ -1028,55 +1330,6 @@ read_key_file (struct key2 *key2, const char *file, const unsigned int flags) gc_free (&gc); } -int -read_passphrase_hash (const char *passphrase_file, - const md_kt_t *digest, - uint8_t *output, - int len) -{ - unsigned int outlen = 0; - md_ctx_t md; - - ASSERT (len >= md_kt_size(digest)); - memset (output, 0, len); - - md_ctx_init(&md, digest); - - /* read passphrase file */ - { - const int min_passphrase_size = 8; - uint8_t buf[64]; - int total_size = 0; - int fd = platform_open (passphrase_file, O_RDONLY, 0); - - if (fd == -1) - msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file); - - for (;;) - { - int size = read (fd, buf, sizeof (buf)); - if (size == 0) - break; - if (size == -1) - msg (M_ERR, "Read error on passphrase file: '%s'", - passphrase_file); - md_ctx_update(&md, buf, size); - total_size += size; - } - close (fd); - - warn_if_group_others_accessible (passphrase_file); - - if (total_size < min_passphrase_size) - msg (M_FATAL, - "Passphrase file '%s' is too small (must have at least %d characters)", - passphrase_file, min_passphrase_size); - } - md_ctx_final(&md, output); - md_ctx_cleanup(&md); - return md_kt_size(digest); -} - /* * Write key to file, return number of random bits * written. @@ -1367,7 +1620,6 @@ prng_bytes (uint8_t *output, int len) const int md_size = md_kt_size (nonce_md); while (len > 0) { - unsigned int outlen = 0; const int blen = min_int (len, md_size); md_full(nonce_md, nonce_data, md_size + nonce_secret_len, nonce_data); memcpy (output, nonce_data, blen); @@ -1397,79 +1649,42 @@ get_random() return l; } -#ifndef ENABLE_SSL +static const cipher_name_pair * +get_cipher_name_pair(const char *cipher_name) { + const cipher_name_pair *pair; + size_t i = 0; -void -init_ssl_lib (void) -{ - crypto_init_lib (); -} + /* Search for a cipher name translation */ + for (; i < cipher_name_translation_table_count; i++) + { + pair = &cipher_name_translation_table[i]; + if (0 == strcmp (cipher_name, pair->openvpn_name) || + 0 == strcmp (cipher_name, pair->lib_name)) + return pair; + } -void -free_ssl_lib (void) -{ - crypto_uninit_lib (); - prng_uninit(); + /* Nothing found, return null */ + return NULL; } -#endif /* ENABLE_SSL */ - -/* - * md5 functions - */ - const char * -md5sum (uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc) -{ - uint8_t digest[MD5_DIGEST_LENGTH]; - const md_kt_t *md5_kt = md_kt_get("MD5"); - - md_full(md5_kt, buf, len, digest); - - return format_hex (digest, MD5_DIGEST_LENGTH, n_print_chars, gc); -} - -void -md5_state_init (struct md5_state *s) -{ - const md_kt_t *md5_kt = md_kt_get("MD5"); - - md_ctx_init(&s->ctx, md5_kt); -} +translate_cipher_name_from_openvpn (const char *cipher_name) { + const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); -void -md5_state_update (struct md5_state *s, void *data, size_t len) -{ - md_ctx_update(&s->ctx, data, len); -} + if (NULL == pair) + return cipher_name; -void -md5_state_final (struct md5_state *s, struct md5_digest *out) -{ - md_ctx_final(&s->ctx, out->digest); - md_ctx_cleanup(&s->ctx); + return pair->lib_name; } -void -md5_digest_clear (struct md5_digest *digest) -{ - CLEAR (*digest); -} +const char * +translate_cipher_name_to_openvpn (const char *cipher_name) { + const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); -bool -md5_digest_defined (const struct md5_digest *digest) -{ - int i; - for (i = 0; i < MD5_DIGEST_LENGTH; ++i) - if (digest->digest[i]) - return true; - return false; -} + if (NULL == pair) + return cipher_name; -bool -md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2) -{ - return memcmp(d1->digest, d2->digest, MD5_DIGEST_LENGTH) == 0; + return pair->openvpn_name; } #endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h index e489827..ff90745 100644 --- a/src/openvpn/crypto.h +++ b/src/openvpn/crypto.h @@ -86,6 +86,30 @@ * [ HMAC ] [ - IV - ] [ * packet payload * ] * * @par + * GCM data channel crypto format \n + * GCM modes are only supported in TLS mode. In these modes, the IV consists of + * the 32-bit packet counter followed by data from the HMAC key. The HMAC key + * can be used as IV, since in GCM and CCM modes the HMAC key is not used for + * the HMAC. The packet counter may not roll over within a single TLS sessions. + * This results in a unique IV for each packet, as required by GCM. + * + * @par + * The HMAC key data is pre-shared during the connection setup, and thus can be + * omitted in on-the-wire packets, saving 8 bytes per packet (for GCM and CCM). + * + * @par + * In GCM mode, P_DATA_V2 headers (the opcode and peer-id) are also + * authenticated as Additional Data. + * + * @par + * GCM IV format: \n + * [ - packet ID - ] [ - HMAC key data - ] \n + * P_DATA_V1 GCM data channel crypto format: \n + * [ opcode ] [ - packet ID - ] [ TAG ] [ * packet payload * ] + * P_DATA_V2 GCM data channel crypto format: \n + * [ - opcode/peer-id - ] [ - packet ID - ] [ TAG ] [ * packet payload * ] + * + * @par * No-crypto data channel format \n * In no-crypto mode (\c \-\-cipher \c none is specified), both TLS-mode and * static key mode are supported. No encryption will be performed on the packet, @@ -108,6 +132,11 @@ #include "packet_id.h" #include "mtu.h" +/** Wrapper struct to pass around MD5 digests */ +struct md5_digest { + uint8_t digest[MD5_DIGEST_LENGTH]; +}; + /* * Defines a key type and key length for both cipher and HMAC. */ @@ -133,13 +162,16 @@ struct key /** - * Container for one set of OpenSSL cipher and/or HMAC contexts. + * Container for one set of cipher and/or HMAC contexts. * @ingroup control_processor */ struct key_ctx { cipher_ctx_t *cipher; /**< Generic cipher %context. */ - hmac_ctx_t *hmac; /**< Generic HMAC %context. */ + hmac_ctx_t *hmac; /**< Generic HMAC %context. */ + uint8_t implicit_iv[OPENVPN_MAX_IV_LENGTH]; + /**< The implicit part of the IV */ + size_t implicit_iv_len; /**< The length of implicit_iv */ }; #define KEY_DIRECTION_BIDIRECTIONAL 0 /* same keys for both directions */ @@ -190,10 +222,11 @@ struct key_direction_state */ struct key_ctx_bi { - struct key_ctx encrypt; /**< OpenSSL cipher and/or HMAC contexts - * for sending direction. */ - struct key_ctx decrypt; /**< OpenSSL cipher and/or HMAC contexts - * for receiving direction. */ + struct key_ctx encrypt; /**< Cipher and/or HMAC contexts for sending + * direction. */ + struct key_ctx decrypt; /**< cipher and/or HMAC contexts for + * receiving direction. */ + bool initialized; }; /** @@ -202,11 +235,11 @@ struct key_ctx_bi */ struct crypto_options { - struct key_ctx_bi *key_ctx_bi; + struct key_ctx_bi key_ctx_bi; /**< OpenSSL cipher and HMAC contexts for * both sending and receiving * directions. */ - struct packet_id *packet_id; /**< Current packet ID state for both + struct packet_id packet_id; /**< Current packet ID state for both * sending and receiving directions. */ struct packet_id_persist *pid_persist; /**< Persistent packet ID state for @@ -233,6 +266,15 @@ struct crypto_options * security operation functions. */ }; +#define CRYPT_ERROR(format) \ + do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false) + +/** + * Minimal IV length for AEAD mode ciphers (in bytes): + * 4-byte packet id + 8 bytes implicit IV. + */ +#define OPENVPN_AEAD_MIN_IV_LEN (sizeof (packet_id_type) + 8) + #define RKF_MUST_SUCCEED (1<<0) #define RKF_INLINE (1<<1) void read_key_file (struct key2 *key2, const char *file, const unsigned int flags); @@ -257,9 +299,20 @@ bool write_key (const struct key *key, const struct key_type *kt, int read_key (struct key *key, const struct key_type *kt, struct buffer *buf); +/** + * Initialize a key_type structure with. + * + * @param kt The struct key_type to initialize + * @param ciphername The name of the cipher to use + * @param authname The name of the HMAC digest to use + * @param keysize The length of the cipher key to use, in bytes. Only valid + * for ciphers that support variable length keys. + * @param tls_mode Specifies wether we are running in TLS mode, which allows + * more ciphers than static key mode. + * @param warn Print warnings when null cipher / auth is used. + */ void init_key_type (struct key_type *kt, const char *ciphername, - bool ciphername_defined, const char *authname, bool authname_defined, - int keysize, bool cfb_ofb_allowed, bool warn); + const char *authname, int keysize, bool tls_mode, bool warn); /* * Key context functions @@ -296,18 +349,16 @@ void free_key_ctx_bi (struct key_ctx_bi *ctx); * * @param buf - The %buffer containing the packet on which to * perform security operations. - * @param work - A working %buffer. + * @param work - An initialized working %buffer. * @param opt - The security parameter state for this VPN tunnel. - * @param frame - The packet geometry parameters for this VPN - * tunnel. + * * @return This function returns void.\n On return, the \a buf argument * will point to the resulting %buffer. This %buffer will either * contain the processed packet ready for sending, or be empty if an * error occurred. */ void openvpn_encrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame); + struct crypto_options *opt); /** @@ -333,6 +384,8 @@ void openvpn_encrypt (struct buffer *buf, struct buffer work, * @param opt - The security parameter state for this VPN tunnel. * @param frame - The packet geometry parameters for this VPN * tunnel. + * @param ad_start - A pointer into buf, indicating from where to start + * authenticating additional data (AEAD mode only). * * @return * @li True, if the packet was authenticated and decrypted successfully. @@ -342,18 +395,35 @@ void openvpn_encrypt (struct buffer *buf, struct buffer work, * an error occurred. */ bool openvpn_decrypt (struct buffer *buf, struct buffer work, - const struct crypto_options *opt, - const struct frame* frame); + struct crypto_options *opt, const struct frame* frame, + const uint8_t *ad_start); /** @} name Functions for performing security operations on data channel packets */ +/** + * Check packet ID for replay, and perform replay administration. + * + * @param opt Crypto options for this packet, contains replay state. + * @param pin Packet ID read from packet. + * @param error_prefix Prefix to use when printing error messages. + * @param gc Garbage collector to use. + * + * @return true if packet ID is validated to be not a replay, false otherwise. + */ +bool crypto_check_replay(struct crypto_options *opt, + const struct packet_id_net *pin, const char *error_prefix, + struct gc_arena *gc); + + +/** Calculate crypto overhead and adjust frame to account for that */ void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type* kt, - bool cipher_defined, bool use_iv, bool packet_id, bool packet_id_long_form); +/** Return the worst-case OpenVPN crypto overhead (in bytes) */ +size_t crypto_max_overhead(void); /* Minimum length of the nonce used by the PRNG */ #define NONCE_SECRET_LEN_MIN 16 @@ -392,7 +462,7 @@ void prng_bytes (uint8_t *output, int len); void prng_uninit (); -void test_crypto (const struct crypto_options *co, struct frame* f); +void test_crypto (struct crypto_options *co, struct frame* f); /* key direction functions */ @@ -413,45 +483,31 @@ void key2_print (const struct key2* k, const char* prefix0, const char* prefix1); -#ifdef ENABLE_SSL - -#define GHK_INLINE (1<<0) -void get_tls_handshake_key (const struct key_type *key_type, - struct key_ctx_bi *ctx, - const char *passphrase_file, - const int key_direction, - const unsigned int flags); - -#else - -void init_ssl_lib (void); -void free_ssl_lib (void); - -#endif /* ENABLE_SSL */ +void crypto_read_openvpn_key (const struct key_type *key_type, + struct key_ctx_bi *ctx, const char *key_file, const char *key_inline, + const int key_direction, const char *key_name, const char *opt_name); /* - * md5 functions + * Inline functions */ -struct md5_state { - md_ctx_t ctx; -}; - -struct md5_digest { - uint8_t digest [MD5_DIGEST_LENGTH]; -}; - -const char *md5sum(uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc); -void md5_state_init (struct md5_state *s); -void md5_state_update (struct md5_state *s, void *data, size_t len); -void md5_state_final (struct md5_state *s, struct md5_digest *out); -void md5_digest_clear (struct md5_digest *digest); -bool md5_digest_defined (const struct md5_digest *digest); -bool md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2); - -/* - * Inline functions +/** + * As memcmp(), but constant-time. + * Returns 0 when data is equal, non-zero otherwise. */ +static inline int +memcmp_constant_time (const void *a, const void *b, size_t size) { + const uint8_t * a1 = a; + const uint8_t * b1 = b; + int ret = 0; + size_t i; + + for (i = 0; i < size; i++) { + ret |= *a1++ ^ *b1++; + } + + return ret; +} static inline bool key_ctx_bi_defined(const struct key_ctx_bi* key) diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h index 4c1ce9f..bf7d78c 100644 --- a/src/openvpn/crypto_backend.h +++ b/src/openvpn/crypto_backend.h @@ -33,11 +33,29 @@ #ifdef ENABLE_CRYPTO_OPENSSL #include "crypto_openssl.h" #endif -#ifdef ENABLE_CRYPTO_POLARSSL -#include "crypto_polarssl.h" +#ifdef ENABLE_CRYPTO_MBEDTLS +#include "crypto_mbedtls.h" #endif #include "basic.h" +/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */ +#define OPENVPN_AEAD_TAG_LENGTH 16 + +/* Maximum cipher block size (bytes) */ +#define OPENVPN_MAX_CIPHER_BLOCK_SIZE 32 + +/* Maximum HMAC digest size (bytes) */ +#define OPENVPN_MAX_HMAC_SIZE 64 + +/** Struct used in cipher name translation table */ +typedef struct { + const char *openvpn_name; /**< Cipher name used by OpenVPN */ + const char *lib_name; /**< Cipher name used by crypto library */ +} cipher_name_pair; + +/** Cipher name translation table */ +extern const cipher_name_pair cipher_name_translation_table[]; +extern const size_t cipher_name_translation_table_count; /* * This routine should have additional OpenSSL crypto library initialisations @@ -177,7 +195,8 @@ void cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH], * \c AES-128-CBC). * * @return A statically allocated structure containing parameters - * for the given cipher. + * for the given cipher, or NULL if no matching parameters + * were found. */ const cipher_kt_t * cipher_kt_get (const char *ciphername); @@ -220,6 +239,16 @@ int cipher_kt_iv_size (const cipher_kt_t *cipher_kt); */ int cipher_kt_block_size (const cipher_kt_t *cipher_kt); +/** + * Returns the MAC tag size of the cipher, in bytes. + * + * @param ctx Static cipher parameters. + * + * @return Tag size in bytes, or 0 if the tag size could not be + * determined. + */ +int cipher_kt_tag_size (const cipher_kt_t *cipher_kt); + /** * Returns the mode that the cipher runs in. * @@ -248,6 +277,15 @@ bool cipher_kt_mode_cbc(const cipher_kt_t *cipher); */ bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher); +/** + * Check if the supplied cipher is a supported AEAD mode cipher. + * + * @param cipher Static cipher parameters. + * + * @return true iff the cipher is a AEAD mode cipher. + */ +bool cipher_kt_mode_aead(const cipher_kt_t *cipher); + /** * @@ -263,7 +301,7 @@ bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher); * @param key_len Length of the key, in bytes * @param kt Static cipher parameters to use * @param enc Whether to encrypt or decrypt (either - * \c POLARSSL_OP_ENCRYPT or \c POLARSSL_OP_DECRYPT). + * \c MBEDTLS_OP_ENCRYPT or \c MBEDTLS_OP_DECRYPT). */ void cipher_ctx_init (cipher_ctx_t *ctx, uint8_t *key, int key_len, const cipher_kt_t *kt, int enc); @@ -286,6 +324,15 @@ void cipher_ctx_cleanup (cipher_ctx_t *ctx); */ int cipher_ctx_iv_length (const cipher_ctx_t *ctx); +/** + * Gets the computed message authenticated code (MAC) tag for this cipher. + * + * @param ctx The cipher's context + * @param tag The buffer to write computed tag in. + * @param tag_size The tag buffer size, in bytes. + */ +int cipher_ctx_get_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len); + /** * Returns the block size of the cipher, in bytes. * @@ -308,12 +355,12 @@ int cipher_ctx_mode (const cipher_ctx_t *ctx); /** * Returns the static cipher parameters for this context. * - * @param ctx Cipher's context. May not be NULL. + * @param ctx Cipher's context. * - * @return Static cipher parameters for the supplied context. + * @return Static cipher parameters for the supplied context, or + * NULL if unable to determine cipher parameters. */ -const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) - __attribute__((nonnull)); +const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx); /** * Resets the given cipher context, setting the IV to the specified value. @@ -326,6 +373,18 @@ const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) */ int cipher_ctx_reset (cipher_ctx_t *ctx, uint8_t *iv_buf); +/** + * Updates the given cipher context, providing additional data (AD) for + * authenticated encryption with additional data (AEAD) cipher modes. + * + * @param ctx Cipher's context. May not be NULL. + * @param src Source buffer + * @param src_len Length of the source buffer, in bytes + * + * @return \c 0 on failure, \c 1 on success. + */ +int cipher_ctx_update_ad (cipher_ctx_t *ctx, const uint8_t *src, int src_len); + /** * Updates the given cipher context, encrypting data in the source buffer, and * placing any complete blocks in the destination buffer. @@ -358,6 +417,23 @@ int cipher_ctx_update (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len, */ int cipher_ctx_final (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len); +/** + * Like \c cipher_ctx_final, but check the computed authentication tag against + * the supplied (expected) tag. This function reports failure when the tags + * don't match. + * + * @param ctx Cipher's context. May not be NULL. + * @param dst Destination buffer. + * @param dst_len Length of the destination buffer, in bytes. + * @param tag The expected authentication tag. + * @param tag_len The length of tag, in bytes. + * + * @return \c 0 on failure, \c 1 on success. + */ +int cipher_ctx_final_check_tag (cipher_ctx_t *ctx, uint8_t *dst, int *dst_len, + uint8_t *tag, size_t tag_len); + + /* * * Generic message digest information functions @@ -525,4 +601,24 @@ void hmac_ctx_update (hmac_ctx_t *ctx, const uint8_t *src, int src_len); */ void hmac_ctx_final (hmac_ctx_t *ctx, uint8_t *dst); +/** + * Translate an OpenVPN cipher name to a crypto library cipher name. + * + * @param cipher_name An OpenVPN cipher name + * + * @return The corresponding crypto library cipher name, or NULL + * if no matching cipher name was found. + */ +const char * translate_cipher_name_from_openvpn (const char *cipher_name); + +/** + * Translate a crypto library cipher name to an OpenVPN cipher name. + * + * @param cipher_name A crypto library cipher name + * + * @return The corresponding OpenVPN cipher name, or NULL if no + * matching cipher name was found. + */ +const char * translate_cipher_name_to_openvpn (const char *cipher_name); + #endif /* CRYPTO_BACKEND_H_ */ diff --git a/src/openvpn/crypto_mbedtls.c b/src/openvpn/crypto_mbedtls.c new file mode 100644 index 0000000..6ad5924 --- /dev/null +++ b/src/openvpn/crypto_mbedtls.c @@ -0,0 +1,785 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file Data Channel Cryptography mbed TLS-specific backend interface + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) + +#include "errlevel.h" +#include "basic.h" +#include "buffer.h" +#include "integer.h" +#include "crypto_backend.h" +#include "otime.h" +#include "misc.h" + +#include +#include +#include +#include +#include + +#include + + +/* + * + * Hardware engine support. Allows loading/unloading of engines. + * + */ + +void +crypto_init_lib_engine (const char *engine_name) +{ + msg (M_WARN, "Note: mbed TLS hardware crypto engine functionality is not " + "available"); +} + +/* + * + * Functions related to the core crypto library + * + */ + +void +crypto_init_lib (void) +{ +} + +void +crypto_uninit_lib (void) +{ +} + +void +crypto_clear_error (void) +{ +} + +bool mbed_log_err(unsigned int flags, int errval, const char *prefix) +{ + if (0 != errval) + { + char errstr[256]; + mbedtls_strerror(errval, errstr, sizeof(errstr)); + + if (NULL == prefix) prefix = "mbed TLS error"; + msg (flags, "%s: %s", prefix, errstr); + } + + return 0 == errval; +} + +bool mbed_log_func_line(unsigned int flags, int errval, const char *func, + int line) +{ + char prefix[256]; + + if (!openvpn_snprintf(prefix, sizeof(prefix), "%s:%d", func, line)) + return mbed_log_err(flags, errval, func); + + return mbed_log_err(flags, errval, prefix); +} + + +#ifdef DMALLOC +void +crypto_init_dmalloc (void) +{ + msg (M_ERR, "Error: dmalloc support is not available for mbed TLS."); +} +#endif /* DMALLOC */ + +const cipher_name_pair cipher_name_translation_table[] = { + { "BF-CBC", "BLOWFISH-CBC" }, + { "BF-CFB", "BLOWFISH-CFB64" }, + { "CAMELLIA-128-CFB", "CAMELLIA-128-CFB128" }, + { "CAMELLIA-192-CFB", "CAMELLIA-192-CFB128" }, + { "CAMELLIA-256-CFB", "CAMELLIA-256-CFB128" } +}; +const size_t cipher_name_translation_table_count = + sizeof (cipher_name_translation_table) / sizeof (*cipher_name_translation_table); + +static void print_cipher(const cipher_kt_t *info) +{ + if (info && (cipher_kt_mode_cbc(info) +#ifdef HAVE_AEAD_CIPHER_MODES + || cipher_kt_mode_aead(info) +#endif + )) + { + const char *ssl_only = cipher_kt_mode_cbc(info) ? + "" : ", TLS client/server mode only"; + const char *var_key_size = info->flags & MBEDTLS_CIPHER_VARIABLE_KEY_LEN ? + " by default" : ""; + + printf ("%s (%d bit key%s, %d bit block%s)\n", + cipher_kt_name(info), cipher_kt_key_size(info) * 8, var_key_size, + cipher_kt_block_size(info) * 8, ssl_only); + } +} + +void +show_available_ciphers () +{ + const int *ciphers = mbedtls_cipher_list(); + +#ifndef ENABLE_SMALL + printf ("The following ciphers and cipher modes are available for use\n" + "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n" + "parameter to the --cipher option. Using a CBC or GCM mode is\n" + "recommended. In static key mode only CBC mode is allowed.\n\n"); +#endif + + while (*ciphers != 0) + { + const cipher_kt_t *info = mbedtls_cipher_info_from_type(*ciphers); + if (info && cipher_kt_block_size(info) >= 128/8) + { + print_cipher(info); + } + ciphers++; + } + + printf ("\nThe following ciphers have a block size of less than 128 bits, \n" + "and are therefore deprecated. Do not use unless you have to.\n\n"); + ciphers = mbedtls_cipher_list(); + while (*ciphers != 0) + { + const cipher_kt_t *info = mbedtls_cipher_info_from_type(*ciphers); + if (info && cipher_kt_block_size(info) < 128/8) + { + print_cipher(info); + } + ciphers++; + } + printf ("\n"); +} + +void +show_available_digests () +{ + const int *digests = mbedtls_md_list(); + +#ifndef ENABLE_SMALL + printf ("The following message digests are available for use with\n" + PACKAGE_NAME ". A message digest is used in conjunction with\n" + "the HMAC function, to authenticate received packets.\n" + "You can specify a message digest as parameter to\n" + "the --auth option.\n\n"); +#endif + + while (*digests != 0) + { + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(*digests); + + if (info) + printf ("%s %d bit default key\n", mbedtls_md_get_name(info), + mbedtls_md_get_size(info) * 8); + digests++; + } + printf ("\n"); +} + +void +show_available_engines () +{ + printf ("Sorry, mbed TLS hardware crypto engine functionality is not " + "available\n"); +} + +/* + * + * Random number functions, used in cases where we want + * reasonably strong cryptographic random number generation + * without depleting our entropy pool. Used for random + * IV values and a number of other miscellaneous tasks. + * + */ + +/* + * Initialise the given ctr_drbg context, using a personalisation string and an + * entropy gathering function. + */ +mbedtls_ctr_drbg_context * rand_ctx_get() +{ + static mbedtls_entropy_context ec = {0}; + static mbedtls_ctr_drbg_context cd_ctx = {0}; + static bool rand_initialised = false; + + if (!rand_initialised) + { + struct gc_arena gc = gc_new(); + struct buffer pers_string = alloc_buf_gc(100, &gc); + + /* + * Personalisation string, should be as unique as possible (see NIST + * 800-90 section 8.7.1). We have very little information at this stage. + * Include Program Name, memory address of the context and PID. + */ + buf_printf(&pers_string, "OpenVPN %0u %p %s", platform_getpid(), &cd_ctx, time_string(0, 0, 0, &gc)); + + /* Initialise mbed TLS RNG, and built-in entropy sources */ + mbedtls_entropy_init(&ec); + + mbedtls_ctr_drbg_init(&cd_ctx); + if (!mbed_ok(mbedtls_ctr_drbg_seed(&cd_ctx, mbedtls_entropy_func, &ec, + BPTR(&pers_string), BLEN(&pers_string)))) + msg (M_FATAL, "Failed to initialize random generator"); + + gc_free(&gc); + rand_initialised = true; + } + + return &cd_ctx; +} + +#ifdef ENABLE_PREDICTION_RESISTANCE +void rand_ctx_enable_prediction_resistance() +{ + mbedtls_ctr_drbg_context *cd_ctx = rand_ctx_get(); + + mbedtls_ctr_drbg_set_prediction_resistance(cd_ctx, 1); +} +#endif /* ENABLE_PREDICTION_RESISTANCE */ + +int +rand_bytes (uint8_t *output, int len) +{ + mbedtls_ctr_drbg_context *rng_ctx = rand_ctx_get(); + + while (len > 0) + { + const size_t blen = min_int (len, MBEDTLS_CTR_DRBG_MAX_REQUEST); + if (0 != mbedtls_ctr_drbg_random(rng_ctx, output, blen)) + return 0; + + output += blen; + len -= blen; + } + + return 1; +} + +/* + * + * Key functions, allow manipulation of keys. + * + */ + + +int +key_des_num_cblocks (const mbedtls_cipher_info_t *kt) +{ + int ret = 0; + if (kt->type == MBEDTLS_CIPHER_DES_CBC) + ret = 1; + if (kt->type == MBEDTLS_CIPHER_DES_EDE_CBC) + ret = 2; + if (kt->type == MBEDTLS_CIPHER_DES_EDE3_CBC) + ret = 3; + + dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret); + return ret; +} + +bool +key_des_check (uint8_t *key, int key_len, int ndc) +{ + int i; + struct buffer b; + + buf_set_read (&b, key, key_len); + + for (i = 0; i < ndc; ++i) + { + unsigned char *key = buf_read_alloc(&b, MBEDTLS_DES_KEY_SIZE); + if (!key) + { + msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: insufficient key material"); + goto err; + } + if (0 != mbedtls_des_key_check_weak(key)) + { + msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: weak key detected"); + goto err; + } + if (0 != mbedtls_des_key_check_key_parity(key)) + { + msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: bad parity detected"); + goto err; + } + } + return true; + + err: + return false; +} + +void +key_des_fixup (uint8_t *key, int key_len, int ndc) +{ + int i; + struct buffer b; + + buf_set_read (&b, key, key_len); + for (i = 0; i < ndc; ++i) + { + unsigned char *key = buf_read_alloc(&b, MBEDTLS_DES_KEY_SIZE); + if (!key) + { + msg (D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material"); + return; + } + mbedtls_des_key_set_parity(key); + } +} + +/* + * + * Generic cipher key type functions + * + */ + + +const mbedtls_cipher_info_t * +cipher_kt_get (const char *ciphername) +{ + const mbedtls_cipher_info_t *cipher = NULL; + + ASSERT (ciphername); + + cipher = mbedtls_cipher_info_from_string(ciphername); + + if (NULL == cipher) + { + msg (D_LOW, "Cipher algorithm '%s' not found", ciphername); + return NULL; + } + + if (cipher->key_bitlen/8 > MAX_CIPHER_KEY_LENGTH) + { + msg (D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) " + "which is larger than " PACKAGE_NAME "'s current maximum key size " + "(%d bytes)", ciphername, cipher->key_bitlen/8, MAX_CIPHER_KEY_LENGTH); + return NULL; + } + + return cipher; +} + +const char * +cipher_kt_name (const mbedtls_cipher_info_t *cipher_kt) +{ + if (NULL == cipher_kt) + return "[null-cipher]"; + + return translate_cipher_name_to_openvpn(cipher_kt->name); +} + +int +cipher_kt_key_size (const mbedtls_cipher_info_t *cipher_kt) +{ + if (NULL == cipher_kt) + return 0; + + return cipher_kt->key_bitlen/8; +} + +int +cipher_kt_iv_size (const mbedtls_cipher_info_t *cipher_kt) +{ + if (NULL == cipher_kt) + return 0; + return cipher_kt->iv_size; +} + +int +cipher_kt_block_size (const mbedtls_cipher_info_t *cipher_kt) +{ + if (NULL == cipher_kt) + return 0; + return cipher_kt->block_size; +} + +int +cipher_kt_tag_size (const mbedtls_cipher_info_t *cipher_kt) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + if (cipher_kt && cipher_kt_mode_aead(cipher_kt)) + return OPENVPN_AEAD_TAG_LENGTH; +#endif + return 0; +} + +int +cipher_kt_mode (const mbedtls_cipher_info_t *cipher_kt) +{ + ASSERT(NULL != cipher_kt); + return cipher_kt->mode; +} + +bool +cipher_kt_mode_cbc(const cipher_kt_t *cipher) +{ + return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; +} + +bool +cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) +{ + return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || + cipher_kt_mode(cipher) == OPENVPN_MODE_CFB); +} + +bool +cipher_kt_mode_aead(const cipher_kt_t *cipher) +{ + return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_GCM; +} + + +/* + * + * Generic cipher context functions + * + */ + + +void +cipher_ctx_init (mbedtls_cipher_context_t *ctx, uint8_t *key, int key_len, + const mbedtls_cipher_info_t *kt, const mbedtls_operation_t operation) +{ + ASSERT(NULL != kt && NULL != ctx); + + CLEAR (*ctx); + + if (!mbed_ok(mbedtls_cipher_setup(ctx, kt))) + msg (M_FATAL, "mbed TLS cipher context init #1"); + + if (!mbed_ok(mbedtls_cipher_setkey(ctx, key, key_len*8, operation))) + msg (M_FATAL, "mbed TLS cipher set key"); + + /* make sure we used a big enough key */ + ASSERT (ctx->key_bitlen <= key_len*8); +} + +void cipher_ctx_cleanup (mbedtls_cipher_context_t *ctx) +{ + mbedtls_cipher_free(ctx); +} + +int cipher_ctx_iv_length (const mbedtls_cipher_context_t *ctx) +{ + return mbedtls_cipher_get_iv_size(ctx); +} + +int cipher_ctx_get_tag (cipher_ctx_t *ctx, uint8_t* tag, int tag_len) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + if (tag_len > SIZE_MAX) + return 0; + + if (!mbed_ok (mbedtls_cipher_write_tag (ctx, (unsigned char *) tag, tag_len))) + return 0; + + return 1; +#else + ASSERT(0); +#endif /* HAVE_AEAD_CIPHER_MODES */ +} + +int cipher_ctx_block_size(const mbedtls_cipher_context_t *ctx) +{ + return mbedtls_cipher_get_block_size(ctx); +} + +int cipher_ctx_mode (const mbedtls_cipher_context_t *ctx) +{ + ASSERT(NULL != ctx); + + return cipher_kt_mode(ctx->cipher_info); +} + +const cipher_kt_t * +cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) +{ + return ctx ? ctx->cipher_info : NULL; +} + +int cipher_ctx_reset (mbedtls_cipher_context_t *ctx, uint8_t *iv_buf) +{ + if (!mbed_ok(mbedtls_cipher_reset(ctx))) + return 0; + + if (!mbed_ok(mbedtls_cipher_set_iv(ctx, iv_buf, ctx->cipher_info->iv_size))) + return 0; + + return 1; +} + +int cipher_ctx_update_ad (cipher_ctx_t *ctx, const uint8_t *src, int src_len) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + if (src_len > SIZE_MAX) + return 0; + + if (!mbed_ok (mbedtls_cipher_update_ad (ctx, src, src_len))) + return 0; + + return 1; +#else + ASSERT(0); +#endif /* HAVE_AEAD_CIPHER_MODES */ +} + +int cipher_ctx_update (mbedtls_cipher_context_t *ctx, uint8_t *dst, + int *dst_len, uint8_t *src, int src_len) +{ + size_t s_dst_len = *dst_len; + + if (!mbed_ok(mbedtls_cipher_update(ctx, src, (size_t) src_len, dst, + &s_dst_len))) + return 0; + + *dst_len = s_dst_len; + + return 1; +} + +int cipher_ctx_final (mbedtls_cipher_context_t *ctx, uint8_t *dst, int *dst_len) +{ + size_t s_dst_len = *dst_len; + + if (!mbed_ok(mbedtls_cipher_finish(ctx, dst, &s_dst_len))) + return 0; + + *dst_len = s_dst_len; + + return 1; +} + +int cipher_ctx_final_check_tag (mbedtls_cipher_context_t *ctx, uint8_t *dst, + int *dst_len, uint8_t *tag, size_t tag_len) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + size_t olen = 0; + + if (MBEDTLS_DECRYPT != ctx->operation) + return 0; + + if (tag_len > SIZE_MAX) + return 0; + + if (!mbed_ok (mbedtls_cipher_finish (ctx, dst, &olen))) + { + msg (D_CRYPT_ERRORS, "%s: cipher_ctx_final() failed", __func__); + return 0; + } + + if (olen > INT_MAX) + return 0; + *dst_len = olen; + + if (!mbed_ok (mbedtls_cipher_check_tag (ctx, (const unsigned char *) tag, + tag_len))) + return 0; + + return 1; +#else + ASSERT(0); +#endif /* HAVE_AEAD_CIPHER_MODES */ +} + +void +cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH], + unsigned char *src, + unsigned char *dst) +{ + mbedtls_des_context ctx; + + ASSERT (mbed_ok(mbedtls_des_setkey_enc(&ctx, key))); + ASSERT (mbed_ok(mbedtls_des_crypt_ecb(&ctx, src, dst))); +} + + + +/* + * + * Generic message digest information functions + * + */ + + +const mbedtls_md_info_t * +md_kt_get (const char *digest) +{ + const mbedtls_md_info_t *md = NULL; + ASSERT (digest); + + md = mbedtls_md_info_from_string(digest); + if (!md) + msg (M_FATAL, "Message hash algorithm '%s' not found", digest); + if (mbedtls_md_get_size(md) > MAX_HMAC_KEY_LENGTH) + msg (M_FATAL, "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum hash size (%d bytes)", + digest, + mbedtls_md_get_size(md), + MAX_HMAC_KEY_LENGTH); + return md; +} + +const char * +md_kt_name (const mbedtls_md_info_t *kt) +{ + if (NULL == kt) + return "[null-digest]"; + return mbedtls_md_get_name (kt); +} + +int +md_kt_size (const mbedtls_md_info_t *kt) +{ + if (NULL == kt) + return 0; + return mbedtls_md_get_size(kt); +} + +/* + * + * Generic message digest functions + * + */ + +int +md_full (const md_kt_t *kt, const uint8_t *src, int src_len, uint8_t *dst) +{ + return 0 == mbedtls_md(kt, src, src_len, dst); +} + + +void +md_ctx_init (mbedtls_md_context_t *ctx, const mbedtls_md_info_t *kt) +{ + ASSERT(NULL != ctx && NULL != kt); + + mbedtls_md_init(ctx); + ASSERT(0 == mbedtls_md_setup(ctx, kt, 0)); + ASSERT(0 == mbedtls_md_starts(ctx)); +} + +void +md_ctx_cleanup(mbedtls_md_context_t *ctx) +{ +} + +int +md_ctx_size (const mbedtls_md_context_t *ctx) +{ + if (NULL == ctx) + return 0; + return mbedtls_md_get_size(ctx->md_info); +} + +void +md_ctx_update (mbedtls_md_context_t *ctx, const uint8_t *src, int src_len) +{ + ASSERT(0 == mbedtls_md_update(ctx, src, src_len)); +} + +void +md_ctx_final (mbedtls_md_context_t *ctx, uint8_t *dst) +{ + ASSERT(0 == mbedtls_md_finish(ctx, dst)); + mbedtls_md_free(ctx); +} + + +/* + * + * Generic HMAC functions + * + */ + + +/* + * TODO: re-enable dmsg for crypto debug + */ +void +hmac_ctx_init (mbedtls_md_context_t *ctx, const uint8_t *key, int key_len, + const mbedtls_md_info_t *kt) +{ + ASSERT(NULL != kt && NULL != ctx); + + mbedtls_md_init(ctx); + ASSERT(0 == mbedtls_md_setup(ctx, kt, 1)); + ASSERT(0 == mbedtls_md_hmac_starts(ctx, key, key_len)); + + /* make sure we used a big enough key */ + ASSERT (mbedtls_md_get_size(kt) <= key_len); +} + +void +hmac_ctx_cleanup(mbedtls_md_context_t *ctx) +{ + mbedtls_md_free(ctx); +} + +int +hmac_ctx_size (const mbedtls_md_context_t *ctx) +{ + if (NULL == ctx) + return 0; + return mbedtls_md_get_size(ctx->md_info); +} + +void +hmac_ctx_reset (mbedtls_md_context_t *ctx) +{ + ASSERT(0 == mbedtls_md_hmac_reset(ctx)); +} + +void +hmac_ctx_update (mbedtls_md_context_t *ctx, const uint8_t *src, int src_len) +{ + ASSERT(0 == mbedtls_md_hmac_update(ctx, src, src_len)); +} + +void +hmac_ctx_final (mbedtls_md_context_t *ctx, uint8_t *dst) +{ + ASSERT(0 == mbedtls_md_hmac_finish(ctx, dst)); +} + +#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_MBEDTLS */ diff --git a/src/openvpn/crypto_mbedtls.h b/src/openvpn/crypto_mbedtls.h new file mode 100644 index 0000000..574a63f --- /dev/null +++ b/src/openvpn/crypto_mbedtls.h @@ -0,0 +1,146 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file Data Channel Cryptography mbed TLS-specific backend interface + */ + +#ifndef CRYPTO_MBEDTLS_H_ +#define CRYPTO_MBEDTLS_H_ + +#include +#include +#include + +/** Generic cipher key type %context. */ +typedef mbedtls_cipher_info_t cipher_kt_t; + +/** Generic message digest key type %context. */ +typedef mbedtls_md_info_t md_kt_t; + +/** Generic cipher %context. */ +typedef mbedtls_cipher_context_t cipher_ctx_t; + +/** Generic message digest %context. */ +typedef mbedtls_md_context_t md_ctx_t; + +/** Generic HMAC %context. */ +typedef mbedtls_md_context_t hmac_ctx_t; + +/** Maximum length of an IV */ +#define OPENVPN_MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH + +/** Cipher is in CBC mode */ +#define OPENVPN_MODE_CBC MBEDTLS_MODE_CBC + +/** Cipher is in OFB mode */ +#define OPENVPN_MODE_OFB MBEDTLS_MODE_OFB + +/** Cipher is in CFB mode */ +#define OPENVPN_MODE_CFB MBEDTLS_MODE_CFB + +/** Cipher is in GCM mode */ +#define OPENVPN_MODE_GCM MBEDTLS_MODE_GCM + +/** Cipher should encrypt */ +#define OPENVPN_OP_ENCRYPT MBEDTLS_ENCRYPT + +/** Cipher should decrypt */ +#define OPENVPN_OP_DECRYPT MBEDTLS_DECRYPT + +#define MD4_DIGEST_LENGTH 16 +#define MD5_DIGEST_LENGTH 16 +#define SHA_DIGEST_LENGTH 20 +#define DES_KEY_LENGTH 8 + +/** + * Returns a singleton instance of the mbed TLS random number generator. + * + * For PolarSSL/mbed TLS 1.1+, this is the CTR_DRBG random number generator. If it + * hasn't been initialised yet, the RNG will be initialised using the default + * entropy sources. Aside from the default platform entropy sources, an + * additional entropy source, the HAVEGE random number generator will also be + * added. During initialisation, a personalisation string will be added based + * on the time, the PID, and a pointer to the random context. + */ +mbedtls_ctr_drbg_context *rand_ctx_get(); + +#ifdef ENABLE_PREDICTION_RESISTANCE +/** + * Enable prediction resistance on the random number generator. + */ +void rand_ctx_enable_prediction_resistance(); +#endif + +/** + * Log the supplied mbed TLS error, prefixed by supplied prefix. + * + * @param flags Flags to indicate error type and priority. + * @param errval mbed TLS error code to convert to error message. + * @param prefix Prefix to mbed TLS error message. + * + * @returns true if no errors are detected, false otherwise. + */ +bool mbed_log_err(unsigned int flags, int errval, const char *prefix); + +/** + * Log the supplied mbed TLS error, prefixed by function name and line number. + * + * @param flags Flags to indicate error type and priority. + * @param errval mbed TLS error code to convert to error message. + * @param func Function name where error was reported. + * @param line Line number where error was reported. + * + * @returns true if no errors are detected, false otherwise. + */ +bool mbed_log_func_line(unsigned int flags, int errval, const char *func, + int line); + +/** Wraps mbed_log_func_line() to prevent function calls for non-errors */ +static inline bool mbed_log_func_line_lite(unsigned int flags, int errval, + const char *func, int line) { + if (errval) { + return mbed_log_func_line (flags, errval, func, line); + } + return true; +} + +/** + * Check errval and log on error. + * + * Convenience wrapper to put around mbed TLS library calls, e.g. + * if (!mbed_ok (mbedtls_ssl_func())) return 0; + * or + * ASSERT (mbed_ok (mbedtls_ssl_func())); + * + * @param errval mbed TLS error code to convert to error message. + * + * @returns true if no errors are detected, false otherwise. + */ +#define mbed_ok(errval) \ + mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__) + + +#endif /* CRYPTO_MBEDTLS_H_ */ diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index c147245..1ea06bb 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -42,9 +42,12 @@ #include "integer.h" #include "crypto.h" #include "crypto_backend.h" -#include -#include + #include +#include +#include +#include +#include /* * Check for key size creepage. @@ -58,41 +61,6 @@ #warning Some OpenSSL HMAC message digests now support key lengths greater than MAX_HMAC_KEY_LENGTH -- consider increasing MAX_HMAC_KEY_LENGTH #endif -/* - * - * Workarounds for incompatibilites between OpenSSL libraries. - * Right now we accept OpenSSL libraries from 0.9.5 to 0.9.7. - * - */ - -#if SSLEAY_VERSION_NUMBER < 0x00907000L - -/* Workaround: EVP_CIPHER_mode is defined wrong in OpenSSL 0.9.6 but is fixed in 0.9.7 */ -#undef EVP_CIPHER_mode -#define EVP_CIPHER_mode(e) (((e)->flags) & EVP_CIPH_MODE) - -#define DES_cblock des_cblock -#define DES_is_weak_key des_is_weak_key -#define DES_check_key_parity des_check_key_parity -#define DES_set_odd_parity des_set_odd_parity - -#define HMAC_CTX_init(ctx) CLEAR (*ctx) -#define HMAC_Init_ex(ctx,sec,len,md,impl) HMAC_Init(ctx, sec, len, md) -#define HMAC_CTX_cleanup(ctx) HMAC_cleanup(ctx) -#define EVP_MD_CTX_cleanup(md) CLEAR (*md) - -#define INFO_CALLBACK_SSL_CONST - -#endif - -#ifndef EVP_CIPHER_name -#define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e)) -#endif - -#ifndef EVP_MD_name -#define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_type(e)) -#endif - #if HAVE_OPENSSL_ENGINE #include @@ -179,14 +147,6 @@ crypto_init_lib_engine (const char *engine_name) void crypto_init_lib (void) { -#ifndef ENABLE_SSL - /* If SSL is enabled init is taken care of in ssl_openssl.c */ -#ifndef ENABLE_SMALL - ERR_load_crypto_strings (); -#endif - OpenSSL_add_all_algorithms (); -#endif - /* * If you build the OpenSSL library and OpenVPN with * CRYPTO_MDEBUG, you will get a listing of OpenSSL @@ -201,14 +161,6 @@ crypto_init_lib (void) void crypto_uninit_lib (void) { -#ifndef ENABLE_SSL - /* If SSL is enabled cleanup is taken care of in ssl_openssl.c */ - EVP_cleanup (); -#ifndef ENABLE_SMALL - ERR_free_strings (); -#endif -#endif - #ifdef CRYPTO_MDEBUG FILE* fp = fopen ("sdlog", "w"); ASSERT (fp); @@ -237,9 +189,21 @@ crypto_print_openssl_errors(const unsigned int flags) { size_t err = 0; while ((err = ERR_get_error ())) - msg (flags, "OpenSSL: %s", ERR_error_string (err, NULL)); + { + /* Be more clear about frequently occurring "no shared cipher" error */ + if (err == ERR_PACK(ERR_LIB_SSL,SSL_F_SSL3_GET_CLIENT_HELLO, + SSL_R_NO_SHARED_CIPHER)) + { + msg (D_CRYPT_ERRORS, "TLS error: The server has no TLS ciphersuites " + "in common with the client. Your --tls-cipher setting might be " + "too restrictive."); + } + + msg (flags, "OpenSSL: %s", ERR_error_string (err, NULL)); + } } + /* * * OpenSSL memory debugging. If dmalloc debugging is enabled, tell @@ -276,55 +240,97 @@ crypto_init_dmalloc (void) } #endif /* DMALLOC */ -const char * -translate_cipher_name_from_openvpn (const char *cipher_name) { - // OpenSSL doesn't require any translation - return cipher_name; +const cipher_name_pair cipher_name_translation_table[] = { + { "AES-128-GCM", "id-aes128-GCM" }, + { "AES-192-GCM", "id-aes192-GCM" }, + { "AES-256-GCM", "id-aes256-GCM" }, +}; +const size_t cipher_name_translation_table_count = + sizeof (cipher_name_translation_table) / sizeof (*cipher_name_translation_table); + + +static int +cipher_name_cmp(const void *a, const void *b) +{ + const EVP_CIPHER * const *cipher_a = a; + const EVP_CIPHER * const *cipher_b = b; + + const char *cipher_name_a = + translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_a)); + const char *cipher_name_b = + translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_b)); + + return strcmp(cipher_name_a, cipher_name_b); } -const char * -translate_cipher_name_to_openvpn (const char *cipher_name) { - // OpenSSL doesn't require any translation - return cipher_name; +static void +print_cipher(const EVP_CIPHER *cipher) +{ + const char *var_key_size = + (EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ? + " by default" : ""; + const char *ssl_only = cipher_kt_mode_cbc(cipher) ? + "" : ", TLS client/server mode only"; + + printf ("%s (%d bit key%s, %d bit block%s)\n", + translate_cipher_name_to_openvpn (EVP_CIPHER_name (cipher)), + EVP_CIPHER_key_length (cipher) * 8, var_key_size, + cipher_kt_block_size (cipher) * 8, ssl_only); } void show_available_ciphers () { int nid; + size_t i; + /* If we ever exceed this, we must be more selective */ + const size_t cipher_list_len = 1000; + const EVP_CIPHER *cipher_list[cipher_list_len]; + size_t num_ciphers = 0; #ifndef ENABLE_SMALL - printf ("The following ciphers and cipher modes are available\n" - "for use with " PACKAGE_NAME ". Each cipher shown below may be\n" - "used as a parameter to the --cipher option. The default\n" - "key size is shown as well as whether or not it can be\n" - "changed with the --keysize directive. Using a CBC mode\n" - "is recommended. In static key mode only CBC mode is allowed.\n\n"); + printf ("The following ciphers and cipher modes are available for use\n" + "with " PACKAGE_NAME ". Each cipher shown below may be use as a\n" + "parameter to the --cipher option. The default key size is\n" + "shown as well as whether or not it can be changed with the\n" + "--keysize directive. Using a CBC or GCM mode is recommended.\n" + "In static key mode only CBC mode is allowed.\n\n"); #endif - for (nid = 0; nid < 10000; ++nid) /* is there a better way to get the size of the nid list? */ + for (nid = 0; nid < 10000; ++nid) { - const EVP_CIPHER *cipher = EVP_get_cipherbynid (nid); - if (cipher) - { - if (cipher_kt_mode_cbc(cipher) + const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid); + if (cipher && (cipher_kt_mode_cbc(cipher) #ifdef ENABLE_OFB_CFB_MODE || cipher_kt_mode_ofb_cfb(cipher) #endif - ) - { - const char *var_key_size = - (EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ? - "variable" : "fixed"; - const char *ssl_only = cipher_kt_mode_ofb_cfb(cipher) ? - " (TLS client/server mode)" : ""; - - printf ("%s %d bit default key (%s)%s\n", OBJ_nid2sn (nid), - EVP_CIPHER_key_length (cipher) * 8, var_key_size, - ssl_only); - } +#ifdef HAVE_AEAD_CIPHER_MODES + || cipher_kt_mode_aead(cipher) +#endif + )) + { + cipher_list[num_ciphers++] = cipher; + } + if (num_ciphers == cipher_list_len) + { + msg (M_WARN, "WARNING: Too many ciphers, not showing all"); + break; } } + + qsort (cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp); + + for (i = 0; i < num_ciphers; i++) { + if (cipher_kt_block_size(cipher_list[i]) >= 128/8) + print_cipher(cipher_list[i]); + } + + printf ("\nThe following ciphers have a block size of less than 128 bits, \n" + "and are therefore deprecated. Do not use unless you have to.\n\n"); + for (i = 0; i < num_ciphers; i++) { + if (cipher_kt_block_size(cipher_list[i]) < 128/8) + print_cipher(cipher_list[i]); + } printf ("\n"); } @@ -498,13 +504,20 @@ cipher_kt_get (const char *ciphername) cipher = EVP_get_cipherbyname (ciphername); if (NULL == cipher) - crypto_msg (M_FATAL, "Cipher algorithm '%s' not found", ciphername); + { + crypto_msg (D_LOW, "Cipher algorithm '%s' not found", ciphername); + return NULL; + } + if (EVP_CIPHER_key_length (cipher) > MAX_CIPHER_KEY_LENGTH) - msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)", - ciphername, - EVP_CIPHER_key_length (cipher), - MAX_CIPHER_KEY_LENGTH); + { + msg (D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) " + "which is larger than " PACKAGE_NAME "'s current maximum key size " + "(%d bytes)", ciphername, EVP_CIPHER_key_length (cipher), + MAX_CIPHER_KEY_LENGTH); + return NULL; + } return cipher; } @@ -530,9 +543,46 @@ cipher_kt_iv_size (const EVP_CIPHER *cipher_kt) } int -cipher_kt_block_size (const EVP_CIPHER *cipher_kt) +cipher_kt_block_size (const EVP_CIPHER *cipher) { + /* OpenSSL reports OFB/CFB/GCM cipher block sizes as '1 byte'. To work + * around that, try to replace the mode with 'CBC' and return the block size + * reported for that cipher, if possible. If that doesn't work, just return + * the value reported by OpenSSL. + */ + char *name = NULL; + char *mode_str = NULL; + const char *orig_name = NULL; + const EVP_CIPHER *cbc_cipher = NULL; + + int block_size = EVP_CIPHER_block_size(cipher); + + orig_name = cipher_kt_name(cipher); + if (!orig_name) + goto cleanup; + + name = string_alloc(translate_cipher_name_to_openvpn(orig_name), NULL); + mode_str = strrchr (name, '-'); + if (!mode_str || strlen(mode_str) < 4) + goto cleanup; + + strcpy (mode_str, "-CBC"); + + cbc_cipher = EVP_get_cipherbyname(translate_cipher_name_from_openvpn(name)); + if (cbc_cipher) + block_size = EVP_CIPHER_block_size(cbc_cipher); + +cleanup: + free (name); + return block_size; +} + +int +cipher_kt_tag_size (const EVP_CIPHER *cipher_kt) { - return EVP_CIPHER_block_size (cipher_kt); + if (cipher_kt_mode_aead(cipher_kt)) + return OPENVPN_AEAD_TAG_LENGTH; + else + return 0; } int @@ -565,6 +615,16 @@ cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) ; } +bool +cipher_kt_mode_aead(const cipher_kt_t *cipher) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM); +#else + return false; +#endif +} + /* * * Generic cipher context functions @@ -606,6 +666,15 @@ cipher_ctx_iv_length (const EVP_CIPHER_CTX *ctx) return EVP_CIPHER_CTX_iv_length (ctx); } +int cipher_ctx_get_tag (EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + return EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag_buf); +#else + ASSERT (0); +#endif +} + int cipher_ctx_block_size(const EVP_CIPHER_CTX *ctx) { @@ -621,7 +690,7 @@ cipher_ctx_mode (const EVP_CIPHER_CTX *ctx) const cipher_kt_t * cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) { - return EVP_CIPHER_CTX_cipher(ctx); + return ctx ? EVP_CIPHER_CTX_cipher(ctx) : NULL; } @@ -631,6 +700,19 @@ cipher_ctx_reset (EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) return EVP_CipherInit (ctx, NULL, NULL, iv_buf, -1); } +int +cipher_ctx_update_ad (EVP_CIPHER_CTX *ctx, const uint8_t *src, int src_len) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + int len; + if (!EVP_CipherUpdate (ctx, NULL, &len, src, src_len)) + crypto_msg(M_FATAL, "%s: EVP_CipherUpdate() failed", __func__); + return 1; +#else + ASSERT (0); +#endif +} + int cipher_ctx_update (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, uint8_t *src, int src_len) @@ -646,6 +728,20 @@ cipher_ctx_final (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len) return EVP_CipherFinal (ctx, dst, dst_len); } +int +cipher_ctx_final_check_tag (EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, + uint8_t *tag, size_t tag_len) +{ +#ifdef HAVE_AEAD_CIPHER_MODES + ASSERT (tag_len < SIZE_MAX); + if (!EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) + return 0; + + return cipher_ctx_final (ctx, dst, dst_len); +#else + ASSERT (0); +#endif +} void cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH], diff --git a/src/openvpn/crypto_openssl.h b/src/openvpn/crypto_openssl.h index 42c7e9a..f157041 100644 --- a/src/openvpn/crypto_openssl.h +++ b/src/openvpn/crypto_openssl.h @@ -61,6 +61,13 @@ typedef HMAC_CTX hmac_ctx_t; /** Cipher is in CFB mode */ #define OPENVPN_MODE_CFB EVP_CIPH_CFB_MODE +#ifdef HAVE_AEAD_CIPHER_MODES + +/** Cipher is in GCM mode */ +#define OPENVPN_MODE_GCM EVP_CIPH_GCM_MODE + +#endif /* HAVE_AEAD_CIPHER_MODES */ + /** Cipher should encrypt */ #define OPENVPN_OP_ENCRYPT 1 diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c deleted file mode 100644 index 92fdb78..0000000 --- a/src/openvpn/crypto_polarssl.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file Data Channel Cryptography PolarSSL-specific backend interface - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_POLARSSL) - -#include "errlevel.h" -#include "basic.h" -#include "buffer.h" -#include "integer.h" -#include "crypto_backend.h" -#include "otime.h" -#include "misc.h" - -#include -#include -#include -#include -#include - -#include - -/* - * - * Hardware engine support. Allows loading/unloading of engines. - * - */ - -void -crypto_init_lib_engine (const char *engine_name) -{ - msg (M_WARN, "Note: PolarSSL hardware crypto engine functionality is not " - "available"); -} - -/* - * - * Functions related to the core crypto library - * - */ - -void -crypto_init_lib (void) -{ -} - -void -crypto_uninit_lib (void) -{ -} - -void -crypto_clear_error (void) -{ -} - -bool polar_log_err(unsigned int flags, int errval, const char *prefix) -{ - if (0 != errval) - { - char errstr[256]; - polarssl_strerror(errval, errstr, sizeof(errstr)); - - if (NULL == prefix) prefix = "PolarSSL error"; - msg (flags, "%s: %s", prefix, errstr); - } - - return 0 == errval; -} - -bool polar_log_func_line(unsigned int flags, int errval, const char *func, - int line) -{ - char prefix[256]; - - if (!openvpn_snprintf(prefix, sizeof(prefix), "%s:%d", func, line)) - return polar_log_err(flags, errval, func); - - return polar_log_err(flags, errval, prefix); -} - - -#ifdef DMALLOC -void -crypto_init_dmalloc (void) -{ - msg (M_ERR, "Error: dmalloc support is not available for PolarSSL."); -} -#endif /* DMALLOC */ - -typedef struct { const char * openvpn_name; const char * polarssl_name; } cipher_name_pair; -cipher_name_pair cipher_name_translation_table[] = { - { "BF-CBC", "BLOWFISH-CBC" }, - { "BF-CFB", "BLOWFISH-CFB64" }, - { "CAMELLIA-128-CFB", "CAMELLIA-128-CFB128" }, - { "CAMELLIA-192-CFB", "CAMELLIA-192-CFB128" }, - { "CAMELLIA-256-CFB", "CAMELLIA-256-CFB128" } -}; - -const cipher_name_pair * -get_cipher_name_pair(const char *cipher_name) { - cipher_name_pair *pair; - size_t i = 0; - - /* Search for a cipher name translation */ - for (; i < sizeof (cipher_name_translation_table) / sizeof (*cipher_name_translation_table); i++) - { - pair = &cipher_name_translation_table[i]; - if (0 == strcmp (cipher_name, pair->openvpn_name) || - 0 == strcmp (cipher_name, pair->polarssl_name)) - return pair; - } - - /* Nothing found, return null */ - return NULL; -} - -const char * -translate_cipher_name_from_openvpn (const char *cipher_name) { - const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); - - if (NULL == pair) - return cipher_name; - - return pair->polarssl_name; -} - -const char * -translate_cipher_name_to_openvpn (const char *cipher_name) { - const cipher_name_pair *pair = get_cipher_name_pair(cipher_name); - - if (NULL == pair) - return cipher_name; - - return pair->openvpn_name; -} - -void -show_available_ciphers () -{ - const int *ciphers = cipher_list(); - -#ifndef ENABLE_SMALL - printf ("The following ciphers and cipher modes are available\n" - "for use with " PACKAGE_NAME ". Each cipher shown below may be\n" - "used as a parameter to the --cipher option. The default\n" - "key size is shown as well as whether or not it can be\n" - "changed with the --keysize directive. Using a CBC mode\n" - "is recommended.\n\n"); -#endif - - while (*ciphers != 0) - { - const cipher_info_t *info = cipher_info_from_type(*ciphers); - - if (info && info->mode == POLARSSL_MODE_CBC) - printf ("%s %d bit default key\n", - cipher_kt_name(info), cipher_kt_key_size(info) * 8); - - ciphers++; - } - printf ("\n"); -} - -void -show_available_digests () -{ - const int *digests = md_list(); - -#ifndef ENABLE_SMALL - printf ("The following message digests are available for use with\n" - PACKAGE_NAME ". A message digest is used in conjunction with\n" - "the HMAC function, to authenticate received packets.\n" - "You can specify a message digest as parameter to\n" - "the --auth option.\n\n"); -#endif - - while (*digests != 0) - { - const md_info_t *info = md_info_from_type(*digests); - - if (info) - printf ("%s %d bit default key\n", - info->name, info->size * 8); - digests++; - } - printf ("\n"); -} - -void -show_available_engines () -{ - printf ("Sorry, PolarSSL hardware crypto engine functionality is not " - "available\n"); -} - -/* - * - * Random number functions, used in cases where we want - * reasonably strong cryptographic random number generation - * without depleting our entropy pool. Used for random - * IV values and a number of other miscellaneous tasks. - * - */ - -/* - * Initialise the given ctr_drbg context, using a personalisation string and an - * entropy gathering function. - */ -ctr_drbg_context * rand_ctx_get() -{ - static entropy_context ec = {0}; - static ctr_drbg_context cd_ctx = {0}; - static bool rand_initialised = false; - - if (!rand_initialised) - { - struct gc_arena gc = gc_new(); - struct buffer pers_string = alloc_buf_gc(100, &gc); - - /* - * Personalisation string, should be as unique as possible (see NIST - * 800-90 section 8.7.1). We have very little information at this stage. - * Include Program Name, memory address of the context and PID. - */ - buf_printf(&pers_string, "OpenVPN %0u %p %s", platform_getpid(), &cd_ctx, time_string(0, 0, 0, &gc)); - - /* Initialise PolarSSL RNG, and built-in entropy sources */ - entropy_init(&ec); - - if (!polar_ok(ctr_drbg_init(&cd_ctx, entropy_func, &ec, - BPTR(&pers_string), BLEN(&pers_string)))) - msg (M_FATAL, "Failed to initialize random generator"); - - gc_free(&gc); - rand_initialised = true; - } - - return &cd_ctx; -} - -#ifdef ENABLE_PREDICTION_RESISTANCE -void rand_ctx_enable_prediction_resistance() -{ - ctr_drbg_context *cd_ctx = rand_ctx_get(); - - ctr_drbg_set_prediction_resistance(cd_ctx, 1); -} -#endif /* ENABLE_PREDICTION_RESISTANCE */ - -int -rand_bytes (uint8_t *output, int len) -{ - ctr_drbg_context *rng_ctx = rand_ctx_get(); - - while (len > 0) - { - const size_t blen = min_int (len, CTR_DRBG_MAX_REQUEST); - if (0 != ctr_drbg_random(rng_ctx, output, blen)) - return 0; - - output += blen; - len -= blen; - } - - return 1; -} - -/* - * - * Key functions, allow manipulation of keys. - * - */ - - -int -key_des_num_cblocks (const cipher_info_t *kt) -{ - int ret = 0; - if (kt->type == POLARSSL_CIPHER_DES_CBC) - ret = 1; - if (kt->type == POLARSSL_CIPHER_DES_EDE_CBC) - ret = 2; - if (kt->type == POLARSSL_CIPHER_DES_EDE3_CBC) - ret = 3; - - dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret); - return ret; -} - -bool -key_des_check (uint8_t *key, int key_len, int ndc) -{ - int i; - struct buffer b; - - buf_set_read (&b, key, key_len); - - for (i = 0; i < ndc; ++i) - { - unsigned char *key = buf_read_alloc(&b, DES_KEY_SIZE); - if (!key) - { - msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: insufficient key material"); - goto err; - } - if (0 != des_key_check_weak(key)) - { - msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: weak key detected"); - goto err; - } - if (0 != des_key_check_key_parity(key)) - { - msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: bad parity detected"); - goto err; - } - } - return true; - - err: - return false; -} - -void -key_des_fixup (uint8_t *key, int key_len, int ndc) -{ - int i; - struct buffer b; - - buf_set_read (&b, key, key_len); - for (i = 0; i < ndc; ++i) - { - unsigned char *key = buf_read_alloc(&b, DES_KEY_SIZE); - if (!key) - { - msg (D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material"); - return; - } - des_key_set_parity(key); - } -} - -/* - * - * Generic cipher key type functions - * - */ - - -const cipher_info_t * -cipher_kt_get (const char *ciphername) -{ - const cipher_info_t *cipher = NULL; - - ASSERT (ciphername); - - cipher = cipher_info_from_string(ciphername); - - if (NULL == cipher) - msg (M_FATAL, "Cipher algorithm '%s' not found", ciphername); - - if (cipher->key_length/8 > MAX_CIPHER_KEY_LENGTH) - msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)", - ciphername, - cipher->key_length/8, - MAX_CIPHER_KEY_LENGTH); - - return cipher; -} - -const char * -cipher_kt_name (const cipher_info_t *cipher_kt) -{ - if (NULL == cipher_kt) - return "[null-cipher]"; - - return translate_cipher_name_to_openvpn(cipher_kt->name); -} - -int -cipher_kt_key_size (const cipher_info_t *cipher_kt) -{ - if (NULL == cipher_kt) - return 0; - if (POLARSSL_CIPHER_ID_BLOWFISH == cipher_kt->base->cipher) - return 128/8; /* Override PolarSSL 32 bit default key size with sane 128 bit default */ - - return cipher_kt->key_length/8; -} - -int -cipher_kt_iv_size (const cipher_info_t *cipher_kt) -{ - if (NULL == cipher_kt) - return 0; - return cipher_kt->iv_size; -} - -int -cipher_kt_block_size (const cipher_info_t *cipher_kt) -{ - if (NULL == cipher_kt) - return 0; - return cipher_kt->block_size; -} - -int -cipher_kt_mode (const cipher_info_t *cipher_kt) -{ - ASSERT(NULL != cipher_kt); - return cipher_kt->mode; -} - -bool -cipher_kt_mode_cbc(const cipher_kt_t *cipher) -{ - return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; -} - -bool -cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) -{ - return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || - cipher_kt_mode(cipher) == OPENVPN_MODE_CFB); -} - - -/* - * - * Generic cipher context functions - * - */ - - -void -cipher_ctx_init (cipher_context_t *ctx, uint8_t *key, int key_len, - const cipher_info_t *kt, int enc) -{ - ASSERT(NULL != kt && NULL != ctx); - - CLEAR (*ctx); - - if (!polar_ok(cipher_init_ctx(ctx, kt))) - msg (M_FATAL, "PolarSSL cipher context init #1"); - - if (!polar_ok(cipher_setkey(ctx, key, key_len*8, enc))) - msg (M_FATAL, "PolarSSL cipher set key"); - - /* make sure we used a big enough key */ - ASSERT (ctx->key_length <= key_len*8); -} - -void cipher_ctx_cleanup (cipher_context_t *ctx) -{ - cipher_free(ctx); -} - -int cipher_ctx_iv_length (const cipher_context_t *ctx) -{ - return cipher_get_iv_size(ctx); -} - -int cipher_ctx_block_size(const cipher_context_t *ctx) -{ - return cipher_get_block_size(ctx); -} - -int cipher_ctx_mode (const cipher_context_t *ctx) -{ - ASSERT(NULL != ctx); - - return cipher_kt_mode(ctx->cipher_info); -} - -const cipher_kt_t * -cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx) -{ - ASSERT(NULL != ctx); - - return ctx->cipher_info; -} - -int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf) -{ - if (!polar_ok(cipher_reset(ctx))) - return 0; - - if (!polar_ok(cipher_set_iv(ctx, iv_buf, ctx->cipher_info->iv_size))) - return 0; - - return 1; -} - -int cipher_ctx_update (cipher_context_t *ctx, uint8_t *dst, int *dst_len, - uint8_t *src, int src_len) -{ - size_t s_dst_len = *dst_len; - - if (!polar_ok(cipher_update(ctx, src, (size_t)src_len, dst, &s_dst_len))) - return 0; - - *dst_len = s_dst_len; - - return 1; -} - -int cipher_ctx_final (cipher_context_t *ctx, uint8_t *dst, int *dst_len) -{ - size_t s_dst_len = *dst_len; - - if (!polar_ok(cipher_finish(ctx, dst, &s_dst_len))) - return 0; - - *dst_len = s_dst_len; - - return 1; -} - -void -cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH], - unsigned char *src, - unsigned char *dst) -{ - des_context ctx; - - ASSERT (polar_ok(des_setkey_enc(&ctx, key))); - ASSERT (polar_ok(des_crypt_ecb(&ctx, src, dst))); -} - - - -/* - * - * Generic message digest information functions - * - */ - - -const md_info_t * -md_kt_get (const char *digest) -{ - const md_info_t *md = NULL; - ASSERT (digest); - - md = md_info_from_string(digest); - if (!md) - msg (M_FATAL, "Message hash algorithm '%s' not found", digest); - if (md->size > MAX_HMAC_KEY_LENGTH) - msg (M_FATAL, "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum hash size (%d bytes)", - digest, - md->size, - MAX_HMAC_KEY_LENGTH); - return md; -} - -const char * -md_kt_name (const md_info_t *kt) -{ - if (NULL == kt) - return "[null-digest]"; - return md_get_name (kt); -} - -int -md_kt_size (const md_info_t *kt) -{ - if (NULL == kt) - return 0; - return md_get_size(kt); -} - -/* - * - * Generic message digest functions - * - */ - -int -md_full (const md_kt_t *kt, const uint8_t *src, int src_len, uint8_t *dst) -{ - return 0 == md(kt, src, src_len, dst); -} - - -void -md_ctx_init (md_context_t *ctx, const md_info_t *kt) -{ - ASSERT(NULL != ctx && NULL != kt); - - CLEAR(*ctx); - - ASSERT(0 == md_init_ctx(ctx, kt)); - ASSERT(0 == md_starts(ctx)); -} - -void -md_ctx_cleanup(md_context_t *ctx) -{ -} - -int -md_ctx_size (const md_context_t *ctx) -{ - if (NULL == ctx) - return 0; - return md_get_size(ctx->md_info); -} - -void -md_ctx_update (md_context_t *ctx, const uint8_t *src, int src_len) -{ - ASSERT(0 == md_update(ctx, src, src_len)); -} - -void -md_ctx_final (md_context_t *ctx, uint8_t *dst) -{ - ASSERT(0 == md_finish(ctx, dst)); - md_free(ctx); -} - - -/* - * - * Generic HMAC functions - * - */ - - -/* - * TODO: re-enable dmsg for crypto debug - */ -void -hmac_ctx_init (md_context_t *ctx, const uint8_t *key, int key_len, const md_info_t *kt) -{ - ASSERT(NULL != kt && NULL != ctx); - - CLEAR(*ctx); - - ASSERT(0 == md_init_ctx(ctx, kt)); - ASSERT(0 == md_hmac_starts(ctx, key, key_len)); - - /* make sure we used a big enough key */ - ASSERT (md_get_size(kt) <= key_len); -} - -void -hmac_ctx_cleanup(md_context_t *ctx) -{ - md_free(ctx); -} - -int -hmac_ctx_size (const md_context_t *ctx) -{ - if (NULL == ctx) - return 0; - return md_get_size(ctx->md_info); -} - -void -hmac_ctx_reset (md_context_t *ctx) -{ - ASSERT(0 == md_hmac_reset(ctx)); -} - -void -hmac_ctx_update (md_context_t *ctx, const uint8_t *src, int src_len) -{ - ASSERT(0 == md_hmac_update(ctx, src, src_len)); -} - -void -hmac_ctx_final (md_context_t *ctx, uint8_t *dst) -{ - ASSERT(0 == md_hmac_finish(ctx, dst)); -} - -#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_POLARSSL */ diff --git a/src/openvpn/crypto_polarssl.h b/src/openvpn/crypto_polarssl.h deleted file mode 100644 index 12b5146..0000000 --- a/src/openvpn/crypto_polarssl.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file Data Channel Cryptography PolarSSL-specific backend interface - */ - -#ifndef CRYPTO_POLARSSL_H_ -#define CRYPTO_POLARSSL_H_ - -#include -#include -#include - -/** Generic cipher key type %context. */ -typedef cipher_info_t cipher_kt_t; - -/** Generic message digest key type %context. */ -typedef md_info_t md_kt_t; - -/** Generic cipher %context. */ -typedef cipher_context_t cipher_ctx_t; - -/** Generic message digest %context. */ -typedef md_context_t md_ctx_t; - -/** Generic HMAC %context. */ -typedef md_context_t hmac_ctx_t; - -/** Maximum length of an IV */ -#define OPENVPN_MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH - -/** Cipher is in CBC mode */ -#define OPENVPN_MODE_CBC POLARSSL_MODE_CBC - -/** Cipher is in OFB mode */ -#define OPENVPN_MODE_OFB POLARSSL_MODE_OFB - -/** Cipher is in CFB mode */ -#define OPENVPN_MODE_CFB POLARSSL_MODE_CFB - -/** Cipher should encrypt */ -#define OPENVPN_OP_ENCRYPT POLARSSL_ENCRYPT - -/** Cipher should decrypt */ -#define OPENVPN_OP_DECRYPT POLARSSL_DECRYPT - -#define MD4_DIGEST_LENGTH 16 -#define MD5_DIGEST_LENGTH 16 -#define SHA_DIGEST_LENGTH 20 -#define DES_KEY_LENGTH 8 - -/** - * Returns a singleton instance of the PolarSSL random number generator. - * - * For PolarSSL 1.1+, this is the CTR_DRBG random number generator. If it - * hasn't been initialised yet, the RNG will be initialised using the default - * entropy sources. Aside from the default platform entropy sources, an - * additional entropy source, the HAVEGE random number generator will also be - * added. During initialisation, a personalisation string will be added based - * on the time, the PID, and a pointer to the random context. - */ -ctr_drbg_context * rand_ctx_get(); - -#ifdef ENABLE_PREDICTION_RESISTANCE -/** - * Enable prediction resistance on the random number generator. - */ -void rand_ctx_enable_prediction_resistance(); -#endif - -/** - * Log the supplied PolarSSL error, prefixed by supplied prefix. - * - * @param flags Flags to indicate error type and priority. - * @param errval PolarSSL error code to convert to error message. - * @param prefix Prefix to PolarSSL error message. - * - * @returns true if no errors are detected, false otherwise. - */ -bool polar_log_err(unsigned int flags, int errval, const char *prefix); - -/** - * Log the supplied PolarSSL error, prefixed by function name and line number. - * - * @param flags Flags to indicate error type and priority. - * @param errval PolarSSL error code to convert to error message. - * @param func Function name where error was reported. - * @param line Line number where error was reported. - * - * @returns true if no errors are detected, false otherwise. - */ -bool polar_log_func_line(unsigned int flags, int errval, const char *func, - int line); - -/** Wraps polar_log_func_line() to prevent function calls for non-errors */ -static inline bool polar_log_func_line_lite(unsigned int flags, int errval, - const char *func, int line) { - if (errval) { - return polar_log_func_line (flags, errval, func, line); - } - return true; -} - -/** - * Check errval and log on error. - * - * Convenience wrapper to put around polarssl library calls, e.g. - * if (!polar_ok(polarssl_func())) return 0; - * or - * ASSERT (polar_ok(polarssl_func())); - * - * @param errval PolarSSL error code to convert to error message. - * - * @returns true if no errors are detected, false otherwise. - */ -#define polar_ok(errval) \ - polar_log_func_line_lite (D_CRYPT_ERRORS, errval, __func__, __LINE__) - - -#endif /* CRYPTO_POLARSSL_H_ */ diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 853c07b..e107bd3 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -465,4 +465,4 @@ int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) #ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */ static void dummy (void) {} #endif -#endif /* WIN32 */ +#endif /* _WIN32 */ diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h index 3ee4ebc..9d56eb4 100644 --- a/src/openvpn/errlevel.h +++ b/src/openvpn/errlevel.h @@ -105,7 +105,6 @@ #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_LOW LOGLEV(4, 63, 0) /* show low-freq packet-id debugging info */ #define D_PID_DEBUG_MEDIUM LOGLEV(4, 64, 0) /* show medium-freq packet-id debugging info */ @@ -143,11 +142,12 @@ #define D_PS_PROXY_DEBUG LOGLEV(7, 70, M_DEBUG) /* port share proxy debug */ #define D_AUTO_USERID LOGLEV(7, 70, M_DEBUG) /* AUTO_USERID debugging */ #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_ARGV_PARSE_CMD LOGLEV(7, 70, M_DEBUG) /* show parse_line() errors in argv_parse_cmd */ #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 */ +#define D_PUSH_DEBUG LOGLEV(7, 73, M_DEBUG) /* show push/pull debugging info */ #define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */ #define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */ diff --git a/src/openvpn/error.c b/src/openvpn/error.c index 6ccdeae..425bc30 100644 --- a/src/openvpn/error.c +++ b/src/openvpn/error.c @@ -75,6 +75,10 @@ static bool std_redir; /* GLOBAL */ /* Should messages be written to the syslog? */ static bool use_syslog; /* GLOBAL */ +/* Should stdout/stderr be be parsable and always be prefixed with time + * and message flags */ +static bool machine_readable_output; /* GLOBAL */ + /* Should timestamps be included on messages to stdout/stderr? */ static bool suppress_timestamps; /* GLOBAL */ @@ -147,11 +151,18 @@ set_suppress_timestamps (bool suppressed) suppress_timestamps = suppressed; } +void +set_machine_readable_output (bool parsable) +{ + machine_readable_output = parsable; +} + void error_reset () { use_syslog = std_redir = false; suppress_timestamps = false; + machine_readable_output = false; x_debug_level = 1; mute_cutoff = 0; mute_count = 0; @@ -301,7 +312,22 @@ void x_msg_va (const unsigned int flags, const char *format, va_list arglist) FILE *fp = msg_fp(flags); const bool show_usec = check_debug_level (DEBUG_LEVEL_USEC_TIME); - if ((flags & M_NOPREFIX) || suppress_timestamps) + if (machine_readable_output) + { + struct timeval tv; + gettimeofday (&tv, NULL); + + fprintf (fp, "%lu.%06lu %x %s%s%s%s", + tv.tv_sec, + (unsigned long)tv.tv_usec, + flags, + prefix, + prefix_sep, + m1, + "\n"); + + } + else if ((flags & M_NOPREFIX) || suppress_timestamps) { fprintf (fp, "%s%s%s%s", prefix, @@ -427,7 +453,7 @@ close_syslog () #endif } -#ifdef WIN32 +#ifdef _WIN32 static HANDLE orig_stderr; @@ -445,7 +471,7 @@ get_orig_stderr (void) void redirect_stdout_stderr (const char *file, bool append) { -#if defined(WIN32) +#if defined(_WIN32) if (!std_redir) { struct gc_arena gc = gc_new (); @@ -577,7 +603,7 @@ x_check_status (int status, const char *extended_msg = NULL; msg (x_cs_verbose_level, "%s %s returned %d", - sock ? proto2ascii (sock->info.proto, true) : "", + sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "", description, status); @@ -596,7 +622,7 @@ x_check_status (int status, sock->info.mtu_changed = true; } } -#elif defined(WIN32) +#elif defined(_WIN32) /* get possible driver error from TAP-Windows driver */ extended_msg = tap_win_getinfo (tt, &gc); #endif @@ -605,14 +631,14 @@ x_check_status (int status, if (extended_msg) msg (x_cs_info_level, "%s %s [%s]: %s (code=%d)", description, - sock ? proto2ascii (sock->info.proto, true) : "", + sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "", extended_msg, strerror_ts (my_errno, &gc), my_errno); else msg (x_cs_info_level, "%s %s: %s (code=%d)", description, - sock ? proto2ascii (sock->info.proto, true) : "", + sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "", strerror_ts (my_errno, &gc), my_errno); @@ -651,7 +677,7 @@ openvpn_exit (const int status) tun_abort(); -#ifdef WIN32 +#ifdef _WIN32 uninit_win32 (); #endif @@ -711,7 +737,7 @@ crash (void) } #endif -#ifdef WIN32 +#ifdef _WIN32 const char * strerror_win32 (DWORD errnum, struct gc_arena *gc) diff --git a/src/openvpn/error.h b/src/openvpn/error.h index 4024e5e..f43bc38 100644 --- a/src/openvpn/error.h +++ b/src/openvpn/error.h @@ -27,6 +27,11 @@ #include "basic.h" +#include +#include + +#include + /* #define ABORT_ON_ERROR */ #ifdef ENABLE_PKCS11 @@ -66,7 +71,7 @@ struct gc_arena; /* String and Error functions */ -#ifdef WIN32 +#ifdef _WIN32 # define openvpn_errno() GetLastError() # define openvpn_strerror(e, gc) strerror_win32(e, gc) const char *strerror_win32 (DWORD errnum, struct gc_arena *gc); @@ -138,12 +143,6 @@ extern int x_msg_line_num; /** Check muting filter */ bool dont_mute (unsigned int flags); -/** Return true if flags represent an enabled, not muted log level */ -static inline bool msg_test (unsigned int flags) -{ - return ((flags & M_DEBUG_LEVEL) <= x_debug_level) && dont_mute (flags); -} - /* Macro to ensure (and teach static analysis tools) we exit on fatal errors */ #define EXIT_FATAL(flags) do { if ((flags) & M_FATAL) _exit(1); } while (false) @@ -197,6 +196,8 @@ void error_reset (void); void errors_to_stderr (void); void set_suppress_timestamps (bool suppressed); +void set_machine_readable_output (bool parsable); + #define SDL_CONSTRAIN (1<<0) bool set_debug_level (const int level, const unsigned int flags); @@ -223,6 +224,14 @@ FILE *msg_fp(const unsigned int flags); void assert_failed (const char *filename, int line, const char *condition) __attribute__((__noreturn__)); +/* Poor-man's static_assert() for when not supplied by assert.h, taken from + * Linux's sys/cdefs.h under GPLv2 */ +#ifndef static_assert +#define static_assert(expr, diagnostic) \ + extern int (*__OpenVPN_static_assert_function (void)) \ + [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })] +#endif + #ifdef ENABLE_DEBUG void crash (void); /* force a segfault (debugging only) */ #endif @@ -235,6 +244,12 @@ check_debug_level (unsigned int level) return (level & M_DEBUG_LEVEL) <= x_debug_level; } +/** Return true if flags represent an enabled, not muted log level */ +static inline bool msg_test (unsigned int flags) +{ + return check_debug_level (flags) && dont_mute (flags); +} + /* Call if we forked */ void msg_forked (void); @@ -246,7 +261,7 @@ void close_syslog (); /* log file output */ void redirect_stdout_stderr (const char *file, bool append); -#ifdef WIN32 +#ifdef _WIN32 /* get original stderr handle, even if redirected by --log/--log-append */ HANDLE get_orig_stderr (void); #endif @@ -341,7 +356,7 @@ static inline bool ignore_sys_error (const int err) { /* I/O operation pending */ -#ifdef WIN32 +#ifdef _WIN32 if (err == WSAEWOULDBLOCK || err == WSAEINVAL) return true; #else diff --git a/src/openvpn/event.c b/src/openvpn/event.c index c642691..409ad13 100644 --- a/src/openvpn/event.c +++ b/src/openvpn/event.c @@ -49,7 +49,7 @@ /* * All non-windows OSes are assumed to have select() */ -#ifdef WIN32 +#ifdef _WIN32 #define SELECT 0 #else #define SELECT 1 @@ -74,7 +74,7 @@ tv_to_ms_timeout (const struct timeval *tv) return max_int (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000, 1); } -#ifdef WIN32 +#ifdef _WIN32 struct we_set { @@ -462,7 +462,7 @@ we_init (int *maxevents, unsigned int flags) return (struct event_set *) wes; } -#endif /* WIN32 */ +#endif /* _WIN32 */ #if EPOLL @@ -1007,7 +1007,7 @@ static struct event_set * event_set_init_simple (int *maxevents, unsigned int flags) { struct event_set *ret = NULL; -#ifdef WIN32 +#ifdef _WIN32 ret = we_init (maxevents, flags); #elif POLL && SELECT #if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */ diff --git a/src/openvpn/event.h b/src/openvpn/event.h index bd29fdc..565343d 100644 --- a/src/openvpn/event.h +++ b/src/openvpn/event.h @@ -42,7 +42,7 @@ #define EVENT_METHOD_US_TIMEOUT (1<<0) #define EVENT_METHOD_FAST (1<<1) -#ifdef WIN32 +#ifdef _WIN32 typedef const struct rw_handle *event_t; @@ -137,7 +137,7 @@ event_set_return_init (struct event_set_return *esr) esr->arg = NULL; } -#ifdef WIN32 +#ifdef _WIN32 static inline void wait_signal (struct event_set *es, void *arg) diff --git a/src/openvpn/fdmisc.c b/src/openvpn/fdmisc.c index 7fe449c..ce01319 100644 --- a/src/openvpn/fdmisc.c +++ b/src/openvpn/fdmisc.c @@ -39,7 +39,7 @@ bool set_nonblock_action (int fd) { -#ifdef WIN32 +#ifdef _WIN32 u_long arg = 1; if (ioctlsocket (fd, FIONBIO, &arg)) return false; @@ -54,7 +54,7 @@ set_nonblock_action (int fd) bool set_cloexec_action (int fd) { -#ifndef WIN32 +#ifndef _WIN32 if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) return false; #endif diff --git a/src/openvpn/fdmisc.h b/src/openvpn/fdmisc.h index 13d6552..d34db9b 100644 --- a/src/openvpn/fdmisc.h +++ b/src/openvpn/fdmisc.h @@ -37,7 +37,7 @@ void set_cloexec (int fd); static inline void openvpn_fd_set(int fd, fd_set *setp) { -#ifndef WIN32 /* The Windows FD_SET() implementation does not overflow */ +#ifndef _WIN32 /* The Windows FD_SET() implementation does not overflow */ ASSERT (fd >= 0 && fd < FD_SETSIZE); #endif FD_SET (fd, setp); diff --git a/src/openvpn/forward-inline.h b/src/openvpn/forward-inline.h index 5853ce2..5d4e308 100644 --- a/src/openvpn/forward-inline.h +++ b/src/openvpn/forward-inline.h @@ -35,7 +35,7 @@ static inline void check_tls (struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(ENABLE_CRYPTO) void check_tls_dowork (struct context *c); if (c->c2.tls_multi) check_tls_dowork (c); @@ -49,7 +49,7 @@ check_tls (struct context *c) static inline void check_tls_errors (struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(ENABLE_CRYPTO) void check_tls_errors_co (struct context *c); void check_tls_errors_nco (struct context *c); if (c->c2.tls_multi && c->c2.tls_exit_signal) @@ -125,7 +125,7 @@ check_server_poll_timeout (struct context *c) { void check_server_poll_timeout_dowork (struct context *c); - if (c->options.server_poll_timeout + if (c->options.ce.connect_timeout && event_timeout_trigger (&c->c2.server_poll_interval, &c->c2.timeval, ETT_DEFAULT)) check_server_poll_timeout_dowork (c); } diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index d55fa3b..b50a2e0 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -39,6 +39,7 @@ #include "ps.h" #include "dhcp.h" #include "common.h" +#include "ssl_verify.h" #include "memdbg.h" @@ -87,7 +88,7 @@ show_wait_status (struct context *c) * traffic on the control-channel. * */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO void check_tls_dowork (struct context *c) { @@ -116,9 +117,6 @@ check_tls_dowork (struct context *c) if (wakeup) context_reschedule_sec (c, wakeup); } -#endif - -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) void check_tls_errors_co (struct context *c) @@ -132,8 +130,7 @@ check_tls_errors_nco (struct context *c) { register_signal (c, c->c2.tls_exit_signal, "tls-error"); /* SOFT-SIGUSR1 -- TLS error */ } - -#endif +#endif /* ENABLE_CRYPTO */ #if P2MP @@ -211,12 +208,14 @@ check_connection_established_dowork (struct context *c) management_set_state (management, OPENVPN_STATE_GET_CONFIG, NULL, - 0, - 0); + NULL, + NULL, + NULL, + NULL); } #endif - /* send push request in 1 sec */ - event_timeout_init (&c->c2.push_request_interval, 1, now); + /* fire up push request right away (already 1s delayed) */ + event_timeout_init (&c->c2.push_request_interval, 0, now); reset_coarse_timers (c); } else @@ -238,7 +237,7 @@ check_connection_established_dowork (struct context *c) bool send_control_channel_string (struct context *c, const char *str, int msglevel) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { struct gc_arena gc = gc_new (); bool stat; @@ -263,7 +262,7 @@ send_control_channel_string (struct context *c, const char *str, int msglevel) gc_free (&gc); return stat; } -#endif +#endif /* ENABLE_CRYPTO */ return true; } @@ -302,7 +301,7 @@ check_add_routes_dowork (struct context *c) { register_signal (c, SIGHUP, "ip-fail"); c->persist.restart_sleep_seconds = 10; -#ifdef WIN32 +#ifdef _WIN32 show_routes (M_INFO|M_NOPREFIX); show_adapters (M_INFO|M_NOPREFIX); #endif @@ -325,6 +324,13 @@ check_inactivity_timeout_dowork (struct context *c) register_signal (c, SIGTERM, "inactive"); } +int +get_server_poll_remaining_time (struct event_timeout* server_poll_timeout) +{ + update_time(); + int remaining = event_timeout_remaining(server_poll_timeout); + return max_int (0, remaining); +} #if P2MP void @@ -385,7 +391,7 @@ check_fragment_dowork (struct context *c) struct link_socket_info *lsi = get_link_socket_info (c); /* OS MTU Hint? */ - if (lsi->mtu_changed && c->c2.ipv4_tun) + if (lsi->mtu_changed) { frame_adjust_path_mtu (&c->c2.frame_fragment, c->c2.link_socket->mtu, c->options.ce.proto); @@ -433,6 +439,7 @@ encrypt_sign (struct context *c, bool comp_frag) { struct context_buffers *b = c->c2.buffers; const uint8_t *orig_buf = c->c2.buf.data; + struct crypto_options *co = NULL; #if P2MP_SERVER /* @@ -445,10 +452,10 @@ encrypt_sign (struct context *c, bool comp_frag) if (comp_frag) { -#ifdef ENABLE_LZO +#ifdef USE_COMP /* Compress the packet. */ - if (lzo_defined (&c->c2.lzo_compwork)) - lzo_compress (&c->c2.buf, b->lzo_compress_buf, &c->c2.lzo_compwork, &c->c2.frame); + if (c->c2.comp_context) + (*c->c2.comp_context->alg.compress)(&c->c2.buf, b->compress_buf, c->c2.comp_context, &c->c2.frame); #endif #ifdef ENABLE_FRAGMENT if (c->c2.fragment) @@ -457,43 +464,41 @@ encrypt_sign (struct context *c, bool comp_frag) } #ifdef ENABLE_CRYPTO -#ifdef ENABLE_SSL - /* - * If TLS mode, get the key we will use to encrypt - * the packet. - */ + /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ + ASSERT (buf_init (&b->encrypt_buf, FRAME_HEADROOM (&c->c2.frame))); + if (c->c2.tls_multi) { - tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &c->c2.crypto_options); + /* Get the key we will use to encrypt the packet. */ + tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &co); + /* If using P_DATA_V2, prepend the 1-byte opcode and 3-byte peer-id to the + * packet before openvpn_encrypt(), so we can authenticate the opcode too. + */ + if (c->c2.buf.len > 0 && !c->c2.tls_multi->opt.server && c->c2.tls_multi->use_peer_id) + tls_prepend_opcode_v2 (c->c2.tls_multi, &b->encrypt_buf); + } + else + { + co = &c->c2.crypto_options; } -#endif - /* - * Encrypt the packet and write an optional - * HMAC signature. - */ - openvpn_encrypt (&c->c2.buf, b->encrypt_buf, &c->c2.crypto_options, &c->c2.frame); + /* Encrypt and authenticate the packet */ + openvpn_encrypt (&c->c2.buf, b->encrypt_buf, co); + + /* Do packet administration */ + if (c->c2.tls_multi) + { + if (c->c2.buf.len > 0 && (c->c2.tls_multi->opt.server || !c->c2.tls_multi->use_peer_id)) + tls_prepend_opcode_v1(c->c2.tls_multi, &c->c2.buf); + tls_post_encrypt (c->c2.tls_multi, &c->c2.buf); + } #endif + /* * Get the address we will be sending the packet to. */ link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c), &c->c2.to_link_addr); -#ifdef ENABLE_CRYPTO -#ifdef ENABLE_SSL - /* - * In TLS mode, prepend the appropriate one-byte opcode - * to the packet which identifies it as a data channel - * packet and gives the low-permutation version of - * the key-id to the recipient so it knows which - * decrypt key to use. - */ - if (c->c2.tls_multi) - { - tls_post_encrypt (c->c2.tls_multi, &c->c2.buf); - } -#endif -#endif /* if null encryption, copy result to read_tun_buf */ buffer_turnover (orig_buf, &c->c2.to_link, &c->c2.buf, &b->read_tun_buf); @@ -540,13 +545,16 @@ process_coarse_timers (struct context *c) return; #if P2MP - check_server_poll_timeout (c); - if (c->sig->signal_received) - return; + if (c->c2.tls_multi) + { + check_server_poll_timeout (c); + if (c->sig->signal_received) + return; - check_scheduled_exit (c); - if (c->sig->signal_received) - return; + check_scheduled_exit (c); + if (c->sig->signal_received) + return; + } #endif #ifdef ENABLE_OCC @@ -611,8 +619,6 @@ check_timeout_random_component (struct context *c) tv_add (&c->c2.timeval, &c->c2.timeout_random_component); } -#ifdef ENABLE_SOCKS - /* * Handle addition and removal of the 10-byte Socks5 header * in UDP packets. @@ -621,7 +627,7 @@ check_timeout_random_component (struct context *c) static inline void socks_postprocess_incoming_link (struct context *c) { - if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4) + if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDP) socks_process_incoming_udp (&c->c2.buf, &c->c2.from); } @@ -630,7 +636,7 @@ socks_preprocess_outgoing_link (struct context *c, struct link_socket_actual **to_addr, int *size_delta) { - if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4) + if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDP) { *size_delta += socks_process_outgoing_udp (&c->c2.to_link, c->c2.to_link_addr); *to_addr = &c->c2.link_socket->socks_relay; @@ -650,7 +656,6 @@ link_socket_write_post_size_adjust (int *size, *size = 0; } } -#endif /* * Output: c->c2.buf @@ -674,7 +679,6 @@ read_incoming_link (struct context *c) status = link_socket_read (c->c2.link_socket, &c->c2.buf, - MAX_RW_SIZE_LINK (&c->c2.frame), &c->c2.from); if (socket_connection_reset (c->c2.link_socket, status)) @@ -719,28 +723,17 @@ read_incoming_link (struct context *c) /* check recvfrom status */ check_status (status, "read", c->c2.link_socket, NULL); -#ifdef ENABLE_SOCKS /* Remove socks header if applicable */ socks_postprocess_incoming_link (c); -#endif perf_pop (); } -/* - * Input: c->c2.buf - * Output: c->c2.to_tun - */ - -void -process_incoming_link (struct context *c) +bool +process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated) { struct gc_arena gc = gc_new (); - bool decrypt_status; - struct link_socket_info *lsi = get_link_socket_info (c); - const uint8_t *orig_buf = c->c2.buf.data; - - perf_push (PERF_PROC_IN_LINK); + bool decrypt_status = false; if (c->c2.buf.len > 0) { @@ -779,7 +772,7 @@ process_incoming_link (struct context *c) fprintf (stderr, "R"); #endif msg (D_LINK_RW, "%s READ [%d] from %s: %s", - proto2ascii (lsi->proto, true), + proto2ascii (lsi->proto, lsi->af, true), BLEN (&c->c2.buf), print_link_socket_actual (&c->c2.from, &gc), PROTO_DUMP (&c->c2.buf, &gc)); @@ -793,11 +786,12 @@ process_incoming_link (struct context *c) */ if (c->c2.buf.len > 0) { + struct crypto_options *co = NULL; + const uint8_t *ad_start = NULL; if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from)) link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from); #ifdef ENABLE_CRYPTO -#ifdef ENABLE_SSL if (c->c2.tls_multi) { /* @@ -810,7 +804,8 @@ process_incoming_link (struct context *c) * will load crypto_options with the correct encryption key * and return false. */ - if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options)) + if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &co, + floated, &ad_start)) { interval_action (&c->c2.tmp_int); @@ -819,6 +814,10 @@ process_incoming_link (struct context *c) event_timeout_reset (&c->c2.ping_rec_interval); } } + else + { + co = &c->c2.crypto_options; + } #if P2MP_SERVER /* * Drop non-TLS packet if client-connect script/plugin has not @@ -827,30 +826,44 @@ process_incoming_link (struct context *c) if (c->c2.context_auth != CAS_SUCCEEDED) c->c2.buf.len = 0; #endif -#endif /* ENABLE_SSL */ /* authenticate and decrypt the incoming packet */ - decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame); + decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, + co, &c->c2.frame, ad_start); if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket)) { /* decryption errors are fatal in TCP mode */ register_signal (c, SIGUSR1, "decryption-error"); /* SOFT-SIGUSR1 -- decryption error in TCP mode */ msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); - goto done; } - +#else /* ENABLE_CRYPTO */ + decrypt_status = true; #endif /* ENABLE_CRYPTO */ + } + else + { + buf_reset (&c->c2.to_tun); + } + gc_free (&gc); + + return decrypt_status; +} +void +process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf) +{ + if (c->c2.buf.len > 0) + { #ifdef ENABLE_FRAGMENT if (c->c2.fragment) fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); #endif -#ifdef ENABLE_LZO +#ifdef USE_COMP /* decompress the incoming packet */ - if (lzo_defined (&c->c2.lzo_compwork)) - lzo_decompress (&c->c2.buf, c->c2.buffers->lzo_decompress_buf, &c->c2.lzo_compwork, &c->c2.frame); + if (c->c2.comp_context) + (*c->c2.comp_context->alg.decompress)(&c->c2.buf, c->c2.buffers->decompress_buf, c->c2.comp_context, &c->c2.frame); #endif #ifdef PACKET_TRUNCATION_CHECK @@ -908,9 +921,20 @@ process_incoming_link (struct context *c) { buf_reset (&c->c2.to_tun); } - done: +} + +void +process_incoming_link (struct context *c) +{ + perf_push (PERF_PROC_IN_LINK); + + struct link_socket_info *lsi = get_link_socket_info (c); + const uint8_t *orig_buf = c->c2.buf.data; + + process_incoming_link_part1(c, lsi, false); + process_incoming_link_part2(c, lsi, orig_buf); + perf_pop (); - gc_free (&gc); } /* @@ -969,6 +993,76 @@ read_incoming_tun (struct context *c) perf_pop (); } +/** + * Drops UDP packets which OS decided to route via tun. + * + * On Windows and OS X when netwotk adapter is disabled or + * disconnected, platform starts to use tun as external interface. + * When packet is sent to tun, it comes to openvpn, encapsulated + * and sent to routing table, which sends it again to tun. + */ +static void +drop_if_recursive_routing (struct context *c, struct buffer *buf) +{ + bool drop = false; + struct openvpn_sockaddr tun_sa; + int ip_hdr_offset = 0; + + if (c->c2.to_link_addr == NULL) /* no remote addr known */ + return; + + tun_sa = c->c2.to_link_addr->dest; + + int proto_ver = get_tun_ip_ver (TUNNEL_TYPE (c->c1.tuntap), &c->c2.buf, &ip_hdr_offset); + + if (proto_ver == 4) + { + const struct openvpn_iphdr *pip; + + /* make sure we got whole IP header */ + if (BLEN (buf) < ((int) sizeof (struct openvpn_iphdr) + ip_hdr_offset)) + return; + + /* skip ipv4 packets for ipv6 tun */ + if (tun_sa.addr.sa.sa_family != AF_INET) + return; + + pip = (struct openvpn_iphdr *) (BPTR (buf) + ip_hdr_offset); + + /* drop packets with same dest addr as gateway */ + if (tun_sa.addr.in4.sin_addr.s_addr == pip->daddr) + drop = true; + } + else if (proto_ver == 6) + { + const struct openvpn_ipv6hdr *pip6; + + /* make sure we got whole IPv6 header */ + if (BLEN (buf) < ((int) sizeof (struct openvpn_ipv6hdr) + ip_hdr_offset)) + return; + + /* skip ipv6 packets for ipv4 tun */ + if (tun_sa.addr.sa.sa_family != AF_INET6) + return; + + /* drop packets with same dest addr as gateway */ + pip6 = (struct openvpn_ipv6hdr *) (BPTR (buf) + ip_hdr_offset); + if (IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr)) + drop = true; + } + + if (drop) + { + struct gc_arena gc = gc_new (); + + c->c2.buf.len = 0; + + msg(D_LOW, "Recursive routing detected, drop tun packet to %s", + print_link_socket_actual(c->c2.to_link_addr, &gc)); + gc_free (&gc); + } +} + /* * Input: c->c2.buf * Output: c->c2.to_link @@ -994,6 +1088,8 @@ process_incoming_tun (struct context *c) if (c->c2.buf.len > 0) { + if ((c->options.mode == MODE_POINT_TO_POINT) && (!c->options.allow_recursive_routing)) + drop_if_recursive_routing (c, &c->c2.buf); /* * The --passtos and --mssfix options require * us to examine the IP header (IPv4 or IPv6). @@ -1028,6 +1124,8 @@ process_ip_header (struct context *c, unsigned int flags, struct buffer *buf) if (!c->options.passtos) flags &= ~PIPV4_PASSTOS; #endif + if (!c->options.client_nat) + flags &= ~PIPV4_CLIENT_NAT; if (!c->options.route_gateway_via_dhcp) flags &= ~PIPV4_EXTRACT_DHCP_ROUTER; @@ -1037,11 +1135,13 @@ process_ip_header (struct context *c, unsigned int flags, struct buffer *buf) * The --passtos and --mssfix options require * us to examine the IPv4 header. */ + + if (flags & (PIP_MSSFIX #if PASSTOS_CAPABILITY - if (flags & (PIPV4_PASSTOS|PIP_MSSFIX)) -#else - if (flags & PIP_MSSFIX) + | PIPV4_PASSTOS #endif + | PIPV4_CLIENT_NAT + )) { struct buffer ipbuf = *buf; if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf)) @@ -1056,14 +1156,12 @@ process_ip_header (struct context *c, unsigned int flags, struct buffer *buf) if (flags & PIP_MSSFIX) mss_fixup_ipv4 (&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) { @@ -1090,6 +1188,7 @@ void process_outgoing_link (struct context *c) { struct gc_arena gc = gc_new (); + int error_code = 0; perf_push (PERF_PROC_OUT_LINK); @@ -1133,7 +1232,7 @@ process_outgoing_link (struct context *c) fprintf (stderr, "W"); #endif msg (D_LINK_RW, "%s WRITE [%d] to %s: %s", - proto2ascii (c->c2.link_socket->info.proto, true), + proto2ascii (c->c2.link_socket->info.proto, c->c2.link_socket->info.af, true), BLEN (&c->c2.to_link), print_link_socket_actual (c->c2.to_link_addr, &gc), PROTO_DUMP (&c->c2.to_link, &gc)); @@ -1141,23 +1240,18 @@ process_outgoing_link (struct context *c) /* Packet send complexified by possible Socks5 usage */ { struct link_socket_actual *to_addr = c->c2.to_link_addr; -#ifdef ENABLE_SOCKS int size_delta = 0; -#endif -#ifdef ENABLE_SOCKS /* If Socks5 over UDP, prepend header */ socks_preprocess_outgoing_link (c, &to_addr, &size_delta); -#endif + /* Send packet */ size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr); -#ifdef ENABLE_SOCKS /* Undo effect of prepend */ link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link); -#endif } if (size > 0) @@ -1182,6 +1276,7 @@ process_outgoing_link (struct context *c) } /* Check return status */ + error_code = openvpn_errno(); check_status (size, "write", c->c2.link_socket, NULL); if (size > 0) @@ -1198,6 +1293,17 @@ process_outgoing_link (struct context *c) /* if not a ping/control message, indicate activity regarding --inactive parameter */ if (c->c2.buf.len > 0 ) register_activity (c, size); + + +#ifdef ENABLE_CRYPTO + /* for unreachable network and "connecting" state switch to the next host */ + if (size < 0 && ENETUNREACH == error_code && c->c2.tls_multi && + !tls_initial_packet_received (c->c2.tls_multi) && c->options.mode == MODE_POINT_TO_POINT) + { + msg (M_INFO, "Network unreachable, restarting"); + register_signal (c, SIGUSR1, "network-unreachable"); + } +#endif } else { @@ -1314,7 +1420,7 @@ pre_select (struct context *c) c->c2.timeval.tv_sec = BIG_TIMEOUT; c->c2.timeval.tv_usec = 0; -#if defined(WIN32) +#if defined(_WIN32) if (check_debug_level (D_TAP_WIN_DEBUG)) { c->c2.timeval.tv_sec = 1; @@ -1373,6 +1479,9 @@ io_wait_dowork (struct context *c, const unsigned int flags) #ifdef ENABLE_MANAGEMENT static int management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */ #endif +#ifdef ENABLE_ASYNC_PUSH + static int file_shift = 8; /* listening inotify events */ +#endif /* * Decide what kind of events we want to wait for. @@ -1467,6 +1576,11 @@ io_wait_dowork (struct context *c, const unsigned int flags) management_socket_set (management, c->c2.event_set, (void*)&management_shift, NULL); #endif +#ifdef ENABLE_ASYNC_PUSH + /* arm inotify watcher */ + event_ctl (c->c2.event_set, c->c2.inotify_fd, EVENT_READ, (void*)&file_shift); +#endif + /* * Possible scenarios: * (1) tcp/udp port has data available to read diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 1830a00..0856aa7 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -103,7 +103,7 @@ void show_wait_status (struct context *c); * once for each remaining fragment with this parameter set to false. */ void encrypt_sign (struct context *c, bool comp_frag); - +int get_server_poll_remaining_time (struct event_timeout* server_poll_timeout); /**********************************************************************/ /** @@ -127,12 +127,11 @@ void encrypt_sign (struct context *c, bool comp_frag); */ void read_incoming_link (struct context *c); - /** - * Process a packet read from the external network interface. + * Starts processing a packet read from the external network interface. * @ingroup external_multiplexer * - * This function controls the processing of a data channel packet which + * This function starts the processing of a data channel packet which * has come out of a VPN tunnel. It's high-level structure is as follows: * - Verify that a nonzero length packet has been received from a valid * source address for the given context \a c. @@ -146,6 +145,25 @@ void read_incoming_link (struct context *c); * - Call \c openvpn_decrypt() of the \link data_crypto Data Channel * Crypto module\endlink to authenticate and decrypt the packet using * the security parameters loaded by \c tls_pre_decrypt() above. + * + * @param c - The context structure of the VPN tunnel associated with the + * packet. + * @param lsi - link_socket_info obtained from context before processing. + * @param floated - Flag indicates that peer has floated. + * + * @return true if packet is authenticated, false otherwise. + */ +bool process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated); + +/** + * Continues processing a packet read from the external network interface. + * @ingroup external_multiplexer + * + * This function continues the processing of a data channel packet which + * has come out of a VPN tunnel. It must be called after + * \c process_incoming_link_part1() function. + * + * It's high-level structure is as follows: * - Call \c fragment_incoming() of the \link fragmentation Data Channel * Fragmentation module\endlink to reassemble the packet if it's * fragmented. @@ -158,9 +176,11 @@ void read_incoming_link (struct context *c); * * @param c - The context structure of the VPN tunnel associated with the * packet. + * @param lsi - link_socket_info obtained from context before processing. + * @param orig_buf - Pointer to a buffer data. + * */ -void process_incoming_link (struct context *c); - +void process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf); /** * Write a packet to the external network interface. diff --git a/src/openvpn/helper.c b/src/openvpn/helper.c index 62f88ec..229523d 100644 --- a/src/openvpn/helper.c +++ b/src/openvpn/helper.c @@ -200,8 +200,6 @@ helper_client_server (struct options *o) add_in6_addr( o->server_network_ipv6, 0x1000 ); o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6; - o->tun_ipv6 = true; - push_option( o, "tun-ipv6", M_USAGE ); } diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 2148777..470dc89 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -43,6 +43,9 @@ #include "lladdr.h" #include "ping.h" #include "mstats.h" +#include "ssl_verify.h" +#include "tls_crypt.h" +#include "forward-inline.h" #include "memdbg.h" @@ -125,42 +128,25 @@ management_callback_proxy_cmd (void *arg, const char **p) ret = true; else if (p[2] && p[3]) { - const int port = atoi(p[3]); - if (!legal_ipv4_port (port)) - { - msg (M_WARN, "Bad proxy port number: %s", p[3]); - return false; - } - if (streq (p[1], "HTTP")) { -#ifndef ENABLE_HTTP_PROXY - msg (M_WARN, "HTTP proxy support is not available"); -#else struct http_proxy_options *ho; - if (ce->proto != PROTO_TCPv4 && ce->proto != PROTO_TCPv4_CLIENT && - ce->proto != PROTO_TCPv6 && ce->proto != PROTO_TCPv6_CLIENT) + if (ce->proto != PROTO_TCP && ce->proto != PROTO_TCP_CLIENT ) { msg (M_WARN, "HTTP proxy support only works for TCP based connections"); return false; } ho = init_http_proxy_options_once (&ce->http_proxy_options, gc); ho->server = string_alloc (p[2], gc); - ho->port = port; - ho->retry = true; + ho->port = string_alloc (p[3], gc); ho->auth_retry = (p[4] && streq (p[4], "nct") ? PAR_NCT : PAR_ALL); ret = true; -#endif } else if (streq (p[1], "SOCKS")) { -#ifndef ENABLE_SOCKS - msg (M_WARN, "SOCKS proxy support is not available"); -#else ce->socks_proxy_server = string_alloc (p[2], gc); - ce->socks_proxy_port = port; + ce->socks_proxy_port = p[3]; ret = true; -#endif } } else @@ -227,8 +213,7 @@ management_callback_remote_cmd (void *arg, const char **p) } else if (!strcmp(p[1], "MOD") && p[2] && p[3]) { - const int port = atoi(p[3]); - if (strlen(p[2]) < RH_HOST_LEN && legal_ipv4_port(port)) + if (strlen(p[2]) < RH_HOST_LEN && strlen(p[3]) < RH_PORT_LEN) { struct remote_host_store *rhs = c->options.rh_store; if (!rhs) @@ -237,8 +222,10 @@ management_callback_remote_cmd (void *arg, const char **p) c->options.rh_store = rhs; } strncpynt(rhs->host, p[2], RH_HOST_LEN); + strncpynt(rhs->port, p[3], RH_PORT_LEN); + ce->remote = rhs->host; - ce->remote_port = port; + ce->remote_port = rhs->port; flags = CE_MAN_QUERY_REMOTE_MOD; ret = true; } @@ -253,7 +240,7 @@ management_callback_remote_cmd (void *arg, const char **p) } static bool -ce_management_query_remote (struct context *c, const char *remote_ip_hint) +ce_management_query_remote (struct context *c) { struct gc_arena gc = gc_new (); volatile struct connection_entry *ce = &c->options.ce; @@ -262,7 +249,7 @@ ce_management_query_remote (struct context *c, const char *remote_ip_hint) if (management) { struct buffer out = alloc_buf_gc (256, &gc); - buf_printf (&out, ">REMOTE:%s,%d,%s", np(ce->remote), ce->remote_port, proto2ascii(ce->proto, false)); + buf_printf (&out, ">REMOTE:%s,%s,%s", np(ce->remote), ce->remote_port, proto2ascii(ce->proto, ce->af, false)); management_notify_generic(management, BSTR (&out)); ce->flags &= ~(CE_MAN_QUERY_REMOTE_MASK<flags |= (CE_MAN_QUERY_REMOTE_QUERY<flags>>CE_MAN_QUERY_REMOTE_SHIFT) & CE_MAN_QUERY_REMOTE_MASK); - if (flags == CE_MAN_QUERY_REMOTE_ACCEPT && remote_ip_hint) - ce->remote = remote_ip_hint; ret = (flags != CE_MAN_QUERY_REMOTE_SKIP); } gc_free (&gc); @@ -294,27 +279,36 @@ static void init_connection_list (struct context *c) { struct connection_list *l = c->options.connection_list; - if (l) + + l->current = -1; + if (c->options.remote_random) { - l->current = -1; - if (c->options.remote_random) - { - int i; - for (i = 0; i < l->len; ++i) - { - const int j = get_random () % l->len; - if (i != j) - { - struct connection_entry *tmp; - tmp = l->array[i]; - l->array[i] = l->array[j]; - l->array[j] = tmp; - } - } - } + int i; + for (i = 0; i < l->len; ++i) + { + const int j = get_random () % l->len; + if (i != j) + { + struct connection_entry *tmp; + tmp = l->array[i]; + l->array[i] = l->array[j]; + l->array[j] = tmp; + } + } } } +/* + * Clear the remote address list + */ +static void clear_remote_addrlist (struct link_socket_addr *lsa, bool free) +{ + if (lsa->remote_list && free) + freeaddrinfo(lsa->remote_list); + lsa->remote_list = NULL; + lsa->current_remote = NULL; +} + /* * Increment to next connection entry */ @@ -322,67 +316,92 @@ static void next_connection_entry (struct context *c) { struct connection_list *l = c->options.connection_list; - if (l) - { - bool ce_defined; - struct connection_entry *ce; - int n_cycles = 0; - - do { - const char *remote_ip_hint = NULL; - bool newcycle = false; + bool ce_defined; + struct connection_entry *ce; + int n_cycles = 0; - ce_defined = true; - if (l->no_advance && l->current >= 0) - { - l->no_advance = false; - } - else - { - if (++l->current >= l->len) + do { + ce_defined = true; + if (c->options.no_advance && l->current >= 0) + { + c->options.no_advance = false; + } + else + { + /* Check if there is another resolved address to try for + * the current connection */ + if (c->c1.link_socket_addr.current_remote && + c->c1.link_socket_addr.current_remote->ai_next) + { + c->c1.link_socket_addr.current_remote = + c->c1.link_socket_addr.current_remote->ai_next; + } + else + { + /* FIXME (schwabe) fix the persist-remote-ip option for real, + * this is broken probably ever since connection lists and multiple + * remote existed + */ + if (!c->options.persist_remote_ip) { - l->current = 0; - ++l->n_cycles; - if (++n_cycles >= 2) - msg (M_FATAL, "No usable connection profiles are present"); + /* close_instance should have cleared the addrinfo objects */ + ASSERT (c->c1.link_socket_addr.current_remote == NULL); + ASSERT (c->c1.link_socket_addr.remote_list == NULL); } + else + c->c1.link_socket_addr.current_remote = + c->c1.link_socket_addr.remote_list; - if (l->current == 0) - newcycle = true; - } + /* + * Increase the number of connection attempts + * If this is connect-retry-max * size(l) + * OpenVPN will quit + */ - ce = l->array[l->current]; + c->options.unsuccessful_attempts++; - if (c->options.remote_ip_hint && !l->n_cycles) - remote_ip_hint = c->options.remote_ip_hint; + if (++l->current >= l->len) + { - if (ce->flags & CE_DISABLED) - ce_defined = false; + l->current = 0; + if (++n_cycles >= 2) + msg (M_FATAL, "No usable connection profiles are present"); + } + } + } + + ce = l->array[l->current]; - c->options.ce = *ce; + if (ce->flags & CE_DISABLED) + ce_defined = false; + + c->options.ce = *ce; #ifdef ENABLE_MANAGEMENT - if (ce_defined && management && management_query_remote_enabled(management)) - { - /* allow management interface to override connection entry details */ - ce_defined = ce_management_query_remote(c, remote_ip_hint); - if (IS_SIG (c)) - break; - } - else + if (ce_defined && management && management_query_remote_enabled(management)) + { + /* allow management interface to override connection entry details */ + ce_defined = ce_management_query_remote(c); + if (IS_SIG (c)) + break; + } + else #endif - if (remote_ip_hint) - c->options.ce.remote = remote_ip_hint; #ifdef ENABLE_MANAGEMENT - if (ce_defined && management && management_query_proxy_enabled (management)) - { - ce_defined = ce_management_query_proxy (c); - if (IS_SIG (c)) - break; - } + if (ce_defined && management && management_query_proxy_enabled (management)) + { + ce_defined = ce_management_query_proxy (c); + if (IS_SIG (c)) + break; + } #endif - } while (!ce_defined); - } + } while (!ce_defined); + + /* Check if this connection attempt would bring us over the limit */ + if (c->options.connect_retry_max > 0 && + c->options.unsuccessful_attempts > (l->len * c->options.connect_retry_max)) + msg(M_FATAL, "All connections have been connect-retry-max (%d) times unsuccessful, exiting", + c->options.connect_retry_max); update_options_ce_post (&c->options); } @@ -392,7 +411,7 @@ next_connection_entry (struct context *c) void init_query_passwords (const struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO /* Certificate password input */ if (c->options.key_pass_file) pem_password_setup (c->options.key_pass_file); @@ -415,47 +434,30 @@ init_query_passwords (const struct context *c) * Initialize/Uninitialize HTTP or SOCKS proxy */ -#ifdef GENERAL_PROXY_SUPPORT - -static int -proxy_scope (struct context *c) -{ - return connection_list_defined (&c->options) ? 2 : 1; -} - static void uninit_proxy_dowork (struct context *c) { -#ifdef ENABLE_HTTP_PROXY if (c->c1.http_proxy_owned && c->c1.http_proxy) { http_proxy_close (c->c1.http_proxy); c->c1.http_proxy = NULL; c->c1.http_proxy_owned = false; } -#endif -#ifdef ENABLE_SOCKS if (c->c1.socks_proxy_owned && c->c1.socks_proxy) { socks_proxy_close (c->c1.socks_proxy); c->c1.socks_proxy = NULL; c->c1.socks_proxy_owned = false; } -#endif } static void init_proxy_dowork (struct context *c) { -#ifdef ENABLE_HTTP_PROXY bool did_http = false; -#else - const bool did_http = false; -#endif uninit_proxy_dowork (c); -#ifdef ENABLE_HTTP_PROXY if (c->options.ce.http_proxy_options) { /* Possible HTTP proxy user/pass input */ @@ -466,51 +468,31 @@ init_proxy_dowork (struct context *c) c->c1.http_proxy_owned = true; } } -#endif -#ifdef ENABLE_SOCKS - if (!did_http && c->options.ce.socks_proxy_server) + if (!did_http && c->options.ce.socks_proxy_server) { c->c1.socks_proxy = socks_proxy_new (c->options.ce.socks_proxy_server, c->options.ce.socks_proxy_port, - c->options.ce.socks_proxy_authfile, - c->options.ce.socks_proxy_retry); + c->options.ce.socks_proxy_authfile); if (c->c1.socks_proxy) { c->c1.socks_proxy_owned = true; } } -#endif } static void -init_proxy (struct context *c, const int scope) +init_proxy (struct context *c) { - if (scope == proxy_scope (c)) - init_proxy_dowork (c); + init_proxy_dowork (c); } static void uninit_proxy (struct context *c) { - if (c->sig->signal_received != SIGUSR1 || proxy_scope (c) == 2) - uninit_proxy_dowork (c); -} - -#else - -static inline void -init_proxy (struct context *c, const int scope) -{ -} - -static inline void -uninit_proxy (struct context *c) -{ + uninit_proxy_dowork (c); } -#endif - void context_init_1 (struct context *c) { @@ -544,8 +526,6 @@ context_init_1 (struct context *c) } #endif - /* initialize HTTP or SOCKS proxy object at scope level 1 */ - init_proxy (c, 1); } void @@ -598,7 +578,7 @@ init_static (void) error_reset (); /* initialize error.c */ reset_check_status (); /* initialize status check code in socket.c */ -#ifdef WIN32 +#ifdef _WIN32 init_win32 (); #endif @@ -659,8 +639,10 @@ init_static (void) #ifdef TEST_GET_DEFAULT_GATEWAY { struct route_gateway_info rgi; + struct route_ipv6_gateway_info rgi6; get_default_gateway(&rgi); - print_default_gateway(M_INFO, &rgi); + get_default_gateway_ipv6(&rgi6, NULL); + print_default_gateway(M_INFO, &rgi, &rgi6); return false; } #endif @@ -810,7 +792,7 @@ uninit_static (void) close_port_share (); #endif -#if defined(MEASURE_TLS_HANDSHAKE_STATS) && defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(MEASURE_TLS_HANDSHAKE_STATS) && defined(ENABLE_CRYPTO) show_tls_performance_stats (); #endif } @@ -853,10 +835,7 @@ print_openssl_info (const struct options *options) */ #ifdef ENABLE_CRYPTO if (options->show_ciphers || options->show_digests || options->show_engines -#ifdef ENABLE_SSL - || options->show_tls_ciphers -#endif - ) + || options->show_tls_ciphers || options->show_curves) { if (options->show_ciphers) show_available_ciphers (); @@ -864,10 +843,10 @@ print_openssl_info (const struct options *options) show_available_digests (); if (options->show_engines) show_available_engines (); -#ifdef ENABLE_SSL if (options->show_tls_ciphers) show_available_tls_ciphers (options->cipher_list); -#endif + if (options->show_curves) + show_available_curves(); return true; } #endif @@ -916,9 +895,7 @@ do_persist_tuntap (const struct options *options) || options->ifconfig_remote_netmask #ifdef ENABLE_CRYPTO || options->shared_secret_file -#ifdef ENABLE_SSL || options->tls_server || options->tls_client -#endif #endif ) msg (M_FATAL|M_OPTERR, @@ -1036,7 +1013,7 @@ const char * format_common_name (struct context *c, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (256, gc); -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { buf_printf (&out, "[%s] ", tls_common_name (c->c2.tls_multi, false)); @@ -1048,7 +1025,7 @@ format_common_name (struct context *c, struct gc_arena *gc) void pre_setup (const struct options *options) { -#ifdef WIN32 +#ifdef _WIN32 if (options->exit_event_name) { win32_signal_open (&win32_signal, @@ -1079,6 +1056,19 @@ reset_coarse_timers (struct context *c) c->c2.coarse_timer_wakeup = 0; } +/* + * Initialise the server poll timeout timer + * This timer is used in the http/socks proxy setup so it needs to be setup + * before + */ +static void +do_init_server_poll_timeout (struct context *c) +{ + update_time (); + if (c->options.ce.connect_timeout) + event_timeout_init (&c->c2.server_poll_interval, c->options.ce.connect_timeout, now); +} + /* * Initialize timers */ @@ -1100,11 +1090,6 @@ do_init_timers (struct context *c, bool deferred) if (c->options.ping_rec_timeout) event_timeout_init (&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now); -#if P2MP - if (c->options.server_poll_timeout) - event_timeout_init (&c->c2.server_poll_interval, c->options.server_poll_timeout, now); -#endif - if (!deferred) { /* initialize connection establishment timer */ @@ -1126,9 +1111,7 @@ do_init_timers (struct context *c, bool deferred) #ifdef ENABLE_CRYPTO if (c->options.packet_id_file) event_timeout_init (&c->c2.packet_id_persist_interval, 60, now); -#endif -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) /* initialize tmp_int optimization that limits the number of times we call tls_multi_process in the main event loop */ interval_init (&c->c2.tmp_int, TLS_MULTI_HORIZON, TLS_MULTI_REFRESH); @@ -1161,9 +1144,9 @@ static void do_alloc_route_list (struct context *c) { if (!c->c1.route_list) - c->c1.route_list = new_route_list (c->options.max_routes, &c->gc); + ALLOC_OBJ_CLEAR_GC (c->c1.route_list, struct route_list, &c->gc); if (c->options.routes_ipv6 && !c->c1.route_ipv6_list) - c->c1.route_ipv6_list = new_route_ipv6_list (c->options.max_routes, &c->gc); + ALLOC_OBJ_CLEAR_GC (c->c1.route_ipv6_list, struct route_ipv6_list, &c->gc); } @@ -1175,7 +1158,6 @@ static void do_init_route_list (const struct options *options, struct route_list *route_list, const struct link_socket_info *link_socket_info, - bool fatal, struct env_set *es) { const char *gw = NULL; @@ -1189,17 +1171,12 @@ do_init_route_list (const struct options *options, if (options->route_default_metric) metric = options->route_default_metric; - if (!init_route_list (route_list, + if (init_route_list (route_list, options->routes, gw, metric, link_socket_current_remote (link_socket_info), es)) - { - if (fatal) - openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */ - } - else { /* copy routes to environment */ setenv_routes (es, route_list); @@ -1209,11 +1186,10 @@ do_init_route_list (const struct options *options, static void do_init_route_ipv6_list (const struct options *options, struct route_ipv6_list *route_ipv6_list, - bool fatal, + const struct link_socket_info *link_socket_info, struct env_set *es) { const char *gw = NULL; - int dev = dev_type_enum (options->dev, options->dev_type); int metric = -1; /* no metric set */ gw = options->ifconfig_ipv6_remote; /* default GW = remote end */ @@ -1225,16 +1201,27 @@ do_init_route_ipv6_list (const struct options *options, if (options->route_default_metric) metric = options->route_default_metric; - if (!init_route_ipv6_list (route_ipv6_list, + /* redirect (IPv6) gateway to VPN? if yes, add a few more specifics + */ + if ( options->routes_ipv6->flags & RG_REROUTE_GW ) + { + char *opt_list[] = { "::/3", "2000::/4", "3000::/4", "fc00::/7", NULL }; + int i; + + for (i=0; opt_list[i]; i++) + { + add_route_ipv6_to_option_list( options->routes_ipv6, + string_alloc (opt_list[i], options->routes_ipv6->gc), + NULL, NULL ); + } + } + + if (init_route_ipv6_list (route_ipv6_list, options->routes_ipv6, gw, metric, + link_socket_current_remote_ipv6 (link_socket_info), es)) - { - if (fatal) - openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */ - } - else { /* copy routes to environment */ setenv_routes_ipv6 (es, route_ipv6_list); @@ -1250,13 +1237,16 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) { static const char message[] = "Initialization Sequence Completed"; + /* Reset the unsuccessful connection counter on complete initialisation */ + c->options.unsuccessful_attempts=0; + /* If we delayed UID/GID downgrade or chroot, do it now */ do_uid_gid_chroot (c, true); /* Test if errors */ if (flags & ISC_ERRORS) { -#ifdef WIN32 +#ifdef _WIN32 show_routes (M_INFO|M_NOPREFIX); show_adapters (M_INFO|M_NOPREFIX); msg (M_INFO, "%s With Errors ( see http://openvpn.net/faq.html#dhcpclientserv )", message); @@ -1267,11 +1257,11 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) else msg (M_INFO, "%s", message); - /* Flag connection_list that we initialized */ - if ((flags & (ISC_ERRORS|ISC_SERVER)) == 0 && connection_list_defined (&c->options)) - connection_list_set_no_advance (&c->options); + /* Flag that we initialized */ + if ((flags & (ISC_ERRORS|ISC_SERVER)) == 0) + c->options.no_advance=true; -#ifdef WIN32 +#ifdef _WIN32 fork_register_dns_action (c->c1.tuntap); #endif @@ -1279,26 +1269,52 @@ initialization_sequence_completed (struct context *c, const unsigned int flags) /* Tell management interface that we initialized */ if (management) { - in_addr_t tun_local = 0; - in_addr_t tun_remote = 0; /* FKS */ + in_addr_t *tun_local = NULL; + struct in6_addr *tun_local6 = NULL; + struct openvpn_sockaddr local, remote; + struct link_socket_actual *actual; + socklen_t sa_len = sizeof(local); const char *detail = "SUCCESS"; - if (c->c1.tuntap) - tun_local = c->c1.tuntap->local; - /* 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"; + detail = "ERROR"; + + CLEAR (local); + actual = &get_link_socket_info(c)->lsa->actual; + remote = actual->dest; + getsockname(c->c2.link_socket->sd, &local.addr.sa, &sa_len); +#if ENABLE_IP_PKTINFO + if (!addr_defined(&local)) + { + switch (local.addr.sa.sa_family) + { + case AF_INET: +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + local.addr.in4.sin_addr = actual->pi.in4.ipi_spec_dst; +#else + local.addr.in4.sin_addr = actual->pi.in4; +#endif + break; + case AF_INET6: + local.addr.in6.sin6_addr = actual->pi.in6.ipi6_addr; + break; + } + } +#endif + + if (c->c1.tuntap) + { + tun_local = &c->c1.tuntap->local; + tun_local6 = &c->c1.tuntap->local_ipv6; + } management_set_state (management, OPENVPN_STATE_CONNECTED, detail, tun_local, - tun_remote); + tun_local6, + &local, + &remote); if (tun_local) - management_post_tunnel_open (management, tun_local); + management_post_tunnel_open (management, *tun_local); } #endif } @@ -1335,12 +1351,12 @@ do_route (const struct options *options, { struct argv argv = argv_new (); setenv_str (es, "script_type", "route-up"); - argv_printf (&argv, "%sc", options->route_script); + argv_parse_cmd (&argv, options->route_script); openvpn_run_script (&argv, es, 0, "--route-up"); argv_reset (&argv); } -#ifdef WIN32 +#ifdef _WIN32 if (options->show_net_up) { show_routes (M_INFO|M_NOPREFIX); @@ -1354,21 +1370,6 @@ do_route (const struct options *options, #endif } -/* - * Save current pulled options string in the c1 context store, so we can - * compare against it after possible future restarts. - */ -#if P2MP -static void -save_pulled_options_digest (struct context *c, const struct md5_digest *newdigest) -{ - if (newdigest) - c->c1.pulled_options_digest_save = *newdigest; - else - md5_digest_clear (&c->c1.pulled_options_digest_save); -} -#endif - /* * initialize tun/tap device object */ @@ -1383,14 +1384,11 @@ do_init_tun (struct context *c) c->options.ifconfig_ipv6_local, c->options.ifconfig_ipv6_netbits, c->options.ifconfig_ipv6_remote, - addr_host (&c->c1.link_socket_addr.local), - addr_host (&c->c1.link_socket_addr.remote), + c->c1.link_socket_addr.bind_local, + c->c1.link_socket_addr.remote_list, !c->options.ifconfig_nowarn, c->c2.es); - /* flag tunnel for IPv6 config if --tun-ipv6 is set */ - c->c1.tuntap->ipv6 = c->options.tun_ipv6; - init_tun_post (c->c1.tuntap, &c->c2.frame, &c->options.tuntap_options); @@ -1408,22 +1406,39 @@ do_open_tun (struct context *c) struct gc_arena gc = gc_new (); bool ret = false; - c->c2.ipv4_tun = (!c->options.tun_ipv6 - && is_dev_type (c->options.dev, c->options.dev_type, "tun")); - +#ifndef TARGET_ANDROID if (!c->c1.tuntap) { +#endif + +#ifdef TARGET_ANDROID + /* If we emulate persist-tun on android we still have to open a new tun and + * then close the old */ + int oldtunfd=-1; + if (c->c1.tuntap) + oldtunfd = c->c1.tuntap->fd; +#endif + /* initialize (but do not open) tun/tap object */ do_init_tun (c); +#ifdef _WIN32 + /* store (hide) interactive service handle in tuntap_options */ + c->c1.tuntap->options.msg_channel = c->options.msg_channel; + msg (D_ROUTE, "interactive service msg_channel=%u", (unsigned int) c->options.msg_channel); +#endif + /* allocate route list structure */ do_alloc_route_list (c); /* parse and resolve the route option list */ - if (c->options.routes && c->c1.route_list && c->c2.link_socket) - do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es); - if (c->options.routes_ipv6 && c->c1.route_ipv6_list ) - do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es); + ASSERT(c->c2.link_socket); + if (c->options.routes && c->c1.route_list) + do_init_route_list (&c->options, c->c1.route_list, + &c->c2.link_socket->info, c->c2.es); + if (c->options.routes_ipv6 && c->c1.route_ipv6_list) + do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, + &c->c2.link_socket->info, c->c2.es); /* do ifconfig */ if (!c->options.ifconfig_noexec @@ -1438,6 +1453,16 @@ do_open_tun (struct context *c) do_ifconfig (c->c1.tuntap, guess, TUN_MTU_SIZE (&c->c2.frame), c->c2.es); } + /* possibly add routes */ + if (route_order() == ROUTE_BEFORE_TUN) { + /* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */ + do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list, + c->c1.tuntap, c->plugins, c->c2.es); + } +#ifdef TARGET_ANDROID + /* Store the old fd inside the fd so open_tun can use it */ + c->c1.tuntap->fd = oldtunfd; +#endif /* open the tun device */ open_tun (c->options.dev, c->options.dev_type, c->options.dev_node, c->c1.tuntap); @@ -1458,7 +1483,7 @@ do_open_tun (struct context *c) c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, -#ifdef WIN32 +#ifdef _WIN32 c->c1.tuntap->adapter_index, #endif dev_type_string (c->options.dev, c->options.dev_type), @@ -1471,17 +1496,17 @@ do_open_tun (struct context *c) "up", c->c2.es); -#ifdef WIN32 +#if defined(_WIN32) if (c->options.block_outside_dns) { dmsg (D_LOW, "Blocking outside DNS"); - if (!win_wfp_block_dns(c->c1.tuntap->adapter_index)) + if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel)) msg (M_FATAL, "Blocking DNS failed!"); } #endif /* possibly add routes */ - if (!c->options.route_delay_defined) + if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined)) do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list, c->c1.tuntap, c->plugins, c->c2.es); @@ -1495,6 +1520,7 @@ do_open_tun (struct context *c) ret = true; static_context = c; +#ifndef TARGET_ANDROID } else { @@ -1510,7 +1536,7 @@ do_open_tun (struct context *c) c->plugins, OPENVPN_PLUGIN_UP, c->c1.tuntap->actual_name, -#ifdef WIN32 +#ifdef _WIN32 c->c1.tuntap->adapter_index, #endif dev_type_string (c->options.dev, c->options.dev_type), @@ -1522,7 +1548,17 @@ do_open_tun (struct context *c) NULL, "up", c->c2.es); +#if defined(_WIN32) + if (c->options.block_outside_dns) + { + dmsg (D_LOW, "Blocking outside DNS"); + if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel)) + msg (M_FATAL, "Blocking DNS failed!"); + } +#endif + } +#endif gc_free (&gc); return ret; } @@ -1539,7 +1575,7 @@ do_close_tun_simple (struct context *c) c->c1.tuntap = NULL; c->c1.tuntap_owned = false; #if P2MP - save_pulled_options_digest (c, NULL); /* delete C1-saved pulled_options_digest */ + CLEAR (c->c1.pulled_options_digest_save); #endif } @@ -1550,7 +1586,7 @@ do_close_tun (struct context *c, bool force) if (c->c1.tuntap && c->c1.tuntap_owned) { const char *tuntap_actual = string_alloc (c->c1.tuntap->actual_name, &gc); -#ifdef WIN32 +#ifdef _WIN32 DWORD adapter_index = c->c1.tuntap->adapter_index; #endif const in_addr_t local = c->c1.tuntap->local; @@ -1571,12 +1607,12 @@ do_close_tun (struct context *c, bool force) /* delete any routes we added */ if (c->c1.route_list || c->c1.route_ipv6_list ) - { + { run_up_down (c->options.route_predown_script, c->plugins, OPENVPN_PLUGIN_ROUTE_PREDOWN, tuntap_actual, -#ifdef WIN32 +#ifdef _WIN32 adapter_index, #endif NULL, @@ -1604,7 +1640,7 @@ do_close_tun (struct context *c, bool force) c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, -#ifdef WIN32 +#ifdef _WIN32 adapter_index, #endif NULL, @@ -1618,10 +1654,10 @@ do_close_tun (struct context *c, bool force) "down", c->c2.es); -#ifdef WIN32 +#if defined(_WIN32) if (c->options.block_outside_dns) { - if (!win_wfp_uninit()) + if (!win_wfp_uninit(c->options.msg_channel)) msg (M_FATAL, "Uninitialising WFP failed!"); } #endif @@ -1638,7 +1674,7 @@ do_close_tun (struct context *c, bool force) c->plugins, OPENVPN_PLUGIN_DOWN, tuntap_actual, -#ifdef WIN32 +#ifdef _WIN32 adapter_index, #endif NULL, @@ -1651,6 +1687,15 @@ do_close_tun (struct context *c, bool force) c->sig->signal_text), "down", c->c2.es); + +#if defined(_WIN32) + if (c->options.block_outside_dns) + { + if (!win_wfp_uninit(c->options.msg_channel)) + msg (M_FATAL, "Uninitialising WFP failed!"); + } +#endif + } } gc_free (&gc); @@ -1671,7 +1716,22 @@ tun_abort() * Handle delayed tun/tap interface bringup due to --up-delay or --pull */ -void +#if P2MP +/** + * Helper for do_up(). Take two option hashes and return true if they are not + * equal, or either one is all-zeroes. + */ +static bool +options_hash_changed_or_zero(const struct md5_digest *a, + const struct md5_digest *b) +{ + const struct md5_digest zero = {{0}}; + return memcmp (a, b, sizeof(struct md5_digest)) || + !memcmp (a, &zero, sizeof(struct md5_digest)); +} +#endif /* P2MP */ + +bool do_up (struct context *c, bool pulled_options, unsigned int option_types_found) { if (!c->c2.do_up_ran) @@ -1679,7 +1739,13 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found) reset_coarse_timers (c); if (pulled_options && option_types_found) - do_deferred_options (c, option_types_found); + { + if (!do_deferred_options (c, option_types_found)) + { + msg (D_PUSH_ERRORS, "ERROR: Failed to apply push options"); + return false; + } + } /* if --up-delay specified, open tun, do ifconfig, and run up script now */ if (c->options.up_delay || PULL_DEFINED (&c->options)) @@ -1695,8 +1761,8 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found) if (!c->c2.did_open_tun && PULL_DEFINED (&c->options) && c->c1.tuntap - && (!md5_digest_defined (&c->c1.pulled_options_digest_save) || !md5_digest_defined (&c->c2.pulled_options_digest) - || !md5_digest_equal (&c->c1.pulled_options_digest_save, &c->c2.pulled_options_digest))) + && options_hash_changed_or_zero (&c->c1.pulled_options_digest_save, + &c->c2.pulled_options_digest)) { /* if so, close tun, delete routes, then reinitialize tun and add routes */ msg (M_INFO, "NOTE: Pulled options changed on restart, will need to close and reopen TUN/TAP device."); @@ -1711,11 +1777,11 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found) if (c->c2.did_open_tun) { #if P2MP - save_pulled_options_digest (c, &c->c2.pulled_options_digest); + c->c1.pulled_options_digest_save = c->c2.pulled_options_digest; #endif /* if --route-delay was specified, start timer */ - if (c->options.route_delay_defined) + if ((route_order() == ROUTE_AFTER_TUN) && c->options.route_delay_defined) { event_timeout_init (&c->c2.route_wakeup, c->options.route_delay, now); event_timeout_init (&c->c2.route_wakeup_expire, c->options.route_delay + c->options.route_delay_window, now); @@ -1734,6 +1800,7 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found) c->c2.do_up_ran = true; } + return true; } /* @@ -1761,13 +1828,18 @@ pull_permission_mask (const struct context *c) if (!c->options.route_nopull) flags |= (OPT_P_ROUTE | OPT_P_IPWIN32); +#ifdef ENABLE_CRYPTO + if (c->options.ncp_enabled) + flags |= OPT_P_NCP; +#endif + return flags; } /* * Handle non-tun-related pulled options. */ -void +bool do_deferred_options (struct context *c, const unsigned int found) { if (found & OPT_P_MESSAGES) @@ -1794,14 +1866,12 @@ do_deferred_options (struct context *c, const unsigned int found) } #endif -#ifdef ENABLE_LZO +#ifdef USE_COMP if (found & OPT_P_COMP) { - if (lzo_defined (&c->c2.lzo_compwork)) - { - msg (D_PUSH, "OPTIONS IMPORT: LZO parms modified"); - lzo_modify_flags (&c->c2.lzo_compwork, c->options.lzo); - } + msg (D_PUSH, "OPTIONS IMPORT: compression parms modified"); + comp_uninit (c->c2.comp_context); + c->c2.comp_context = comp_init (&c->options.comp); } #endif @@ -1836,7 +1906,7 @@ do_deferred_options (struct context *c, const unsigned int found) if (found & OPT_P_SETENV) msg (D_PUSH, "OPTIONS IMPORT: environment modified"); -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO if (found & OPT_P_PEER_ID) { msg (D_PUSH, "OPTIONS IMPORT: peer-id set"); @@ -1856,20 +1926,37 @@ do_deferred_options (struct context *c, const unsigned int found) " MTU problems", TUN_MTU_SIZE(&c->c2.frame) ); } } + + /* process (potentially pushed) crypto options */ + if (c->options.pull) + { + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + if (found & OPT_P_NCP) + msg (D_PUSH, "OPTIONS IMPORT: data channel crypto options modified"); + /* Do not regenerate keys if server sends an extra push request */ + if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized && + !tls_session_update_crypto_params(session, &c->options, &c->c2.frame)) + { + msg (D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options"); + return false; + } + } #endif + return true; } /* - * Possible hold on initialization + * Possible hold on initialization, holdtime is the + * time OpenVPN would wait without management */ static bool -do_hold (void) +do_hold (int holdtime) { #ifdef ENABLE_MANAGEMENT if (management) { /* block until management hold is released */ - if (management_hold (management)) + if (management_hold (management, holdtime)) return true; } #endif @@ -1882,31 +1969,16 @@ do_hold (void) static void socket_restart_pause (struct context *c) { - bool proxy = false; int sec = 2; - -#ifdef ENABLE_HTTP_PROXY - if (c->options.ce.http_proxy_options) - proxy = true; -#endif -#ifdef ENABLE_SOCKS - if (c->options.ce.socks_proxy_server) - proxy = true; -#endif + int backoff = 0; switch (c->options.ce.proto) { - case PROTO_UDPv4: - case PROTO_UDPv6: - if (proxy) - sec = c->options.ce.connect_retry_seconds; - break; - case PROTO_TCPv4_SERVER: - case PROTO_TCPv6_SERVER: + case PROTO_TCP_SERVER: sec = 1; break; - case PROTO_TCPv4_CLIENT: - case PROTO_TCPv6_CLIENT: + case PROTO_UDP: + case PROTO_TCP_CLIENT: sec = c->options.ce.connect_retry_seconds; break; } @@ -1919,13 +1991,22 @@ socket_restart_pause (struct context *c) #if P2MP 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 + /* Slow down reconnection after 5 retries per remote -- for tcp only in client mode */ + if (c->options.ce.proto != PROTO_TCP_SERVER) + { + backoff = (c->options.unsuccessful_attempts / c->options.connection_list->len) - 4; + if (backoff > 0) + { + /* sec is less than 2^16; we can left shift it by up to 15 bits without overflow */ + sec = max_int (sec, 1) << min_int (backoff, 15); + } + + if (sec > c->options.ce.connect_retry_seconds_max) + sec = c->options.ce.connect_retry_seconds_max; + } + if (c->persist.restart_sleep_seconds > 0 && c->persist.restart_sleep_seconds > sec) sec = c->persist.restart_sleep_seconds; else if (c->persist.restart_sleep_seconds == -1) @@ -1933,8 +2014,10 @@ socket_restart_pause (struct context *c) c->persist.restart_sleep_seconds = 0; /* do managment hold on context restart, i.e. second, third, fourth, etc. initialization */ - if (do_hold ()) + if (do_hold (sec)) + { sec = 0; + } if (sec) { @@ -1952,7 +2035,7 @@ do_startup_pause (struct context *c) if (!c->first_time) socket_restart_pause (c); else - do_hold (); /* do management hold on first context initialization */ + do_hold (0); /* do management hold on first context initialization */ } /* @@ -1977,6 +2060,7 @@ frame_finalize_options (struct context *c, const struct options *o) |FRAME_HEADROOM_MARKER_READ_STREAM); } + frame_add_to_extra_buffer (&c->c2.frame, PAYLOAD_ALIGN); frame_finalize (&c->c2.frame, o->ce.link_mtu_defined, o->ce.link_mtu, @@ -1992,13 +2076,11 @@ key_schedule_free (struct key_schedule *ks, bool free_ssl_ctx) { #ifdef ENABLE_CRYPTO free_key_ctx_bi (&ks->static_key); -#ifdef ENABLE_SSL if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx) { tls_ctx_free (&ks->ssl_ctx); - free_key_ctx_bi (&ks->tls_auth_key); + free_key_ctx_bi (&ks->tls_wrap_key); } -#endif /* ENABLE_SSL */ #endif /* ENABLE_CRYPTO */ CLEAR (*ks); } @@ -2018,14 +2100,6 @@ init_crypto_pre (struct context *c, const unsigned int flags) packet_id_persist_load (&c->c1.pid_persist, c->options.packet_id_file); } - /* Initialize crypto options */ - - if (c->options.use_iv) - c->c2.crypto_options.flags |= CO_USE_IV; - - if (c->options.mute_replay_warnings) - c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS; - #ifdef ENABLE_PREDICTION_RESISTANCE if (c->options.use_prediction_resistance) rand_ctx_enable_prediction_resistance(); @@ -2044,60 +2118,38 @@ do_init_crypto_static (struct context *c, const unsigned int flags) init_crypto_pre (c, flags); + /* Initialize flags */ + if (c->options.use_iv) + c->c2.crypto_options.flags |= CO_USE_IV; + + if (c->options.mute_replay_warnings) + c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS; + /* Initialize packet ID tracking */ if (options->replay) { - packet_id_init (&c->c2.packet_id, - link_socket_proto_connection_oriented (options->ce.proto), + packet_id_init (&c->c2.crypto_options.packet_id, 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; packet_id_persist_load_obj (&c->c1.pid_persist, - c->c2.crypto_options.packet_id); + &c->c2.crypto_options.packet_id); } if (!key_ctx_bi_defined (&c->c1.ks.static_key)) { - struct key2 key2; - struct key_direction_state kds; - /* Get cipher & hash algorithms */ - init_key_type (&c->c1.ks.key_type, options->ciphername, - options->ciphername_defined, options->authname, - options->authname_defined, options->keysize, - options->test_crypto, true); + init_key_type (&c->c1.ks.key_type, options->ciphername, options->authname, + options->keysize, options->test_crypto, true); /* Read cipher and hmac keys from shared secret file */ - { - unsigned int rkf_flags = RKF_MUST_SUCCEED; - const char *rkf_file = options->shared_secret_file; - - if (options->shared_secret_file_inline) - { - rkf_file = options->shared_secret_file_inline; - rkf_flags |= RKF_INLINE; - } - read_key_file (&key2, rkf_file, rkf_flags); - } - - /* Check for and fix highly unlikely key problems */ - verify_fix_key2 (&key2, &c->c1.ks.key_type, - options->shared_secret_file); - - /* Initialize OpenSSL key objects */ - key_direction_state_init (&kds, options->key_direction); - must_have_n_keys (options->shared_secret_file, "secret", &key2, - kds.need_keys); - init_key_ctx (&c->c1.ks.static_key.encrypt, &key2.keys[kds.out_key], - &c->c1.ks.key_type, OPENVPN_OP_ENCRYPT, "Static Encrypt"); - init_key_ctx (&c->c1.ks.static_key.decrypt, &key2.keys[kds.in_key], - &c->c1.ks.key_type, OPENVPN_OP_DECRYPT, "Static Decrypt"); - - /* Erase the temporary copy of key */ - CLEAR (key2); + crypto_read_openvpn_key (&c->c1.ks.key_type, &c->c1.ks.static_key, + options->shared_secret_file, + options->shared_secret_file_inline, + options->key_direction, "Static Key Encryption", + "secret"); } else { @@ -2105,12 +2157,11 @@ do_init_crypto_static (struct context *c, const unsigned int flags) } /* Get key schedule */ - c->c2.crypto_options.key_ctx_bi = &c->c1.ks.static_key; + c->c2.crypto_options.key_ctx_bi = c->c1.ks.static_key; /* Compute MTU parameters */ crypto_adjust_frame_parameters (&c->c2.frame, &c->c1.ks.key_type, - options->ciphername_defined, options->use_iv, options->replay, true); /* Sanity check on IV, sequence number, and cipher mode options */ @@ -2118,8 +2169,6 @@ do_init_crypto_static (struct context *c, const unsigned int flags) options->use_iv); } -#ifdef ENABLE_SSL - /* * Initialize the persistent component of OpenVPN's TLS mode, * which is preserved across SIGUSR1 resets. @@ -2160,9 +2209,8 @@ do_init_crypto_tls_c1 (struct context *c) } /* Get cipher & hash algorithms */ - init_key_type (&c->c1.ks.key_type, options->ciphername, - options->ciphername_defined, options->authname, - options->authname_defined, options->keysize, true, true); + init_key_type (&c->c1.ks.key_type, options->ciphername, options->authname, + options->keysize, true, true); /* Initialize PRNG with config-specified digest */ prng_init (options->prng_hash, options->prng_nonce_secret_len); @@ -2170,21 +2218,36 @@ do_init_crypto_tls_c1 (struct context *c) /* TLS handshake authentication (--tls-auth) */ if (options->tls_auth_file) { - unsigned int flags = 0; - const char *file = options->tls_auth_file; - - if (options->tls_auth_file_inline) + /* Initialize key_type for tls-auth with auth only */ + CLEAR (c->c1.ks.tls_auth_key_type); + if (!streq (options->authname, "none")) { - flags |= GHK_INLINE; - file = options->tls_auth_file_inline; + c->c1.ks.tls_auth_key_type.digest = md_kt_get (options->authname); + c->c1.ks.tls_auth_key_type.hmac_length = + md_kt_size (c->c1.ks.tls_auth_key_type.digest); } - get_tls_handshake_key (&c->c1.ks.key_type, - &c->c1.ks.tls_auth_key, - file, - options->key_direction, - flags); + else + { + msg (M_FATAL, "ERROR: tls-auth enabled, but no valid --auth " + "algorithm specified ('%s')", options->authname); + } + + crypto_read_openvpn_key (&c->c1.ks.tls_auth_key_type, + &c->c1.ks.tls_wrap_key, options->tls_auth_file, + options->tls_auth_file_inline, options->key_direction, + "Control Channel Authentication", "tls-auth"); } + /* TLS handshake encryption+authentication (--tls-crypt) */ + if (options->tls_crypt_file) { + tls_crypt_init_key (&c->c1.ks.tls_wrap_key, options->tls_crypt_file, + options->tls_crypt_inline, options->tls_server); + } + + c->c1.ciphername = options->ciphername; + c->c1.authname = options->authname; + c->c1.keysize = options->keysize; + #if 0 /* was: #if ENABLE_INLINE_FILES -- Note that enabling this code will break restarts */ if (options->priv_key_file_inline) { @@ -2196,6 +2259,11 @@ do_init_crypto_tls_c1 (struct context *c) else { msg (D_INIT_MEDIUM, "Re-using SSL/TLS context"); + + /* Restore pre-NCP cipher options */ + c->options.ciphername = c->c1.ciphername; + c->options.authname = c->c1.authname; + c->options.keysize = c->c1.keysize; } } @@ -2226,17 +2294,28 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) /* In short form, unique datagram identifier is 32 bits, in long form 64 bits */ packet_id_long_form = cipher_kt_mode_ofb_cfb (c->c1.ks.key_type.cipher); - /* Compute MTU parameters */ - crypto_adjust_frame_parameters (&c->c2.frame, - &c->c1.ks.key_type, - options->ciphername_defined, - options->use_iv, - options->replay, packet_id_long_form); + /* Compute MTU parameters (postpone if we push/pull options) */ + if (c->options.pull || c->options.mode == MODE_SERVER) + { + /* Account for worst-case crypto overhead before allocating buffers */ + frame_add_to_extra_frame (&c->c2.frame, crypto_max_overhead()); + } + else + { + crypto_adjust_frame_parameters(&c->c2.frame, &c->c1.ks.key_type, + options->use_iv, options->replay, packet_id_long_form); + } tls_adjust_frame_parameters (&c->c2.frame); /* Set all command-line TLS-related options */ CLEAR (to); + if (options->use_iv) + to.crypto_flags |= CO_USE_IV; + + if (options->mute_replay_warnings) + to.crypto_flags |= CO_MUTE_REPLAY_WARNINGS; + to.crypto_flags_and = ~(CO_PACKET_ID_LONG_FORM); if (packet_id_long_form) to.crypto_flags_or = CO_PACKET_ID_LONG_FORM; @@ -2249,6 +2328,9 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.replay_window = options->replay_window; to.replay_time = options->replay_time; to.tcp_mode = link_socket_proto_connection_oriented (options->ce.proto); + to.config_ciphername = c->c1.ciphername; + to.config_authname = c->c1.authname; + to.ncp_enabled = options->ncp_enabled; to.transition_window = options->transition_window; to.handshake_window = options->handshake_window; to.packet_timeout = options->tls_timeout; @@ -2256,6 +2338,8 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.renegotiate_packets = options->renegotiate_packets; to.renegotiate_seconds = options->renegotiate_seconds; to.single_session = options->single_session; + to.mode = options->mode; + to.pull = options->pull; #ifdef ENABLE_PUSH_PEER_INFO if (options->push_peer_info) /* all there is */ to.push_peer_info_detail = 2; @@ -2267,7 +2351,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) /* should we not xmit any packets until we get an initial response from client? */ - if (to.server && options->ce.proto == PROTO_TCPv4_SERVER) + if (to.server && options->ce.proto == PROTO_TCP_SERVER) to.xmit_hold = true; #ifdef ENABLE_OCC @@ -2279,6 +2363,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.verify_x509_type = (options->verify_x509_type & 0xff); to.verify_x509_name = options->verify_x509_name; to.crl_file = options->crl_file; + to.crl_file_inline = options->crl_file_inline; 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)); @@ -2308,11 +2393,11 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) if (options->ccd_exclusive) to.client_config_dir_exclusive = options->client_config_dir; to.auth_user_pass_file = options->auth_user_pass_file; + to.auth_token_generate = options->auth_token_generate; + to.auth_token_lifetime = options->auth_token_lifetime; #endif -#ifdef ENABLE_X509_TRACK to.x509_track = options->x509_track; -#endif #if P2MP #ifdef ENABLE_CLIENT_CR @@ -2320,15 +2405,46 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) #endif #endif +#ifdef USE_COMP + to.comp_options = options->comp; +#endif + +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 + if (options->keying_material_exporter_label) + { + to.ekm_size = options->keying_material_exporter_length; + if (to.ekm_size < 16 || to.ekm_size > 4095) + to.ekm_size = 0; + + to.ekm_label = options->keying_material_exporter_label; + to.ekm_label_size = strlen(to.ekm_label); + } + else + { + to.ekm_size = 0; + } +#endif + /* TLS handshake authentication (--tls-auth) */ if (options->tls_auth_file) { - to.tls_auth_key = c->c1.ks.tls_auth_key; - to.tls_auth.pid_persist = &c->c1.pid_persist; - to.tls_auth.flags |= CO_PACKET_ID_LONG_FORM; + to.tls_wrap.mode = TLS_WRAP_AUTH; + to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key; + to.tls_wrap.opt.pid_persist = &c->c1.pid_persist; + to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM; crypto_adjust_frame_parameters (&to.frame, - &c->c1.ks.key_type, - false, false, true, true); + &c->c1.ks.tls_auth_key_type, + false, true, true); + } + + /* TLS handshake encryption (--tls-crypt) */ + if (options->tls_crypt_file) + { + to.tls_wrap.mode = TLS_WRAP_CRYPT; + to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key; + to.tls_wrap.opt.pid_persist = &c->c1.pid_persist; + to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM; + tls_crypt_adjust_frame_parameters (&to.frame); } /* If we are running over TCP, allow for @@ -2364,10 +2480,6 @@ do_init_finalize_tls_frame (struct context *c) } } -#endif /* ENABLE_SSL */ -#endif /* ENABLE_CRYPTO */ - -#ifdef ENABLE_CRYPTO /* * No encryption or authentication. */ @@ -2386,54 +2498,70 @@ do_init_crypto (struct context *c, const unsigned int flags) #ifdef ENABLE_CRYPTO if (c->options.shared_secret_file) do_init_crypto_static (c, flags); -#ifdef ENABLE_SSL else if (c->options.tls_server || c->options.tls_client) do_init_crypto_tls (c, flags); -#endif else /* no encryption or authentication. */ do_init_crypto_none (c); #else /* ENABLE_CRYPTO */ msg (M_WARN, "******* WARNING *******: " PACKAGE_NAME - " built without OpenSSL -- encryption and authentication features disabled -- all data will be tunnelled as cleartext"); + " built without crypto library -- encryption and authentication features disabled -- all data will be tunnelled as cleartext"); #endif /* ENABLE_CRYPTO */ } static void do_init_frame (struct context *c) { -#ifdef ENABLE_LZO +#ifdef USE_COMP /* - * Initialize LZO compression library. + * modify frame parameters if compression is enabled */ - if (c->options.lzo & LZO_SELECTED) + if (comp_enabled(&c->options.comp)) { - lzo_adjust_frame_parameters (&c->c2.frame); + comp_add_to_extra_frame (&c->c2.frame); +#if !defined(ENABLE_LZ4) /* - * LZO usage affects buffer alignment. + * Compression usage affects buffer alignment when non-swapped algs + * such as LZO is used. + * Newer algs like LZ4 and comp-stub with COMP_F_SWAP don't need + * any special alignment because of the control-byte swap approach. + * LZO alignment (on the other hand) is problematic because + * the presence of the control byte means that either the output of + * decryption must be written to an unaligned buffer, or the input + * to compression (or packet dispatch if packet is uncompressed) + * must be read from an unaligned buffer. + * This code tries to align the input to compression (or packet + * dispatch if packet is uncompressed) at the cost of requiring + * decryption output to be written to an unaligned buffer, so + * it's more of a tradeoff than an optimal solution and we don't + * include it when we are doing a modern build with LZ4. + * Strictly speaking, on the server it would be better to execute + * this code for every connection after we decide the compression + * method, but currently the frame code doesn't appear to be + * flexible enough for this, since the frame is already established + * before it is known which compression options will be pushed. */ - if (CIPHER_ENABLED (c)) + if (comp_unswapped_prefix (&c->options.comp) && CIPHER_ENABLED (c)) { - frame_add_to_align_adjust (&c->c2.frame, LZO_PREFIX_LEN); + frame_add_to_align_adjust (&c->c2.frame, COMP_PREFIX_LEN); frame_or_align_flags (&c->c2.frame, FRAME_HEADROOM_MARKER_FRAGMENT |FRAME_HEADROOM_MARKER_DECRYPT); } +#endif #ifdef ENABLE_FRAGMENT - lzo_adjust_frame_parameters (&c->c2.frame_fragment_omit); /* omit LZO frame delta from final frame_fragment */ + comp_add_to_extra_frame (&c->c2.frame_fragment_omit); /* omit compression frame delta from final frame_fragment */ #endif } -#endif /* ENABLE_LZO */ +#endif /* USE_COMP */ -#ifdef ENABLE_SOCKS /* * Adjust frame size for UDP Socks support. */ if (c->options.ce.socks_proxy_server) socks_adjust_frame_parameters (&c->c2.frame, c->options.ce.proto); -#endif /* * Adjust frame size based on the --tun-mtu-extra parameter. @@ -2454,16 +2582,27 @@ do_init_frame (struct context *c) */ frame_finalize_options (c, NULL); +#ifdef USE_COMP + /* + * Modify frame parameters if compression is compiled in. + * Should be called after frame_finalize_options. + */ + comp_add_to_extra_buffer (&c->c2.frame); +#ifdef ENABLE_FRAGMENT + comp_add_to_extra_buffer (&c->c2.frame_fragment_omit); /* omit compression frame delta from final frame_fragment */ +#endif +#endif /* USE_COMP */ + /* packets with peer-id (P_DATA_V2) need 3 extra bytes in frame (on client) * and need link_mtu+3 bytes on socket reception (on server). * - * accomodate receive path in f->extra_link - * send path in f->extra_buffer (+leave room for alignment) + * accomodate receive path in f->extra_link, which has the side effect of + * also increasing send buffers (BUF_SIZE() macro), which need to be + * allocated big enough before receiving peer-id option from server. * * f->extra_frame is adjusted when peer-id option is push-received */ frame_add_to_extra_link(&c->c2.frame, 3); - frame_add_to_extra_buffer(&c->c2.frame, 8); #ifdef ENABLE_FRAGMENT /* @@ -2546,7 +2685,6 @@ do_option_warnings (struct context *c) if (!o->use_iv) msg (M_WARN, "WARNING: You have disabled Crypto IVs (--no-iv) which may make " PACKAGE_NAME " less secure"); -#ifdef ENABLE_SSL if (o->tls_server) warn_on_use_of_common_subnets (); if (o->tls_client @@ -2556,12 +2694,6 @@ do_option_warnings (struct context *c) && !o->remote_cert_eku) msg (M_WARN, "WARNING: No server certificate verification method has been enabled. See http://openvpn.net/howto.html#mitm for more info."); #endif -#endif - -#ifndef CONNECT_NONBLOCK - if (o->ce.connect_timeout_defined) - msg (M_WARN, "NOTE: --connect-timeout option is not supported on this OS"); -#endif /* If a script is used, print appropiate warnings */ if (o->user_script_used) @@ -2578,7 +2710,7 @@ do_option_warnings (struct context *c) static void do_init_frame_tls (struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO do_init_finalize_tls_frame (c); #endif } @@ -2600,9 +2732,9 @@ init_context_buffers (const struct frame *frame) b->decrypt_buf = alloc_buf (BUF_SIZE (frame)); #endif -#ifdef ENABLE_LZO - b->lzo_compress_buf = alloc_buf (BUF_SIZE (frame)); - b->lzo_decompress_buf = alloc_buf (BUF_SIZE (frame)); +#ifdef USE_COMP + b->compress_buf = alloc_buf (BUF_SIZE (frame)); + b->decompress_buf = alloc_buf (BUF_SIZE (frame)); #endif return b; @@ -2617,9 +2749,9 @@ free_context_buffers (struct context_buffers *b) free_buf (&b->read_tun_buf); free_buf (&b->aux_buf); -#ifdef ENABLE_LZO - free_buf (&b->lzo_compress_buf); - free_buf (&b->lzo_decompress_buf); +#ifdef USE_COMP + free_buf (&b->compress_buf); + free_buf (&b->decompress_buf); #endif #ifdef ENABLE_CRYPTO @@ -2657,19 +2789,6 @@ do_init_fragment (struct context *c) } #endif -/* - * Set the --mssfix option. - */ -static void -do_init_mssfix (struct context *c) -{ - if (c->options.ce.mssfix) - { - frame_set_mtu_dynamic (&c->c2.frame, - c->options.ce.mssfix, SET_MTU_UPPER_BOUND); - } -} - /* * Allocate our socket object. */ @@ -2695,20 +2814,18 @@ do_init_socket_1 (struct context *c, const int mode) #endif link_socket_init_phase1 (c->c2.link_socket, - connection_list_defined (&c->options), c->options.ce.local, c->options.ce.local_port, c->options.ce.remote, c->options.ce.remote_port, + c->c1.dns_cache, c->options.ce.proto, + c->options.ce.af, + c->options.ce.bind_ipv6_only, mode, c->c2.accept_from, -#ifdef ENABLE_HTTP_PROXY c->c1.http_proxy, -#endif -#ifdef ENABLE_SOCKS c->c1.socks_proxy, -#endif #ifdef ENABLE_DEBUG c->options.gremlin, #endif @@ -2719,13 +2836,11 @@ do_init_socket_1 (struct context *c, const int mode) c->options.ipchange, c->plugins, c->options.resolve_retry_seconds, - c->options.ce.connect_retry_seconds, - c->options.ce.connect_timeout, - c->options.ce.connect_retry_max, c->options.ce.mtu_discover_type, c->options.rcvbuf, c->options.sndbuf, c->options.mark, + &c->c2.server_poll_interval, sockflags); } @@ -2736,7 +2851,7 @@ static void do_init_socket_2 (struct context *c) { link_socket_init_phase2 (c->c2.link_socket, &c->c2.frame, - &c->sig->signal_received); + c->sig); } /* @@ -2767,22 +2882,14 @@ do_compute_occ_strings (struct context *c) c->c2.options_string_remote = options_string (&c->options, &c->c2.frame, c->c1.tuntap, true, &gc); - msg (D_SHOW_OCC, "Local Options String: '%s'", c->c2.options_string_local); - msg (D_SHOW_OCC, "Expected Remote Options String: '%s'", - c->c2.options_string_remote); + msg (D_SHOW_OCC, "Local Options String (VER=%s): '%s'", + options_string_version (c->c2.options_string_local, &gc), + c->c2.options_string_local); + msg (D_SHOW_OCC, "Expected Remote Options String (VER=%s): '%s'", + options_string_version (c->c2.options_string_remote, &gc), + c->c2.options_string_remote); #ifdef ENABLE_CRYPTO - msg (D_SHOW_OCC_HASH, "Local Options hash (VER=%s): '%s'", - options_string_version (c->c2.options_string_local, &gc), - md5sum ((uint8_t*)c->c2.options_string_local, - strlen (c->c2.options_string_local), 9, &gc)); - msg (D_SHOW_OCC_HASH, "Expected Remote Options hash (VER=%s): '%s'", - options_string_version (c->c2.options_string_remote, &gc), - md5sum ((uint8_t*)c->c2.options_string_remote, - strlen (c->c2.options_string_remote), 9, &gc)); -#endif - -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) if (c->c2.tls_multi) tls_multi_init_set_options (c->c2.tls_multi, c->c2.options_string_local, @@ -2859,7 +2966,7 @@ do_close_free_buf (struct context *c) static void do_close_tls (struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { tls_multi_free (c->c2.tls_multi, true); @@ -2899,14 +3006,31 @@ do_close_link_socket (struct context *c) c->c2.link_socket = NULL; } - if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_remote_ip)) - { - CLEAR (c->c1.link_socket_addr.remote); + + /* Preserve the resolved list of remote if the user request to or if we want + * reconnect to the same host again or there are still addresses that need + * to be tried */ + if (!(c->sig->signal_received == SIGUSR1 && + ( (c->options.persist_remote_ip) + || + ( c->sig->source != SIG_SOURCE_HARD && + ((c->c1.link_socket_addr.current_remote && c->c1.link_socket_addr.current_remote->ai_next) + || c->options.no_advance)) + ))) + { + clear_remote_addrlist(&c->c1.link_socket_addr, !c->options.resolve_in_advance); + } + + /* Clear the remote actual address when persist_remote_ip is not in use */ + if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_remote_ip)) CLEAR (c->c1.link_socket_addr.actual); - } - if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_local_ip)) - CLEAR (c->c1.link_socket_addr.local); + if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_local_ip)) { + if (c->c1.link_socket_addr.bind_local && !c->options.resolve_in_advance) + freeaddrinfo(c->c1.link_socket_addr.bind_local); + + c->c1.link_socket_addr.bind_local=NULL; + } } /* @@ -2916,7 +3040,7 @@ static void do_close_packet_id (struct context *c) { #ifdef ENABLE_CRYPTO - packet_id_free (&c->c2.packet_id); + packet_id_free (&c->c2.crypto_options.packet_id); packet_id_persist_save (&c->c1.pid_persist); if (!(c->sig->signal_received == SIGUSR1)) packet_id_persist_close (&c->c1.pid_persist); @@ -3070,7 +3194,7 @@ do_setup_fast_io (struct context *c) { if (c->options.fast_io) { -#ifdef WIN32 +#ifdef _WIN32 msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows"); #else if (!proto_is_udp(c->options.ce.proto)) @@ -3093,7 +3217,7 @@ do_setup_fast_io (struct context *c) static void do_signal_on_tls_errors (struct context *c) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO if (c->options.tls_exit) c->c2.tls_exit_signal = SIGTERM; else @@ -3183,7 +3307,7 @@ management_callback_status_p2p (void *arg, const int version, struct status_outp void management_show_net_callback (void *arg, const int msglevel) { -#ifdef WIN32 +#ifdef _WIN32 show_routes (msglevel); show_adapters (msglevel); msg (msglevel, "END"); @@ -3192,6 +3316,37 @@ management_show_net_callback (void *arg, const int msglevel) #endif } +#ifdef TARGET_ANDROID +int +management_callback_network_change (void *arg, bool samenetwork) +{ + /* Check if the client should translate the network change to a SIGUSR1 to + reestablish the connection or just reprotect the socket + + At the moment just assume that, for all settings that use pull (not + --static) and are not using peer-id reestablishing the connection is + required (unless the network is the same) + + The function returns -1 on invalid fd and -2 if the socket cannot be + reused. On the -2 return value the man_network_change function triggers + a SIGUSR1 to force a reconnect. + */ + + int socketfd=-1; + struct context *c = (struct context *) arg; + if (!c->c2.link_socket) + return -1; + if (c->c2.link_socket->sd == SOCKET_UNDEFINED) + return -1; + + socketfd = c->c2.link_socket->sd; + if (!c->options.pull || c->c2.tls_multi->use_peer_id || samenetwork) + return socketfd; + else + return -2; +} +#endif + #endif void @@ -3207,6 +3362,9 @@ init_management_callback_p2p (struct context *c) cb.show_net = management_show_net_callback; cb.proxy_cmd = management_callback_proxy_cmd; cb.remote_cmd = management_callback_remote_cmd; +#ifdef TARGET_ANDROID + cb.network_change = management_callback_network_change; +#endif management_set_callback (management, &cb); } #endif @@ -3248,12 +3406,14 @@ open_management (struct context *c) management_set_state (management, OPENVPN_STATE_CONNECTING, NULL, - (in_addr_t)0, - (in_addr_t)0); + NULL, + NULL, + NULL, + NULL); } /* initial management hold, called early, before first context initialization */ - do_hold (); + do_hold (0); if (IS_SIG (c)) { msg (M_WARN, "Signal received from management interface, exiting"); @@ -3326,10 +3486,14 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int /* init garbage collection level */ gc_init (&c->c2.gc); + /* inherit environmental variables */ + if (env) + do_inherit_env (c, env); + /* signals caught here will abort */ c->sig->signal_received = 0; c->sig->signal_text = NULL; - c->sig->hard = false; + c->sig->source = SIG_SOURCE_SOFT; if (c->mode == CM_P2P) init_management_callback_p2p (c); @@ -3342,14 +3506,20 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int goto sig; } + if (c->options.resolve_in_advance) + { + do_preresolve (c); + if (IS_SIG (c)) + goto sig; + } + /* map in current connection entry */ next_connection_entry (c); /* 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 - || c->options.ce.proto == PROTO_TCPv6_SERVER) + if (c->options.ce.proto == PROTO_TCP_SERVER) { if (c->mode == CM_TOP) link_socket_mode = LS_MODE_TCP_LISTEN; @@ -3378,10 +3548,6 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int if (c->mode == CM_P2P || c->mode == CM_TOP) do_option_warnings (c); - /* inherit environmental variables */ - if (env) - do_inherit_env (c, env); - #ifdef ENABLE_PLUGIN /* initialize plugins */ if (c->mode == CM_P2P || c->mode == CM_TOP) @@ -3416,7 +3582,7 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int do_event_set_init (c, false); /* initialize HTTP or SOCKS proxy object at scope level 2 */ - init_proxy (c, 2); + init_proxy (c); /* allocate our socket object */ if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP) @@ -3442,10 +3608,10 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int goto sig; } -#ifdef ENABLE_LZO - /* initialize LZO compression library. */ - if ((options->lzo & LZO_SELECTED) && (c->mode == CM_P2P || child)) - lzo_compress_init (&c->c2.lzo_compwork, options->lzo); +#ifdef USE_COMP + /* initialize compression library. */ + if (comp_enabled(&options->comp) && (c->mode == CM_P2P || child)) + c->c2.comp_context = comp_init (&options->comp); #endif /* initialize MTU variables */ @@ -3465,7 +3631,7 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int #endif /* initialize dynamic MTU variable */ - do_init_mssfix (c); + frame_init_mssfix (&c->c2.frame, &c->options); /* bind the TCP/UDP socket */ if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP) @@ -3498,16 +3664,19 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int open_plugins (c, false, OPENVPN_PLUGIN_INIT_POST_DAEMON); #endif + /* initialise connect timeout timer */ + do_init_server_poll_timeout(c); + + /* finalize the TCP/UDP socket */ + if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP) + do_init_socket_2 (c); + /* * Actually do UID/GID downgrade, and chroot, if requested. * May be delayed by --client, --pull, or --up-delay. */ do_uid_gid_chroot (c, c->c2.did_open_tun); - /* finalize the TCP/UDP socket */ - if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP) - do_init_socket_2 (c); - /* initialize timers */ if (c->mode == CM_P2P || child) do_init_timers (c, false); @@ -3559,9 +3728,12 @@ close_instance (struct context *c) /* if xinetd/inetd mode, don't allow restart */ do_close_check_if_restart_permitted (c); -#ifdef ENABLE_LZO - if (lzo_defined (&c->c2.lzo_compwork)) - lzo_compress_uninit (&c->c2.lzo_compwork); +#ifdef USE_COMP + if (c->c2.comp_context) + { + comp_uninit (c->c2.comp_context); + c->c2.comp_context = NULL; + } #endif /* free buffers */ @@ -3636,11 +3808,14 @@ inherit_context_child (struct context *dest, #ifdef ENABLE_CRYPTO dest->c1.ks.key_type = src->c1.ks.key_type; -#ifdef ENABLE_SSL /* inherit SSL context */ dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx; - dest->c1.ks.tls_auth_key = src->c1.ks.tls_auth_key; -#endif + dest->c1.ks.tls_wrap_key = src->c1.ks.tls_wrap_key; + dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type; + /* inherit pre-NCP ciphers */ + dest->c1.ciphername = src->c1.ciphername; + dest->c1.authname = src->c1.authname; + dest->c1.keysize = src->c1.keysize; #endif /* options */ @@ -3713,7 +3888,7 @@ inherit_context_top (struct context *dest, /* detach plugins */ dest->plugins_owned = false; -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO dest->c2.tls_multi = NULL; #endif @@ -3733,6 +3908,10 @@ inherit_context_top (struct context *dest, dest->c2.event_set = NULL; if (proto_is_dgram(src->options.ce.proto)) do_event_set_init (dest, false); + +#ifdef USE_COMP + dest->c2.comp_context = NULL; +#endif } void @@ -3747,8 +3926,11 @@ close_context (struct context *c, int sig, unsigned int flags) if (c->sig->signal_received == SIGUSR1) { if ((flags & CC_USR1_TO_HUP) - || (c->sig->hard && (flags & CC_HARD_USR1_TO_HUP))) - c->sig->signal_received = SIGHUP; + || (c->sig->source == SIG_SOURCE_HARD && (flags & CC_HARD_USR1_TO_HUP))) + { + c->sig->signal_received = SIGHUP; + c->sig->signal_text = "close_context usr1 to hup"; + } } if (!(flags & CC_NO_CLOSE)) @@ -3773,6 +3955,7 @@ test_crypto_thread (void *arg) ASSERT (options->test_crypto); init_verb_mute (c, IVM_LEVEL_1); context_init_1 (c); + next_connection_entry(c); do_init_crypto_static (c, 0); frame_finalize_options (c, options); @@ -3780,13 +3963,13 @@ test_crypto_thread (void *arg) test_crypto (&c->c2.crypto_options, &c->c2.frame); key_schedule_free (&c->c1.ks, true); - packet_id_free (&c->c2.packet_id); + packet_id_free (&c->c2.crypto_options.packet_id); context_gc_free (c); return NULL; } -#endif +#endif /* ENABLE_CRYPTO */ bool do_test_crypto (const struct options *o) diff --git a/src/openvpn/init.h b/src/openvpn/init.h index a819bd2..524bc64 100644 --- a/src/openvpn/init.h +++ b/src/openvpn/init.h @@ -81,7 +81,7 @@ bool do_test_crypto (const struct options *o); void context_gc_free (struct context *c); -void do_up (struct context *c, +bool do_up (struct context *c, bool pulled_options, unsigned int option_types_found); @@ -91,7 +91,7 @@ const char *format_common_name (struct context *c, struct gc_arena *gc); void reset_coarse_timers (struct context *c); -void do_deferred_options (struct context *c, const unsigned int found); +bool do_deferred_options (struct context *c, const unsigned int found); void inherit_context_child (struct context *dest, const struct context *src); diff --git a/src/openvpn/interval.h b/src/openvpn/interval.h index 4814ec9..59eb1f6 100644 --- a/src/openvpn/interval.h +++ b/src/openvpn/interval.h @@ -185,6 +185,15 @@ event_timeout_modify_wakeup (struct event_timeout* et, interval_t n) et->n = (n >= 0) ? n : 0; } +/* + * Will return the time left for a timeout, this function does not check + * if the timeout is actually valid + */ +static inline interval_t event_timeout_remaining (struct event_timeout* et) +{ + return (int) et->last + et->n - now; +} + /* * This is the principal function for testing and triggering recurring * timers and will return true on a timer signal event. diff --git a/src/openvpn/lzo.c b/src/openvpn/lzo.c index 195b819..25a839b 100644 --- a/src/openvpn/lzo.c +++ b/src/openvpn/lzo.c @@ -34,15 +34,14 @@ #include "syshead.h" -#ifdef ENABLE_LZO +#if defined(ENABLE_LZO) -#include "lzo.h" +#include "comp.h" #include "error.h" #include "otime.h" #include "memdbg.h" -#ifndef ENABLE_LZO_STUB /** * Perform adaptive compression housekeeping. * @@ -97,101 +96,69 @@ lzo_adaptive_compress_data (struct lzo_adaptive_compress *ac, int n_total, int n ac->n_comp += n_comp; } -#endif /* ENABLE_LZO_STUB */ - -void lzo_adjust_frame_parameters (struct frame *frame) -{ - /* Leave room for our one-byte compressed/didn't-compress prefix byte. */ - frame_add_to_extra_frame (frame, LZO_PREFIX_LEN); - - /* Leave room for compression buffer to expand in worst case scenario - where data is totally uncompressible */ - frame_add_to_extra_buffer (frame, LZO_EXTRA_BUFFER (EXPANDED_SIZE(frame))); -} - -void -lzo_compress_init (struct lzo_compress_workspace *lzowork, unsigned int flags) +static void +lzo_compress_init (struct compress_context *compctx) { - CLEAR (*lzowork); - - lzowork->flags = flags; -#ifndef ENABLE_LZO_STUB - lzowork->wmem_size = LZO_WORKSPACE; - + msg (D_INIT_MEDIUM, "LZO compression initializing"); + ASSERT(!(compctx->flags & COMP_F_SWAP)); + compctx->wu.lzo.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 (D_INIT_MEDIUM, "LZO compression initialized"); -#else - msg (D_INIT_MEDIUM, "LZO stub compression initialized"); -#endif - lzowork->defined = true; + compctx->wu.lzo.wmem = (lzo_voidp) lzo_malloc (compctx->wu.lzo.wmem_size); + check_malloc_return (compctx->wu.lzo.wmem); } -void -lzo_compress_uninit (struct lzo_compress_workspace *lzowork) +static void +lzo_compress_uninit (struct compress_context *compctx) { - if (lzowork) - { - ASSERT (lzowork->defined); -#ifndef ENABLE_LZO_STUB - lzo_free (lzowork->wmem); - lzowork->wmem = NULL; -#endif - lzowork->defined = false; - } + lzo_free (compctx->wu.lzo.wmem); + compctx->wu.lzo.wmem = NULL; } static inline bool -lzo_compression_enabled (struct lzo_compress_workspace *lzowork) +lzo_compression_enabled (struct compress_context *compctx) { -#ifndef ENABLE_LZO_STUB - if ((lzowork->flags & (LZO_SELECTED|LZO_ON)) == (LZO_SELECTED|LZO_ON)) + if (compctx->flags & COMP_F_ASYM) + return false; + else { - if (lzowork->flags & LZO_ADAPTIVE) - return lzo_adaptive_compress_test (&lzowork->ac); + if (compctx->flags & COMP_F_ADAPTIVE) + return lzo_adaptive_compress_test (&compctx->wu.lzo.ac); else return true; } -#endif - return false; } -void +static void lzo_compress (struct buffer *buf, struct buffer work, - struct lzo_compress_workspace *lzowork, + struct compress_context *compctx, const struct frame* frame) { -#ifndef ENABLE_LZO_STUB lzo_uint zlen = 0; int err; bool compressed = false; -#endif - - ASSERT (lzowork->defined); if (buf->len <= 0) return; -#ifndef ENABLE_LZO_STUB /* * In order to attempt compression, length must be at least COMPRESS_THRESHOLD, * and our adaptive level must give the OK. */ - if (buf->len >= COMPRESS_THRESHOLD && lzo_compression_enabled (lzowork)) + if (buf->len >= COMPRESS_THRESHOLD && lzo_compression_enabled (compctx)) { + const size_t ps = PAYLOAD_SIZE (frame); ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); - ASSERT (buf_safe (&work, LZO_EXTRA_BUFFER (PAYLOAD_SIZE (frame)))); + ASSERT (buf_safe (&work, ps + COMP_EXTRA_BUFFER (ps))); - if (!(buf->len <= PAYLOAD_SIZE (frame))) + if (buf->len > ps) { dmsg (D_COMP_ERRORS, "LZO compression buffer overflow"); buf->len = 0; return; } - err = LZO_COMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen, lzowork->wmem); + err = LZO_COMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen, compctx->wu.lzo.wmem); if (err != LZO_E_OK) { dmsg (D_COMP_ERRORS, "LZO compression error: %d", err); @@ -203,43 +170,38 @@ lzo_compress (struct buffer *buf, struct buffer work, work.len = zlen; compressed = true; - dmsg (D_COMP, "compress %d -> %d", buf->len, work.len); - lzowork->pre_compress += buf->len; - lzowork->post_compress += work.len; + dmsg (D_COMP, "LZO compress %d -> %d", buf->len, work.len); + compctx->pre_compress += buf->len; + compctx->post_compress += work.len; /* tell adaptive level about our success or lack thereof in getting any size reduction */ - if (lzowork->flags & LZO_ADAPTIVE) - lzo_adaptive_compress_data (&lzowork->ac, buf->len, work.len); + if (compctx->flags & COMP_F_ADAPTIVE) + lzo_adaptive_compress_data (&compctx->wu.lzo.ac, buf->len, work.len); } /* did compression save us anything ? */ if (compressed && work.len < buf->len) { uint8_t *header = buf_prepend (&work, 1); - *header = YES_COMPRESS; + *header = LZO_COMPRESS_BYTE; *buf = work; } else -#endif { uint8_t *header = buf_prepend (buf, 1); - *header = NO_COMPRESS; + *header = NO_COMPRESS_BYTE; } } -void +static void lzo_decompress (struct buffer *buf, struct buffer work, - struct lzo_compress_workspace *lzowork, + struct compress_context *compctx, const struct frame* frame) { -#ifndef ENABLE_LZO_STUB lzo_uint zlen = EXPANDED_SIZE (frame); int err; -#endif uint8_t c; /* flag indicating whether or not our peer compressed */ - ASSERT (lzowork->defined); - if (buf->len <= 0) return; @@ -248,12 +210,11 @@ lzo_decompress (struct buffer *buf, struct buffer work, c = *BPTR (buf); ASSERT (buf_advance (buf, 1)); - if (c == YES_COMPRESS) /* packet was compressed */ + if (c == LZO_COMPRESS_BYTE) /* packet was compressed */ { -#ifndef ENABLE_LZO_STUB ASSERT (buf_safe (&work, zlen)); err = LZO_DECOMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen, - lzowork->wmem); + compctx->wu.lzo.wmem); if (err != LZO_E_OK) { dmsg (D_COMP_ERRORS, "LZO decompression error: %d", err); @@ -264,18 +225,13 @@ lzo_decompress (struct buffer *buf, struct buffer work, ASSERT (buf_safe (&work, zlen)); work.len = zlen; - dmsg (D_COMP, "decompress %d -> %d", buf->len, work.len); - lzowork->pre_decompress += buf->len; - lzowork->post_decompress += work.len; + dmsg (D_COMP, "LZO decompress %d -> %d", buf->len, work.len); + compctx->pre_decompress += buf->len; + compctx->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 */ + else if (c == NO_COMPRESS_BYTE) /* packet was not compressed */ { ; } @@ -286,24 +242,13 @@ lzo_decompress (struct buffer *buf, struct buffer work, } } -void -lzo_modify_flags (struct lzo_compress_workspace *lzowork, unsigned int flags) -{ - ASSERT (lzowork->defined); - lzowork->flags = flags; -} - -void lzo_print_stats (const struct lzo_compress_workspace *lzo_compwork, struct status_output *so) -{ - ASSERT (lzo_compwork->defined); - -#ifndef ENABLE_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 -} +const struct compress_alg lzo_alg = { + "lzo", + lzo_compress_init, + lzo_compress_uninit, + lzo_compress, + lzo_decompress +}; #else static void dummy(void) {} diff --git a/src/openvpn/lzo.h b/src/openvpn/lzo.h index 472204d..f33e587 100644 --- a/src/openvpn/lzo.h +++ b/src/openvpn/lzo.h @@ -32,14 +32,13 @@ */ -#ifdef ENABLE_LZO +#if defined(ENABLE_LZO) /** * @addtogroup compression * @{ */ -#ifndef ENABLE_LZO_STUB #if defined(HAVE_LZO_LZOUTIL_H) #include "lzo/lzoutil.h" #elif defined(HAVE_LZOUTIL_H) @@ -50,28 +49,16 @@ #elif defined(HAVE_LZO1X_H) #include "lzo1x.h" #endif -#endif #include "buffer.h" #include "mtu.h" #include "common.h" #include "status.h" -/**************************************************************************/ -/** @name Bit-flags which control data channel packet compression *//******/ -/** @{ */ -#define LZO_SELECTED (1<<0) /**< Bit-flag indicating that compression - * of data channel packets is enabled. */ -#define LZO_ON (1<<1) /**< Bit-flag indicating that compression - * of data channel packets is active. */ -#define LZO_ADAPTIVE (1<<2) /**< Bit-flag indicating that adaptive - * compression of data channel packets - * has been selected. */ -/** @} name Bit-flags which control data channel packet compression *//****/ +extern const struct compress_alg lzo_alg; /**************************************************************************/ /** @name LZO library interface defines *//** @{ *//***********************/ -#ifndef ENABLE_LZO_STUB #define LZO_COMPRESS lzo1x_1_15_compress /**< LZO library compression function. * @@ -93,36 +80,11 @@ * verify the integrity of incoming * packets, you might want to consider * using the non-safe version. */ -#endif /* ENABLE_LZO_STUB */ /** @} name LZO library interface *//**************************************/ -/**************************************************************************/ -/** @name Miscellaneous compression defines *//** @{ *//*******************/ -#define LZO_EXTRA_BUFFER(len) ((len)/8 + 128 + 3) - /**< LZO 2.0 worst-case size expansion. */ -#ifndef ENABLE_LZO_STUB -#define COMPRESS_THRESHOLD 100 /**< Minimum packet size to attempt - * compression. */ -#endif /* ENABLE_LZO_STUB */ -/** @} name Miscellaneous compression defines *//**************************/ - - -/**************************************************************************/ -/** @name Compression header defines *//** @{ *//**************************/ -#define LZO_PREFIX_LEN 1 /**< Length in bytes of prepended - * compression header. */ -#define YES_COMPRESS 0x66 /**< Single-byte compression header - * indicating this packet has been - * compressed. */ -#define NO_COMPRESS 0xFA /**< Single-byte compression header - * indicating this packet has not been - * compressed. */ -/** @} name Compression header defines *//*********************************/ - /**************************************************************************/ /** @name Adaptive compression defines *//** @{ *//************************/ -#ifndef ENABLE_LZO_STUB #define AC_SAMP_SEC 2 /**< Number of seconds in a sample period. */ #define AC_MIN_BYTES 1000 /**< Minimum number of bytes a sample * period must contain for it to be @@ -132,11 +94,8 @@ * turned off. */ #define AC_OFF_SEC 60 /**< Seconds to wait after compression has * been turned off before retesting. */ -#endif /* ENABLE_LZO_STUB */ /** @} name Adaptive compression defines *//*******************************/ -#ifndef ENABLE_LZO_STUB - /** * Adaptive compression state. */ @@ -147,8 +106,6 @@ struct lzo_adaptive_compress { int n_comp; }; -#endif /* ENABLE_LZO_STUB */ - /** * State for the compression and decompression routines. @@ -162,186 +119,13 @@ struct lzo_adaptive_compress { */ struct lzo_compress_workspace { - bool defined; - unsigned int flags; -#ifndef ENABLE_LZO_STUB lzo_voidp wmem; int wmem_size; struct lzo_adaptive_compress ac; - - /* statistics */ - counter_type pre_decompress; - counter_type post_decompress; - counter_type pre_compress; - counter_type post_compress; -#endif }; - -/**************************************************************************/ -/** @name Functions for initialization and cleanup *//** @{ *//************/ - -/** - * Adjust %frame parameters for data channel payload compression. - * - * Data channel packet compression requires a single-byte header to - * indicate whether a packet has been compressed or not. The packet - * handling buffers must also allow for worst-case payload compression - * where the compressed content size is actually larger than the original - * content size. This function adjusts the parameters of a given frame - * structure to include the header and allow for worst-case compression - * expansion. - * - * @param frame - The frame structure to adjust. - */ -void lzo_adjust_frame_parameters(struct frame *frame); - -/** - * Initialize a compression workspace structure. - * - * This function initializes the given workspace structure \a lzowork. - * This includes allocating a work buffer for internal use and setting its - * flags to the given value of \a flags. - * - * This function also initializes the lzo library. - * - * @param lzowork - A pointer to the workspace structure to - * initialize. - * @param flags - The initial flags to set in the workspace - * structure. - */ -void lzo_compress_init (struct lzo_compress_workspace *lzowork, unsigned int flags); - -/** - * Cleanup a compression workspace structure. - * - * This function cleans up the given workspace structure \a lzowork. This - * includes freeing the structure's internal work buffer. - * - * @param lzowork - A pointer to the workspace structure to clean up. - */ -void lzo_compress_uninit (struct lzo_compress_workspace *lzowork); - -/** - * Set a workspace structure's flags. - * - * @param lzowork - The workspace structure of which to modify the - * flags. - * @param flags - The new value to assign to the workspace - * structure's flags. - */ -void lzo_modify_flags (struct lzo_compress_workspace *lzowork, unsigned int flags); - -/** @} name Functions for initialization and cleanup *//*******************/ - - -/**************************************************************************/ -/** @name Function for packets to be sent to a remote OpenVPN peer *//*****/ -/** @{ */ - -/** - * Process an outgoing packet according to a VPN tunnel's settings. - * @ingroup compression - * - * This function processes the packet contained in \a buf. Its behavior - * depends on the settings contained within \a lzowork. If compression is - * enabled and active, this function compresses the packet. After - * compression, the size of the uncompressed and compressed packets are - * compared, and the smallest is used. - * - * This function prepends a one-byte header indicating whether the packet - * was or was not compressed, so as to let the peer know how to handle the - * packet. - * - * If an error occurs during processing, an error message is logged and - * the length of \a buf is set to zero. - * - * @param buf - A pointer to the buffer containing the outgoing - * packet. This pointer will be modified to point - * to the processed packet on return. - * @param work - A preallocated working buffer. - * @param lzowork - The compression workspace structure associated - * with this VPN tunnel. - * @param frame - The frame parameters of this tunnel. - * - * @return Void.\n On return, \a buf will point to a buffer containing - * the processed, possibly compressed, packet data with a compression - * header prepended. - */ -void lzo_compress (struct buffer *buf, struct buffer work, - struct lzo_compress_workspace *lzowork, - const struct frame* frame); - -/** @} name Function for packets to be sent to a remote OpenVPN peer *//***/ - - -/**************************************************************************/ -/** @name Function for packets received from a remote OpenVPN peer *//*****/ -/** @{ */ - -/** - * Inspect an incoming packet and decompress if it is compressed. - * - * This function inspects the incoming packet contained in \a buf. If its - * one-byte compression header indicates that it was compressed (i.e. \c - * YES_COMPRESS), then it will be decompressed. If its header indicates - * that it was not compressed (i.e. \c NO_COMPRESS), then the buffer is - * not modified except for removing the compression header. - * - * If an error occurs during processing, for example if the compression - * header has a value other than \c YES_COMPRESS or \c NO_COMPRESS, then - * the error is logged and the length of \a buf is set to zero. - * - * @param buf - A pointer to the buffer containing the incoming - * packet. This pointer will be modified to point - * to the processed packet on return. - * @param work - A preallocated working buffer. - * @param lzowork - The compression workspace structure associated - * with this VPN tunnel. - * @param frame - The frame parameters of this tunnel. - * - * @return Void.\n On return, \a buf will point to a buffer containing - * the uncompressed packet data and the one-byte compression header - * will have been removed. - */ -void lzo_decompress (struct buffer *buf, struct buffer work, - struct lzo_compress_workspace *lzowork, - const struct frame* frame); - -/** @} name Function for packets received from a remote OpenVPN peer *//***/ - - -/**************************************************************************/ -/** @name Utility functions *//** @{ *//***********************************/ - -/** - * Print statistics on compression and decompression performance. - * - * @param lzo_compwork - The workspace structure from which to get the - * statistics. - * @param so - The status output structure to which to write the - * statistics. - */ -void lzo_print_stats (const struct lzo_compress_workspace *lzo_compwork, struct status_output *so); - -/** - * Check whether compression is enabled for a workspace structure. - * - * @param lzowork - The workspace structure to check. - * - * @return true if compression is enabled; false otherwise. - */ -static inline bool -lzo_defined (const struct lzo_compress_workspace *lzowork) -{ - return lzowork->defined; -} - -/** @} name Utility functions *//******************************************/ - - /** @} addtogroup compression */ -#endif /* ENABLE_LZO */ +#endif /* ENABLE_LZO && USE_COMP */ #endif diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 0a4542a..77a8006 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -113,6 +113,8 @@ man_help () #ifdef MANAGMENT_EXTERNAL_KEY msg (M_CLIENT, "rsa-sig : Enter an RSA signature in response to >RSA_SIGN challenge"); msg (M_CLIENT, " Enter signature base64 on subsequent lines followed by END"); + msg (M_CLIENT, "certificate : Enter a client certificate in response to >NEED-CERT challenge"); + msg (M_CLIENT, " Enter certificate base64 on subsequent lines followed by END"); #endif msg (M_CLIENT, "signal s : Send signal s to daemon,"); msg (M_CLIENT, " s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2."); @@ -268,7 +270,7 @@ man_delete_unix_socket (struct management *man) static void man_close_socket (struct management *man, const socket_descriptor_t sd) { -#ifndef WIN32 +#ifndef _WIN32 /* * Windows doesn't need this because the ne32 event is permanently * enabled at struct management scope. @@ -701,7 +703,7 @@ man_query_need_str (struct management *man, const char *type, const char *action static void man_forget_passwords (struct management *man) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO ssl_purge_auth (false); msg (M_CLIENT, "SUCCESS: Passwords were forgotten"); #endif @@ -868,6 +870,12 @@ in_extra_dispatch (struct management *man) man->connection.ext_key_input = man->connection.in_extra; man->connection.in_extra = NULL; return; + case IEC_CERTIFICATE: + man->connection.ext_cert_state = EKS_READY; + buffer_list_free (man->connection.ext_cert_input); + man->connection.ext_cert_input = man->connection.in_extra; + man->connection.in_extra = NULL; + return; #endif } in_extra_reset (&man->connection, IER_RESET); @@ -1030,6 +1038,20 @@ man_rsa_sig (struct management *man) msg (M_CLIENT, "ERROR: The rsa-sig command is not currently available"); } +static void +man_certificate (struct management *man) +{ + struct man_connection *mc = &man->connection; + if (mc->ext_cert_state == EKS_SOLICIT) + { + mc->ext_cert_state = EKS_INPUT; + mc->in_extra_cmd = IEC_CERTIFICATE; + in_extra_reset (mc, IER_NEW); + } + else + msg (M_CLIENT, "ERROR: The certificate command is not currently available"); +} + #endif static void @@ -1105,6 +1127,27 @@ man_remote (struct management *man, const char **p) } } +#ifdef TARGET_ANDROID +static void +man_network_change (struct management *man, bool samenetwork) +{ + /* Called to signal the OpenVPN that the network configuration has changed and + the client should either float or reconnect. + + The code is currently only used by ics-openvpn + */ + if (man->persist.callback.network_change) + { + int fd = (*man->persist.callback.network_change) + (man->persist.callback.arg, samenetwork); + man->connection.fdtosend = fd; + msg (M_CLIENT, "PROTECTFD: fd '%d' sent to be protected", fd); + if (fd == -2) + man_signal (man, "SIGUSR1"); + } +} +#endif + static void man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms) { @@ -1148,6 +1191,16 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch if (man_need (man, p, 1, 0)) man_signal (man, p[1]); } +#ifdef TARGET_ANDROID + else if (streq (p[0], "network-change")) + { + bool samenetwork = false; + if (p[1] && streq(p[1], "samenetwork")) + samenetwork = true; + + man_network_change(man, samenetwork); + } +#endif else if (streq (p[0], "load-stats")) { man_load_stats (man); @@ -1311,6 +1364,10 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch { man_rsa_sig (man); } + else if (streq (p[0], "certificate")) + { + man_certificate (man); + } #endif #ifdef ENABLE_PKCS11 else if (streq (p[0], "pkcs11-id-count")) @@ -1356,7 +1413,7 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch gc_free (&gc); } -#ifdef WIN32 +#ifdef _WIN32 static void man_start_ne32 (struct management *man) @@ -1446,7 +1503,7 @@ man_new_connection_post (struct management *man, const char *description) man_connection_settings_reset (man); -#ifdef WIN32 +#ifdef _WIN32 man_start_ne32 (man); #endif @@ -1461,7 +1518,7 @@ man_new_connection_post (struct management *man, const char *description) #endif msg (D_MANAGEMENT, "MANAGEMENT: %s %s", description, - print_sockaddr (&man->settings.local, &gc)); + print_sockaddr (man->settings.local->ai_addr, &gc)); buffer_list_reset (man->connection.out); @@ -1533,7 +1590,7 @@ man_accept (struct management *man) if (socket_defined (man->connection.sd_top)) { -#ifdef WIN32 +#ifdef _WIN32 man_stop_ne32 (man); #endif } @@ -1568,8 +1625,9 @@ man_listen (struct management *man) else #endif { - man->connection.sd_top = create_socket_tcp (AF_INET); - socket_bind (man->connection.sd_top, &man->settings.local, "MANAGEMENT"); + man->connection.sd_top = create_socket_tcp (man->settings.local); + socket_bind (man->connection.sd_top, man->settings.local, + man->settings.local->ai_family, "MANAGEMENT", false); } /* @@ -1593,10 +1651,10 @@ man_listen (struct management *man) else #endif msg (D_MANAGEMENT, "MANAGEMENT: TCP Socket listening on %s", - print_sockaddr (&man->settings.local, &gc)); + print_sockaddr (man->settings.local->ai_addr, &gc)); } -#ifdef WIN32 +#ifdef _WIN32 man_start_ne32 (man); #endif @@ -1634,9 +1692,9 @@ man_connect (struct management *man) else #endif { - man->connection.sd_cli = create_socket_tcp (AF_INET); + man->connection.sd_cli = create_socket_tcp (man->settings.local); status = openvpn_connect (man->connection.sd_cli, - &man->settings.local, + man->settings.local->ai_addr, 5, &signal_received); } @@ -1661,7 +1719,7 @@ man_connect (struct management *man) #endif msg (D_LINK_ERRORS, "MANAGEMENT: connect to %s failed: %s", - print_sockaddr (&man->settings.local, &gc), + print_sockaddr (man->settings.local->ai_addr, &gc), strerror_ts (status, &gc)); throw_signal_soft (SIGTERM, "management-connect-failed"); goto done; @@ -1679,7 +1737,7 @@ man_reset_client_socket (struct management *man, const bool exiting) { if (socket_defined (man->connection.sd_cli)) { -#ifdef WIN32 +#ifdef _WIN32 man_stop_ne32 (man); #endif man_close_socket (man, man->connection.sd_cli); @@ -1694,7 +1752,7 @@ man_reset_client_socket (struct management *man, const bool exiting) } if (!exiting) { -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO if (man->settings.flags & MF_FORGET_DISCONNECT) ssl_purge_auth (false); #endif @@ -1779,6 +1837,120 @@ man_io_error (struct management *man, const char *prefix) return false; } +#ifdef TARGET_ANDROID +static ssize_t man_send_with_fd (int fd, void *ptr, size_t nbytes, int flags, int sendfd) +{ + struct msghdr msg; + struct iovec iov[1]; + + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *((int *) CMSG_DATA(cmptr)) = sendfd; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + return (sendmsg(fd, &msg, flags)); +} + +static ssize_t man_recv_with_fd (int fd, void *ptr, size_t nbytes, int flags, int *recvfd) +{ + struct msghdr msghdr; + struct iovec iov[1]; + ssize_t n; + + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof (int))]; + } control_un; + struct cmsghdr *cmptr; + + msghdr.msg_control = control_un.control; + msghdr.msg_controllen = sizeof(control_un.control); + + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = 1; + + if ( (n = recvmsg(fd, &msghdr, flags)) <= 0) + return (n); + + if ( (cmptr = CMSG_FIRSTHDR(&msghdr)) != NULL && + cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmptr->cmsg_level != SOL_SOCKET) + msg (M_ERR, "control level != SOL_SOCKET"); + if (cmptr->cmsg_type != SCM_RIGHTS) + msg (M_ERR, "control type != SCM_RIGHTS"); + *recvfd = *((int *) CMSG_DATA(cmptr)); + } else + *recvfd = -1; /* descriptor was not passed */ + + return (n); +} + +/* + * The android control method will instruct the GUI part of openvpn to do + * the route/ifconfig/open tun command. See doc/android.txt for details. + */ +bool management_android_control (struct management *man, const char *command, const char *msg) +{ + struct user_pass up; + CLEAR(up); + strncpy (up.username, msg, sizeof(up.username)-1); + + management_query_user_pass(management, &up , command, GET_USER_PASS_NEED_OK,(void*) 0); + return strcmp ("ok", up.password)==0; +} + +/* + * In Android 4.4 it is not possible to open a new tun device and then close the + * old tun device without breaking the whole VPNService stack until the device + * is rebooted. This management method ask the UI what method should be taken to + * ensure the optimal solution for the situation + */ +int managment_android_persisttun_action (struct management *man) +{ + struct user_pass up; + CLEAR(up); + strcpy(up.username,"tunmethod"); + management_query_user_pass(management, &up , "PERSIST_TUN_ACTION", + GET_USER_PASS_NEED_OK,(void*) 0); + if (!strcmp("NOACTION", up.password)) + return ANDROID_KEEP_OLD_TUN; + else if (!strcmp ("OPEN_AFTER_CLOSE", up.password)) + return ANDROID_OPEN_AFTER_CLOSE; + else if (!strcmp ("OPEN_BEFORE_CLOSE", up.password)) + return ANDROID_OPEN_BEFORE_CLOSE; + else + msg (M_ERR, "Got unrecognised '%s' from management for PERSIST_TUN_ACTION query", up.password); + + ASSERT(0); + return ANDROID_OPEN_AFTER_CLOSE; +} + + +#endif + static int man_read (struct management *man) { @@ -1788,7 +1960,15 @@ man_read (struct management *man) unsigned char buf[256]; int len = 0; +#ifdef TARGET_ANDROID + int fd; + len = man_recv_with_fd (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL, &fd); + if(fd >= 0) + man->connection.lastfdreceived = fd; +#else len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL); +#endif + if (len == 0) { man_reset_client_socket (man, false); @@ -1865,6 +2045,13 @@ man_write (struct management *man) if (buf && BLEN (buf)) { const int len = min_int (size_hint, BLEN (buf)); +#ifdef TARGET_ANDROID + if (man->connection.fdtosend > 0) + { + sent = man_send_with_fd (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL,man->connection.fdtosend); + man->connection.fdtosend = -1; + } else +#endif sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL); if (sent >= 0) { @@ -1957,7 +2144,7 @@ man_persist_close (struct man_persist *mp) static void man_settings_init (struct man_settings *ms, const char *addr, - const int port, + const char *port, const char *pass_file, const char *client_user, const char *client_group, @@ -2010,12 +2197,6 @@ man_settings_init (struct man_settings *ms, else #endif { - /* - * Initialize socket address - */ - 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 @@ -2027,8 +2208,15 @@ man_settings_init (struct man_settings *ms, } else { - ms->local.addr.in4.sin_addr.s_addr = getaddr - (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL); + int status; + int resolve_flags = GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL; + + if (! (flags & MF_CONNECT_AS_CLIENT)) + resolve_flags |= GETADDR_PASSIVE; + + status = openvpn_getaddrinfo (resolve_flags, addr, port, 0, + NULL, AF_UNSPEC, &ms->local); + ASSERT(status==0); } } @@ -2054,6 +2242,8 @@ man_settings_init (struct man_settings *ms, static void man_settings_close (struct man_settings *ms) { + if (ms->local) + freeaddrinfo(ms->local); free (ms->write_peer_info_file); CLEAR (*ms); } @@ -2064,7 +2254,7 @@ man_connection_init (struct management *man) { if (man->connection.state == MS_INITIAL) { -#ifdef WIN32 +#ifdef _WIN32 /* * This object is a sort of TCP/IP helper * for Windows. @@ -2105,7 +2295,7 @@ man_connection_close (struct management *man) if (mc->es) event_free (mc->es); -#ifdef WIN32 +#ifdef _WIN32 net_event_win32_close (&mc->ne32); #endif if (socket_defined (mc->sd_top)) @@ -2147,7 +2337,7 @@ management_init (void) bool management_open (struct management *man, const char *addr, - const int port, + const char *port, const char *pass_file, const char *client_user, const char *client_group, @@ -2232,8 +2422,10 @@ void management_set_state (struct management *man, const int state, const char *detail, - const in_addr_t tun_local_ip, - const in_addr_t tun_remote_ip) + const in_addr_t *tun_local_ip, + const struct in6_addr *tun_local_ip6, + const struct openvpn_sockaddr *local, + const struct openvpn_sockaddr *remote) { if (man->persist.state && (!(man->settings.flags & MF_SERVER) || state < OPENVPN_STATE_CLIENT_BASE)) { @@ -2246,9 +2438,15 @@ management_set_state (struct management *man, e.timestamp = now; e.u.state = state; e.string = detail; - e.local_ip = tun_local_ip; - e.remote_ip = tun_remote_ip; - + if (tun_local_ip) + e.local_ip = *tun_local_ip; + if (tun_local_ip6) + e.local_ip6 = *tun_local_ip6; + if (local) + e.local_sock = *local; + if (remote) + e.remote_sock = *remote; + log_history_add (man->persist.state, &e); if (man->connection.state_realtime) @@ -2361,33 +2559,6 @@ management_notify_generic (struct management *man, const char *str) #ifdef MANAGEMENT_DEF_AUTH -static bool -validate_peer_info_line(const char *line) -{ - uint8_t c; - int state = 0; - while ((c=*line++)) - { - switch (state) - { - case 0: - case 1: - if (c == '=' && state == 1) - state = 2; - else if (isalnum(c) || c == '_') - state = 1; - else - return false; - case 2: - if (isprint(c)) - ; - else - return false; - } - } - return (state == 2); -} - static void man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac) { @@ -2426,7 +2597,8 @@ management_notify_client_needing_auth (struct management *management, mode = "REAUTH"; msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id); man_output_extra_env (management, "CLIENT"); - man_output_peer_info_env(management, mdac); + if (management->connection.env_filter_level>0) + man_output_peer_info_env(management, mdac); man_output_env (es, true, management->connection.env_filter_level, "CLIENT"); mdac->flags |= DAF_INITIAL_AUTH; } @@ -2513,7 +2685,13 @@ 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.addr.in4.sin_addr.s_addr = htonl (tun_local_ip); + struct in_addr ia; + int ret; + + ia.s_addr = htonl(tun_local_ip); + ret = openvpn_getaddrinfo(GETADDR_PASSIVE, inet_ntoa(ia), NULL, 0, NULL, + AF_INET, &man->settings.local); + ASSERT (ret==0); man_connection_init (man); } @@ -2553,7 +2731,7 @@ man_persist_state (unsigned int *persistent, const int n) return true; } -#ifdef WIN32 +#ifdef _WIN32 void management_socket_set (struct management *man, @@ -2845,7 +3023,8 @@ management_event_loop_n_seconds (struct management *man, int sec) man_check_for_signals (&signal_received); if (signal_received) return; - } while (expire); + update_time(); + } while (expire && expire > now); /* revert state */ man->persist.standalone_disabled = standalone_disabled_save; @@ -2984,15 +3163,14 @@ management_query_user_pass (struct management *man, #ifdef MANAGMENT_EXTERNAL_KEY -char * /* returns allocated base64 signature */ -management_query_rsa_sig (struct management *man, - const char *b64_data) +int +management_query_multiline (struct management *man, + const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) { struct gc_arena gc = gc_new (); - char *ret = NULL; + int ret = 0; volatile int signal_received = 0; struct buffer alert_msg = clear_buf(); - struct buffer *buf; const bool standalone_disabled_save = man->persist.standalone_disabled; struct man_connection *mc = &man->connection; @@ -3001,10 +3179,15 @@ management_query_rsa_sig (struct management *man, man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */ man->persist.special_state_msg = NULL; - mc->ext_key_state = EKS_SOLICIT; + *state = EKS_SOLICIT; - alert_msg = alloc_buf_gc (strlen(b64_data)+64, &gc); - buf_printf (&alert_msg, ">RSA_SIGN:%s", b64_data); + if (b64_data) { + alert_msg = alloc_buf_gc (strlen(b64_data)+strlen(prompt)+3, &gc); + buf_printf (&alert_msg, ">%s:%s", prompt, b64_data); + } else { + alert_msg = alloc_buf_gc (strlen(prompt)+3, &gc); + buf_printf (&alert_msg, ">%s", prompt); + } man_wait_for_client_connection (man, &signal_received, 0, MWCC_OTHER_WAIT); @@ -3022,40 +3205,107 @@ management_query_rsa_sig (struct management *man, man_check_for_signals (&signal_received); if (signal_received) goto done; - } while (mc->ext_key_state != EKS_READY); + } while (*state != EKS_READY); - if (buffer_list_defined(mc->ext_key_input)) - { - buffer_list_aggregate (mc->ext_key_input, 2048); - buf = buffer_list_peek (mc->ext_key_input); - if (buf && BLEN(buf) > 0) - { - ret = (char *) malloc(BLEN(buf)+1); - check_malloc_return(ret); - memcpy(ret, buf->data, BLEN(buf)); - ret[BLEN(buf)] = '\0'; - } - } + ret = 1; } done: - if (mc->ext_key_state == EKS_READY && ret) - msg (M_CLIENT, "SUCCESS: rsa-sig command succeeded"); - else if (mc->ext_key_state == EKS_INPUT || mc->ext_key_state == EKS_READY) - msg (M_CLIENT, "ERROR: rsa-sig command failed"); + if (*state == EKS_READY && ret) + msg (M_CLIENT, "SUCCESS: %s command succeeded", cmd); + else if (*state == EKS_INPUT || *state == EKS_READY) + msg (M_CLIENT, "ERROR: %s command failed", cmd); /* revert state */ man->persist.standalone_disabled = standalone_disabled_save; man->persist.special_state_msg = NULL; in_extra_reset (mc, IER_RESET); - mc->ext_key_state = EKS_UNDEF; - buffer_list_free (mc->ext_key_input); - mc->ext_key_input = NULL; + *state = EKS_UNDEF; gc_free (&gc); return ret; } +char * /* returns allocated base64 signature */ +management_query_multiline_flatten_newline (struct management *man, + const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) +{ + int ok; + char *result = NULL; + struct buffer *buf; + + ok = management_query_multiline(man, b64_data, prompt, cmd, state, input); + if (ok && buffer_list_defined(*input)) + { + buffer_list_aggregate_separator (*input, 10000, "\n"); + buf = buffer_list_peek (*input); + if (buf && BLEN(buf) > 0) + { + result = (char *) malloc(BLEN(buf)+1); + check_malloc_return(result); + memcpy(result, buf->data, BLEN(buf)); + result[BLEN(buf)] = '\0'; + } + } + + buffer_list_free (*input); + *input = NULL; + + return result; +} + +char * /* returns allocated base64 signature */ +management_query_multiline_flatten (struct management *man, + const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) +{ + int ok; + char *result = NULL; + struct buffer *buf; + + ok = management_query_multiline(man, b64_data, prompt, cmd, state, input); + if (ok && buffer_list_defined(*input)) + { + buffer_list_aggregate (*input, 2048); + buf = buffer_list_peek (*input); + if (buf && BLEN(buf) > 0) + { + result = (char *) malloc(BLEN(buf)+1); + check_malloc_return(result); + memcpy(result, buf->data, BLEN(buf)); + result[BLEN(buf)] = '\0'; + } + } + + buffer_list_free (*input); + *input = NULL; + + return result; +} + +char * /* returns allocated base64 signature */ +management_query_rsa_sig (struct management *man, + const char *b64_data) +{ + return management_query_multiline_flatten(man, b64_data, "RSA_SIGN", "rsa-sign", + &man->connection.ext_key_state, &man->connection.ext_key_input); +} + + +char* management_query_cert (struct management *man, const char *cert_name) +{ + const char prompt_1[] = "NEED-CERTIFICATE:"; + struct buffer buf_prompt = alloc_buf(strlen(cert_name) + 20); + buf_write(&buf_prompt, prompt_1, strlen(prompt_1)); + buf_write(&buf_prompt, cert_name, strlen(cert_name)+1); // +1 for \0 + + char *result; + result = management_query_multiline_flatten_newline(management, + NULL, (char*)buf_bptr(&buf_prompt), "certificate", + &man->connection.ext_cert_state, &man->connection.ext_cert_input); + free_buf(&buf_prompt); + return result; +} + #endif /* @@ -3082,12 +3332,13 @@ management_should_daemonize (struct management *man) * Return true if the caller should not sleep for an additional time interval. */ bool -management_hold (struct management *man) +management_hold (struct management *man, int holdtime) { if (management_would_hold (man)) { volatile int signal_received = 0; const bool standalone_disabled_save = man->persist.standalone_disabled; + struct gc_arena gc = gc_new (); man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */ man->persist.special_state_msg = NULL; @@ -3097,7 +3348,9 @@ management_hold (struct management *man) if (!signal_received) { - man->persist.special_state_msg = ">HOLD:Waiting for hold release"; + struct buffer out = alloc_buf_gc (128, &gc); + buf_printf (&out, ">HOLD:Waiting for hold release:%d", holdtime); + man->persist.special_state_msg = BSTR (&out); msg (M_CLIENT, "%s", man->persist.special_state_msg); /* run command processing event loop until we get our username/password */ @@ -3116,6 +3369,7 @@ management_hold (struct management *man) man->persist.special_state_msg = NULL; man->settings.mansig &= ~MANSIG_IGNORE_USR1_HUP; + gc_free (&gc); return true; } return false; @@ -3218,7 +3472,14 @@ log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena if (flags & LOG_PRINT_LOCAL_IP) buf_printf (&out, ",%s", print_in_addr_t (e->local_ip, IA_EMPTY_IF_UNDEF, gc)); if (flags & LOG_PRINT_REMOTE_IP) - buf_printf (&out, ",%s", print_in_addr_t (e->remote_ip, IA_EMPTY_IF_UNDEF, gc)); + { + buf_printf (&out, ",%s", (!addr_defined (&e->remote_sock) ? "," : + print_sockaddr_ex (&e->remote_sock.addr.sa, ",", PS_DONT_SHOW_FAMILY|PS_SHOW_PORT, gc))); + buf_printf (&out, ",%s", (!addr_defined (&e->local_sock) ? "," : + print_sockaddr_ex (&e->local_sock.addr.sa, ",", PS_DONT_SHOW_FAMILY|PS_SHOW_PORT, gc))); + } + if (flags & LOG_PRINT_LOCAL_IP && !IN6_IS_ADDR_UNSPECIFIED(&e->local_ip6)) + buf_printf (&out, ",%s", print_in6_addr (e->local_ip6, IA_EMPTY_IF_UNDEF, gc)); if (flags & LOG_ECHO_TO_LOG) msg (D_MANAGEMENT, "MANAGEMENT: %s", BSTR (&out)); if (flags & LOG_PRINT_CRLF) diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index 28da69f..3ffced0 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -88,7 +88,9 @@ struct log_entry time_t timestamp; const char *string; in_addr_t local_ip; - in_addr_t remote_ip; + struct in6_addr local_ip6; + struct openvpn_sockaddr local_sock; + struct openvpn_sockaddr remote_sock; union log_entry_union u; }; @@ -173,6 +175,9 @@ struct management_callback #endif bool (*proxy_cmd) (void *arg, const char **p); bool (*remote_cmd) (void *arg, const char **p); +#ifdef TARGET_ANDROID + int (*network_change) (void *arg, bool samenetwork); +#endif }; /* @@ -212,7 +217,7 @@ struct man_persist { struct man_settings { bool defined; unsigned int flags; /* MF_x flags */ - struct openvpn_sockaddr local; + struct addrinfo* local; #if UNIX_SOCK_SUPPORT struct sockaddr_un local_unix; #endif @@ -252,7 +257,7 @@ struct man_connection { socket_descriptor_t sd_cli; struct openvpn_sockaddr remote; -#ifdef WIN32 +#ifdef _WIN32 struct net_event_win32 ne32; #endif @@ -268,6 +273,7 @@ struct man_connection { # define IEC_CLIENT_AUTH 1 # define IEC_CLIENT_PF 2 # define IEC_RSA_SIGN 3 +# define IEC_CERTIFICATE 4 int in_extra_cmd; struct buffer_list *in_extra; #ifdef MANAGEMENT_DEF_AUTH @@ -281,6 +287,8 @@ struct man_connection { # define EKS_READY 3 int ext_key_state; struct buffer_list *ext_key_input; + int ext_cert_state; + struct buffer_list *ext_cert_input; #endif #endif struct event_set *es; @@ -299,6 +307,10 @@ struct man_connection { #ifdef MANAGMENT_EXTERNAL_KEY struct buffer_list *rsa_sig; #endif +#ifdef TARGET_ANDROID + int fdtosend; + int lastfdreceived; +#endif }; struct management @@ -334,10 +346,11 @@ struct management *management_init (void); #define MF_UP_DOWN (1<<10) #define MF_QUERY_REMOTE (1<<11) #define MF_QUERY_PROXY (1<<12) +#define MF_EXTERNAL_CERT (1<<13) bool management_open (struct management *man, const char *addr, - const int port, + const char *port, const char *pass_file, const char *client_user, const char *client_group, @@ -372,9 +385,18 @@ bool management_query_user_pass (struct management *man, const unsigned int flags, const char *static_challenge); +#ifdef TARGET_ANDROID +bool management_android_control (struct management *man, const char *command, const char *msg); + +#define ANDROID_KEEP_OLD_TUN 1 +#define ANDROID_OPEN_AFTER_CLOSE 2 +#define ANDROID_OPEN_BEFORE_CLOSE 3 +int managment_android_persisttun_action (struct management *man); +#endif + bool management_should_daemonize (struct management *man); bool management_would_hold (struct management *man); -bool management_hold (struct management *man); +bool management_hold (struct management *man, int holdtime); void management_event_loop_n_seconds (struct management *man, int sec); @@ -407,6 +429,7 @@ void management_learn_addr (struct management *management, #ifdef MANAGMENT_EXTERNAL_KEY char *management_query_rsa_sig (struct management *man, const char *b64_data); +char* management_query_cert (struct management *man, const char *cert_name); #endif @@ -475,8 +498,10 @@ management_enable_def_auth (const struct management *man) void management_set_state (struct management *man, const int state, const char *detail, - const in_addr_t tun_local_ip, - const in_addr_t tun_remote_ip); + const in_addr_t *tun_local_ip, + const struct in6_addr *tun_local_ip6, + const struct openvpn_sockaddr *local_addr, + const struct openvpn_sockaddr *remote_addr); /* * The management object keeps track of OpenVPN --echo diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index 48ca0d5..56d43e0 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -6,6 +6,8 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. + * Copyright (C) 2014-2015 David Sommerseth + * Copyright (C) 2016 David Sommerseth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -62,7 +64,7 @@ run_up_down (const char *command, const struct plugin_list *plugins, int plugin_type, const char *arg, -#ifdef WIN32 +#ifdef _WIN32 DWORD adapter_index, #endif const char *dev_type, @@ -85,7 +87,7 @@ run_up_down (const char *command, setenv_str (es, "dev", arg); if (dev_type) setenv_str (es, "dev_type", dev_type); -#ifdef WIN32 +#ifdef _WIN32 setenv_int (es, "dev_idx", adapter_index); #endif @@ -118,13 +120,9 @@ run_up_down (const char *command, struct argv argv = argv_new (); ASSERT (arg); setenv_str (es, "script_type", script_type); - argv_printf (&argv, - "%sc %s %d %d %s %s %s", - command, - arg, - tun_mtu, link_mtu, - ifconfig_local, ifconfig_remote, - context); + argv_parse_cmd (&argv, command); + argv_printf_cat (&argv, "%s %d %d %s %s %s", arg, tun_mtu, link_mtu, + ifconfig_local, ifconfig_remote, context); argv_msg (M_INFO, &argv); openvpn_run_script (&argv, es, S_FATAL, "--up/--down"); argv_reset (&argv); @@ -191,31 +189,6 @@ save_inetd_socket_descriptor (void) #endif } -/* - * Warn if a given file is group/others accessible. - */ -void -warn_if_group_others_accessible (const char* filename) -{ -#ifndef WIN32 -#ifdef HAVE_STAT - if (strcmp (filename, INLINE_FILE_TAG)) - { - struct stat st; - if (stat (filename, &st)) - { - msg (M_WARN | M_ERRNO, "WARNING: cannot stat file '%s'", filename); - } - else - { - if (st.st_mode & (S_IRWXG|S_IRWXO)) - msg (M_WARN, "WARNING: file '%s' is group or others accessible", filename); - } - } -#endif -#endif -} - /* * Print an error message based on the status code returned by system(). */ @@ -223,7 +196,7 @@ const char * system_error_message (int stat, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (256, gc); -#ifdef WIN32 +#ifdef _WIN32 if (stat == -1) buf_printf (&out, "external program did not execute -- "); buf_printf (&out, "returned error code %d", stat); @@ -279,7 +252,7 @@ openvpn_execve_allowed (const unsigned int flags) } -#ifndef WIN32 +#ifndef _WIN32 /* * Run execve() inside a fork(). Designed to replicate the semantics of system() but * in a safer way that doesn't require the invocation of a shell or the risks @@ -362,27 +335,29 @@ openvpn_popen (const struct argv *a, const struct env_set *es) pid = fork (); if (pid == (pid_t)0) /* child side */ { - close (pipe_stdout[0]); + close (pipe_stdout[0]); /* Close read end */ dup2 (pipe_stdout[1],1); execve (cmd, argv, envp); exit (127); } - else if (pid < (pid_t)0) /* fork failed */ - { - msg (M_ERR, "openvpn_popen: unable to fork"); - } - else /* parent side */ + else if (pid > (pid_t)0) /* parent side */ { int status = 0; + close (pipe_stdout[1]); /* Close write end */ waitpid(pid, &status, 0); ret = pipe_stdout[0]; + } + else /* fork failed */ + { + close (pipe_stdout[0]); close (pipe_stdout[1]); + msg (M_ERR, "openvpn_popen: unable to fork %s", cmd); } } else { - msg (M_WARN, "openvpn_popen: unable to create stdout pipe"); - ret = -1; + msg (M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd); + ret = -1; } } else if (!warn_shown && (script_security < SSEC_SCRIPTS)) @@ -611,6 +586,16 @@ env_set_add (struct env_set *es, const char *str) env_set_add_nolock (es, str); } +const char* +env_set_get (const struct env_set *es, const char *name) +{ + const struct env_item *item = es->list; + while (item && !env_string_equal(item->string, name)) { + item = item->next; + } + return item ? item->string : NULL; +} + void env_set_print (int msglevel, const struct env_set *es) { @@ -701,14 +686,6 @@ env_set_remove_from_environment (const struct env_set *es) } } -#ifdef HAVE_PUTENV - -/* companion functions to putenv */ - -static struct env_item *global_env = NULL; /* GLOBAL */ - -#endif - /* add/modify/delete environmental strings */ void @@ -753,6 +730,28 @@ setenv_str_safe (struct env_set *es, const char *name, const char *value) msg (M_WARN, "setenv_str_safe: name overflow"); } +void setenv_str_incr(struct env_set *es, const char *name, const char *value) +{ + unsigned int counter = 1; + const size_t tmpname_len = strlen(name) + 5; /* 3 digits counter max */ + char *tmpname = gc_malloc(tmpname_len, true, NULL); + strcpy(tmpname, name); + while (NULL != env_set_get(es, tmpname) && counter < 1000) + { + ASSERT (openvpn_snprintf (tmpname, tmpname_len, "%s_%u", name, counter)); + counter++; + } + if (counter < 1000) + { + setenv_str (es, tmpname, value); + } + else + { + msg (D_TLS_DEBUG_MED, "Too many same-name env variables, ignoring: %s", name); + } + free (tmpname); +} + void setenv_del (struct env_set *es, const char *name) { @@ -830,32 +829,6 @@ setenv_str_i (struct env_set *es, const char *name, const char *value, const int gc_free (&gc); } -/* - * taken from busybox networking/ifupdown.c - */ -unsigned int -count_bits(unsigned int a) -{ - unsigned int result; - result = (a & 0x55) + ((a >> 1) & 0x55); - result = (result & 0x33) + ((result >> 2) & 0x33); - return((result & 0x0F) + ((result >> 4) & 0x0F)); -} - -int -count_netmask_bits(const char *dotted_quad) -{ - unsigned int result, a, b, c, d; - /* Found a netmask... Check if it is dotted quad */ - if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) - return -1; - result = count_bits(a); - result += count_bits(b); - result += count_bits(c); - result += count_bits(d); - return ((int)result); -} - /* return true if filename can be opened for read */ bool test_file (const char *filename) @@ -977,7 +950,7 @@ hostname_randomize(const char *hostname, struct gc_arena *gc) const char * gen_path (const char *directory, const char *filename, struct gc_arena *gc) { -#if WIN32 +#ifdef _WIN32 const int CC_PATH_RESERVED = CC_LESS_THAN|CC_GREATER_THAN|CC_COLON| CC_DOUBLE_QUOTE|CC_SLASH|CC_BACKSLASH|CC_PIPE|CC_QUESTION_MARK|CC_ASTERISK; #else @@ -988,7 +961,7 @@ gen_path (const char *directory, const char *filename, struct gc_arena *gc) if (safe_filename && strcmp (safe_filename, ".") && strcmp (safe_filename, "..") -#ifdef WIN32 +#ifdef _WIN32 && win_safe_filename (safe_filename) #endif ) @@ -1016,7 +989,7 @@ absolute_pathname (const char *pathname) if (pathname) { const int c = pathname[0]; -#ifdef WIN32 +#ifdef _WIN32 return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\'); #else return c == '/'; @@ -1085,13 +1058,23 @@ get_user_pass_cr (struct user_pass *up, struct buffer user_prompt = alloc_buf_gc (128, &gc); buf_printf (&user_prompt, "NEED-OK|%s|%s:", prefix, up->username); - - if (!get_console_input (BSTR (&user_prompt), true, up->password, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); + if (!query_user_SINGLE (BSTR(&user_prompt), BLEN(&user_prompt), + up->password, USER_PASS_LEN, false)) + { + msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); + } if (!strlen (up->password)) strcpy (up->password, "ok"); } + else if (flags & GET_USER_PASS_INLINE_CREDS) + { + struct buffer buf; + buf_set_read (&buf, (uint8_t*) auth_file, strlen (auth_file) + 1); + if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) + buf_parse (&buf, '\n', up->username, USER_PASS_LEN); + buf_parse (&buf, '\n', up->password, USER_PASS_LEN); + } /* * Read from auth file unless this is a dynamic challenge request. */ @@ -1103,8 +1086,6 @@ get_user_pass_cr (struct user_pass *up, FILE *fp; char password_buf[USER_PASS_LEN] = { '\0' }; - warn_if_group_others_accessible (auth_file); - fp = platform_fopen (auth_file, "r"); if (!fp) msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file); @@ -1155,13 +1136,17 @@ get_user_pass_cr (struct user_pass *up, if (ac) { char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); - struct buffer packed_resp; + struct buffer packed_resp, challenge; + challenge = alloc_buf_gc (14+strlen(ac->challenge_text), &gc); + buf_printf (&challenge, "CHALLENGE: %s", ac->challenge_text); buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN); - msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", ac->challenge_text); - if (!get_console_input (ac->challenge_text, BOOL_CAST(ac->flags&CR_ECHO), - response, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read challenge response from stdin"); + + if (!query_user_SINGLE (BSTR(&challenge), BLEN(&challenge), + response, USER_PASS_LEN, BOOL_CAST(ac->flags&CR_ECHO))) + { + msg (M_FATAL, "ERROR: could not read challenge response from stdin"); + } strncpynt (up->username, ac->user, USER_PASS_LEN); buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response); } @@ -1176,32 +1161,49 @@ get_user_pass_cr (struct user_pass *up, struct buffer user_prompt = alloc_buf_gc (128, &gc); struct buffer pass_prompt = alloc_buf_gc (128, &gc); + query_user_clear (); buf_printf (&user_prompt, "Enter %s Username:", prefix); buf_printf (&pass_prompt, "Enter %s Password:", prefix); if (username_from_stdin && !(flags & GET_USER_PASS_PASSWORD_ONLY)) { - if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); + query_user_add (BSTR(&user_prompt), BLEN(&user_prompt), + up->username, USER_PASS_LEN, true); + } + + if (password_from_stdin) + { + query_user_add (BSTR(&pass_prompt), BLEN(&pass_prompt), + up->password, USER_PASS_LEN, false); + } + + if( !query_user_exec () ) + { + msg(M_FATAL, "ERROR: Failed retrieving username or password"); + } + + if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) + { if (strlen (up->username) == 0) msg (M_FATAL, "ERROR: %s username is empty", prefix); } - if (password_from_stdin && !get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); - #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE) && response_from_stdin) { char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); - struct buffer packed_resp; + struct buffer packed_resp, challenge; char *pw64=NULL, *resp64=NULL; - msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", auth_challenge); + challenge = alloc_buf_gc (14+strlen(auth_challenge), &gc); + buf_printf (&challenge, "CHALLENGE: %s", auth_challenge); - if (!get_console_input (auth_challenge, BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), - response, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read static challenge response from stdin"); + if (!query_user_SINGLE (BSTR(&challenge), BLEN(&challenge), + response, USER_PASS_LEN, + BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO))) + { + msg (M_FATAL, "ERROR: could not retrieve static challenge response"); + } if (openvpn_base64_encode(up->password, strlen(up->password), &pw64) == -1 || openvpn_base64_encode(response, strlen(response), &resp64) == -1) msg (M_FATAL, "ERROR: could not base64-encode password/static_response"); @@ -1554,465 +1556,6 @@ adjust_power_of_2 (size_t u) return ret; } -/* - * A printf-like function (that only recognizes a subset of standard printf - * format operators) that prints arguments to an argv list instead - * of a standard string. This is used to build up argv arrays for passing - * to execve. - */ - -void -argv_init (struct argv *a) -{ - a->capacity = 0; - a->argc = 0; - a->argv = NULL; - a->system_str = NULL; -} - -struct argv -argv_new (void) -{ - struct argv ret; - argv_init (&ret); - return ret; -} - -void -argv_reset (struct argv *a) -{ - size_t i; - for (i = 0; i < a->argc; ++i) - free (a->argv[i]); - free (a->argv); - free (a->system_str); - argv_init (a); -} - -static void -argv_extend (struct argv *a, const size_t newcap) -{ - if (newcap > a->capacity) - { - char **newargv; - size_t i; - ALLOC_ARRAY_CLEAR (newargv, char *, newcap); - for (i = 0; i < a->argc; ++i) - newargv[i] = a->argv[i]; - free (a->argv); - a->argv = newargv; - a->capacity = newcap; - } -} - -static void -argv_grow (struct argv *a, const size_t add) -{ - const size_t newargc = a->argc + add + 1; - ASSERT (newargc > a->argc); - argv_extend (a, adjust_power_of_2 (newargc)); -} - -static void -argv_append (struct argv *a, char *str) /* str must have been malloced or be NULL */ -{ - argv_grow (a, 1); - a->argv[a->argc++] = str; -} - -static void -argv_system_str_append (struct argv *a, const char *str, const bool enquote) -{ - if (str) - { - char *newstr; - - /* compute length of new system_str */ - size_t l = strlen (str) + 1; /* space for new string plus trailing '\0' */ - if (a->system_str) - l += strlen (a->system_str) + 1; /* space for existing string + space (" ") separator */ - if (enquote) - l += 2; /* space for two quotes */ - - /* build new system_str */ - newstr = (char *) malloc (l); - newstr[0] = '\0'; - check_malloc_return (newstr); - if (a->system_str) - { - strcpy (newstr, a->system_str); - strcat (newstr, " "); - } - if (enquote) - strcat (newstr, "\""); - strcat (newstr, str); - if (enquote) - strcat (newstr, "\""); - free (a->system_str); - a->system_str = newstr; - } -} - -static char * -argv_extract_cmd_name (const char *path) -{ - char *ret = NULL; - if (path) - { - char *path_cp = string_alloc(path, NULL); /* POSIX basename() implementaions may modify its arguments */ - const char *bn = basename (path_cp); - if (bn) - { - char *dot = NULL; - ret = string_alloc (bn, NULL); - dot = strrchr (ret, '.'); - if (dot) - *dot = '\0'; - free(path_cp); - if (ret[0] == '\0') - { - free(ret); - ret = NULL; - } - } - } - return ret; -} - -const char * -argv_system_str (const struct argv *a) -{ - return a->system_str; -} - -struct argv -argv_clone (const struct argv *a, const size_t headroom) -{ - struct argv r; - size_t i; - - argv_init (&r); - for (i = 0; i < headroom; ++i) - argv_append (&r, NULL); - if (a) - { - for (i = 0; i < a->argc; ++i) - argv_append (&r, string_alloc (a->argv[i], NULL)); - r.system_str = string_alloc (a->system_str, NULL); - } - return r; -} - -struct argv -argv_insert_head (const struct argv *a, const char *head) -{ - struct argv r; - char *s; - - r = argv_clone (a, 1); - r.argv[0] = string_alloc (head, NULL); - s = r.system_str; - r.system_str = string_alloc (head, NULL); - if (s) - { - argv_system_str_append (&r, s, false); - free (s); - } - return r; -} - -char * -argv_term (const char **f) -{ - const char *p = *f; - const char *term = NULL; - size_t termlen = 0; - - if (*p == '\0') - return NULL; - - while (true) - { - const int c = *p; - if (c == '\0') - break; - if (term) - { - if (!isspace (c)) - ++termlen; - else - break; - } - else - { - if (!isspace (c)) - { - term = p; - termlen = 1; - } - } - ++p; - } - *f = p; - - if (term) - { - char *ret; - ASSERT (termlen > 0); - ret = malloc (termlen + 1); - check_malloc_return (ret); - memcpy (ret, term, termlen); - ret[termlen] = '\0'; - return ret; - } - else - return NULL; -} - -const char * -argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags) -{ - if (a->argv) - return print_argv ((const char **)a->argv, gc, flags); - else - return ""; -} - -void -argv_msg (const int msglev, const struct argv *a) -{ - struct gc_arena gc = gc_new (); - msg (msglev, "%s", argv_str (a, &gc, 0)); - gc_free (&gc); -} - -void -argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix) -{ - struct gc_arena gc = gc_new (); - msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0)); - gc_free (&gc); -} - -void -argv_printf (struct argv *a, const char *format, ...) -{ - va_list arglist; - va_start (arglist, format); - argv_printf_arglist (a, format, 0, arglist); - va_end (arglist); - } - -void -argv_printf_cat (struct argv *a, const char *format, ...) -{ - va_list arglist; - va_start (arglist, format); - argv_printf_arglist (a, format, APA_CAT, arglist); - va_end (arglist); -} - -void -argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist) -{ - struct gc_arena gc = gc_new (); - char *term; - const char *f = format; - - if (!(flags & APA_CAT)) - argv_reset (a); - argv_extend (a, 1); /* ensure trailing NULL */ - - while ((term = argv_term (&f)) != NULL) - { - if (term[0] == '%') - { - if (!strcmp (term, "%s")) - { - char *s = va_arg (arglist, char *); - if (!s) - s = ""; - argv_append (a, string_alloc (s, NULL)); - argv_system_str_append (a, s, true); - } - else if (!strcmp (term, "%sc")) - { - char *s = va_arg (arglist, char *); - if (s) - { - int nparms; - char *parms[MAX_PARMS+1]; - int i; - - nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc); - if (nparms) - { - for (i = 0; i < nparms; ++i) - argv_append (a, string_alloc (parms[i], NULL)); - } - else - argv_append (a, string_alloc (s, NULL)); - - argv_system_str_append (a, s, false); - } - else - { - argv_append (a, string_alloc ("", NULL)); - argv_system_str_append (a, "echo", false); - } - } - else if (!strcmp (term, "%d")) - { - char numstr[64]; - openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int)); - argv_append (a, string_alloc (numstr, NULL)); - argv_system_str_append (a, numstr, false); - } - else if (!strcmp (term, "%u")) - { - char numstr[64]; - openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int)); - argv_append (a, string_alloc (numstr, NULL)); - argv_system_str_append (a, numstr, false); - } - else if (!strcmp (term, "%s/%d")) - { - char numstr[64]; - char *s = va_arg (arglist, char *); - - if (!s) - s = ""; - - openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int)); - - { - const size_t len = strlen(s) + strlen(numstr) + 2; - char *combined = (char *) malloc (len); - check_malloc_return (combined); - - strcpy (combined, s); - strcat (combined, "/"); - strcat (combined, numstr); - argv_append (a, combined); - argv_system_str_append (a, combined, false); - } - } - else if (!strcmp (term, "%s%sc")) - { - char *s1 = va_arg (arglist, char *); - char *s2 = va_arg (arglist, char *); - char *combined; - char *cmd_name; - - if (!s1) s1 = ""; - if (!s2) s2 = ""; - combined = (char *) malloc (strlen(s1) + strlen(s2) + 1); - check_malloc_return (combined); - strcpy (combined, s1); - strcat (combined, s2); - argv_append (a, combined); - - cmd_name = argv_extract_cmd_name (combined); - if (cmd_name) - { - argv_system_str_append (a, cmd_name, false); - free (cmd_name); - } - } - else - ASSERT (0); - free (term); - } - else - { - argv_append (a, term); - argv_system_str_append (a, term, false); - } - } - gc_free (&gc); -} - -#ifdef ARGV_TEST -void -argv_test (void) -{ - struct gc_arena gc = gc_new (); - const char *s; - - struct argv a; - - argv_init (&a); - argv_printf (&a, "%sc foo bar %s", "c:\\\\src\\\\test\\\\jyargs.exe", "foo bar"); - argv_msg_prefix (M_INFO, &a, "ARGV"); - msg (M_INFO, "ARGV-S: %s", argv_system_str(&a)); - /*openvpn_execve_check (&a, NULL, 0, "command failed");*/ - - argv_printf (&a, "%sc %s %s", "c:\\\\src\\\\test files\\\\batargs.bat", "foo", "bar"); - argv_msg_prefix (M_INFO, &a, "ARGV"); - msg (M_INFO, "ARGV-S: %s", argv_system_str(&a)); - /*openvpn_execve_check (&a, NULL, 0, "command failed");*/ - - argv_printf (&a, "%s%sc foo bar %s %s/%d %d %u", "/foo", "/bar.exe", "one two", "1.2.3.4", 24, -69, 96); - argv_msg_prefix (M_INFO, &a, "ARGV"); - msg (M_INFO, "ARGV-S: %s", argv_system_str(&a)); - /*openvpn_execve_check (&a, NULL, 0, "command failed");*/ - - argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42); - s = argv_str (&a, &gc, PA_BRACKET); - printf ("PF: %s\n", s); - printf ("PF-S: %s\n", argv_system_str(&a)); - - { - struct argv b = argv_insert_head (&a, "MARK"); - s = argv_str (&b, &gc, PA_BRACKET); - printf ("PF: %s\n", s); - printf ("PF-S: %s\n", argv_system_str(&b)); - argv_reset (&b); - } - - argv_printf (&a, "%sc foo bar %d", "\"multi term\" command following \\\"spaces", 99); - s = argv_str (&a, &gc, PA_BRACKET); - printf ("PF: %s\n", s); - printf ("PF-S: %s\n", argv_system_str(&a)); - argv_reset (&a); - - s = argv_str (&a, &gc, PA_BRACKET); - printf ("PF: %s\n", s); - printf ("PF-S: %s\n", argv_system_str(&a)); - argv_reset (&a); - - argv_printf (&a, "foo bar %d", 99); - argv_printf_cat (&a, "bar %d foo %sc", 42, "nonesuch"); - argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7); - s = argv_str (&a, &gc, PA_BRACKET); - printf ("PF: %s\n", s); - printf ("PF-S: %s\n", argv_system_str(&a)); - argv_reset (&a); - -#if 0 - { - char line[512]; - while (fgets (line, sizeof(line), stdin) != NULL) - { - char *term; - const char *f = line; - int i = 0; - - while ((term = argv_term (&f)) != NULL) - { - printf ("[%d] '%s'\n", i, term); - ++i; - free (term); - } - } - } -#endif - - argv_reset (&a); - gc_free (&gc); -} -#endif - /* * Remove security-sensitive strings from control message * so that they will not be output to log file. @@ -2040,6 +1583,15 @@ sanitize_control_message(const char *src, struct gc_arena *gc) skip = 4; redact = true; } + else if (!check_debug_level(D_SHOW_KEYS) + && (c == 'a' && !strncmp(src, "auth-token ", 11))) + { + /* Unless --verb is 7 or higher (D_SHOW_KEYS), hide + * the auth-token value coming in the src string + */ + skip = 10; + redact = true; + } if (c == ',') /* end of redacted item? */ { @@ -2084,3 +1636,59 @@ compat_flag (unsigned int flag) return (compat_flags & (flag >> 1)); } + +#if P2MP_SERVER + +/* helper to parse peer_info received from multi client, validate + * (this is untrusted data) and put into environment + */ +bool +validate_peer_info_line(char *line) +{ + uint8_t c; + int state = 0; + while (*line) + { + c = *line; + switch (state) + { + case 0: + case 1: + if (c == '=' && state == 1) + state = 2; + else if (isalnum(c) || c == '_') + state = 1; + else + return false; + case 2: + /* after the '=', replace non-printable or shell meta with '_' */ + if (!isprint(c) || isspace(c) || + c == '$' || c == '(' || c == '`' ) + *line = '_'; + } + line++; + } + return (state == 2); +} + +void +output_peer_info_env (struct env_set *es, const char * peer_info) +{ + char line[256]; + struct buffer buf; + buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info)); + while (buf_parse (&buf, '\n', line, sizeof (line))) + { + chomp (line); + if (validate_peer_info_line(line) && + (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) ) + { + msg (M_INFO, "peer info: %s", line); + env_set_add(es, line); + } + else + msg (M_WARN, "validation failed on peer_info line received from client"); + } +} + +#endif /* P2MP_SERVER */ diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h index c1942b6..b8bbaa7 100644 --- a/src/openvpn/misc.h +++ b/src/openvpn/misc.h @@ -25,6 +25,7 @@ #ifndef MISC_H #define MISC_H +#include "argv.h" #include "basic.h" #include "common.h" #include "integer.h" @@ -37,14 +38,6 @@ /* forward declarations */ struct plugin_list; -/* used by argv_x functions */ -struct argv { - size_t capacity; - size_t argc; - char **argv; - char *system_str; -}; - /* * Handle environmental variable lists */ @@ -63,7 +56,7 @@ void run_up_down (const char *command, const struct plugin_list *plugins, int plugin_type, const char *arg, -#ifdef WIN32 +#ifdef _WIN32 DWORD adapter_index, #endif const char *dev_type, @@ -78,9 +71,6 @@ void run_up_down (const char *command, void write_pid (const char *filename); -/* check file protections */ -void warn_if_group_others_accessible(const char* filename); - /* system flags */ #define S_SCRIPT (1<<0) #define S_FATAL (1<<1) @@ -136,6 +126,12 @@ void setenv_str (struct env_set *es, const char *name, const char *value); void setenv_str_safe (struct env_set *es, const char *name, const char *value); void setenv_del (struct env_set *es, const char *name); +/** + * Store the supplied name value pair in the env_set. If the variable with the + * supplied name already exists, append _N to the name, starting at N=1. + */ +void setenv_str_incr(struct env_set *es, const char *name, const char *value); + void setenv_int_i (struct env_set *es, const char *name, const int value, const int i); void setenv_str_i (struct env_set *es, const char *name, const char *value, const int i); @@ -145,6 +141,7 @@ struct env_set *env_set_create (struct gc_arena *gc); void env_set_destroy (struct env_set *es); bool env_set_del (struct env_set *es, const char *str); void env_set_add (struct env_set *es, const char *str); +const char* env_set_get (const struct env_set *es, const char *name); void env_set_print (int msglevel, const struct env_set *es); @@ -162,10 +159,6 @@ const char **make_env_array (const struct env_set *es, const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc); const char **make_extended_arg_array (char **p, struct gc_arena *gc); -/* convert netmasks for iproute2 */ -int count_netmask_bits(const char *); -unsigned int count_bits(unsigned int ); - /* an analogue to the random() function, but use OpenSSL functions if available */ #ifdef ENABLE_CRYPTO long int get_random(void); @@ -253,6 +246,8 @@ struct static_challenge_info {}; #define GET_USER_PASS_STATIC_CHALLENGE (1<<8) /* SCRV1 protocol -- static challenge */ #define GET_USER_PASS_STATIC_CHALLENGE_ECHO (1<<9) /* SCRV1 protocol -- echo response */ +#define GET_USER_PASS_INLINE_CREDS (1<<10) /* indicates that auth_file is actually inline creds */ + bool get_user_pass_cr (struct user_pass *up, const char *auth_file, const char *prefix, @@ -320,49 +315,17 @@ extern int script_security; /* GLOBAL */ /* return the next largest power of 2 */ size_t adjust_power_of_2 (size_t u); -/* - * A printf-like function (that only recognizes a subset of standard printf - * format operators) that prints arguments to an argv list instead - * of a standard string. This is used to build up argv arrays for passing - * to execve. - */ -void argv_init (struct argv *a); -struct argv argv_new (void); -void argv_reset (struct argv *a); -char *argv_term (const char **f); -const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags); -struct argv argv_insert_head (const struct argv *a, const char *head); -void argv_msg (const int msglev, const struct argv *a); -void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix); -const char *argv_system_str (const struct argv *a); - -#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */ -void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist); - -void argv_printf (struct argv *a, const char *format, ...) -#ifdef __GNUC__ -#if __USE_MINGW_ANSI_STDIO - __attribute__ ((format (gnu_printf, 2, 3))) -#else - __attribute__ ((format (__printf__, 2, 3))) -#endif -#endif - ; - -void argv_printf_cat (struct argv *a, const char *format, ...) -#ifdef __GNUC__ -#if __USE_MINGW_ANSI_STDIO - __attribute__ ((format (gnu_printf, 2, 3))) -#else - __attribute__ ((format (__printf__, 2, 3))) -#endif -#endif - ; - #define COMPAT_FLAG_QUERY 0 /** compat_flags operator: Query for a flag */ #define COMPAT_FLAG_SET (1<<0) /** compat_flags operator: Set a compat flag */ #define COMPAT_NAMES (1<<1) /** compat flag: --compat-names set */ #define COMPAT_NO_NAME_REMAPPING (1<<2) /** compat flag: --compat-names without char remapping */ bool compat_flag (unsigned int flag); +#if P2MP_SERVER +/* helper to parse peer_info received from multi client, validate + * (this is untrusted data) and put into environment */ +bool validate_peer_info_line(char *line); +void output_peer_info_env (struct env_set *es, const char * peer_info); +#endif /* P2MP_SERVER */ + #endif diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c index 850e336..c905af7 100644 --- a/src/openvpn/mroute.c +++ b/src/openvpn/mroute.c @@ -58,7 +58,8 @@ is_mac_mcast_addr (const uint8_t *mac) static inline bool is_mac_mcast_maddr (const struct mroute_addr *addr) { - return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr); + return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && + is_mac_mcast_addr (addr->eth_addr); } /* @@ -73,7 +74,7 @@ mroute_learnable_address (const struct mroute_addr *addr) for (i = 0; i < addr->len; ++i) { - int b = addr->addr[i]; + int b = addr->raw_addr[i]; if (b != 0x00) not_all_zeros = true; if (b != 0xFF) @@ -90,7 +91,7 @@ mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int ma->type = MR_ADDR_IPV4 | mask; ma->netbits = 0; ma->len = 4; - *(in_addr_t*)ma->addr = src; + ma->v4.addr = src; } } @@ -102,7 +103,7 @@ mroute_get_in6_addr (struct mroute_addr *ma, const struct in6_addr src, unsigned ma->type = MR_ADDR_IPV6 | mask; ma->netbits = 0; ma->len = 16; - *(struct in6_addr *)ma->addr = src; + ma->v6.addr = src; } } @@ -226,14 +227,14 @@ mroute_extract_addr_ether (struct mroute_addr *src, src->type = MR_ADDR_ETHER; src->netbits = 0; src->len = 6; - memcpy (src->addr, eth->source, 6); + memcpy (src->eth_addr, eth->source, sizeof(dest->eth_addr)); } if (dest) { dest->type = MR_ADDR_ETHER; dest->netbits = 0; dest->len = 6; - memcpy (dest->addr, eth->dest, 6); + memcpy (dest->eth_addr, eth->dest, sizeof(dest->eth_addr)); /* ethernet broadcast/multicast packet? */ if (is_mac_mcast_addr (eth->dest)) @@ -281,15 +282,15 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, addr->type = MR_ADDR_IPV4 | MR_WITH_PORT; addr->netbits = 0; addr->len = 6; - memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); - memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2); + addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr; + addr->v4.port = osaddr->addr.in4.sin_port; } else { addr->type = MR_ADDR_IPV4; addr->netbits = 0; addr->len = 4; - memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4); + addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr; } return true; } @@ -299,15 +300,15 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, 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); + addr->v6.addr = osaddr->addr.in6.sin6_addr; + addr->v6.port = osaddr->addr.in6.sin6_port; } else { addr->type = MR_ADDR_IPV6; addr->netbits = 0; addr->len = 16; - memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16); + addr->v6.addr = osaddr->addr.in6.sin6_addr; } return true; } @@ -326,23 +327,29 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr, void mroute_addr_mask_host_bits (struct mroute_addr *ma) { - in_addr_t addr = ntohl(*(in_addr_t*)ma->addr); if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4) { + in_addr_t addr = ntohl (ma->v4.addr); addr &= netbits_to_netmask (ma->netbits); - *(in_addr_t*)ma->addr = htonl (addr); + ma->v4.addr = htonl (addr); } else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6) { - int byte = ma->len-1; /* rightmost byte in address */ + int byte = sizeof (ma->v6.addr) - 1; /* rightmost byte in address */ int bits_to_clear = 128 - ma->netbits; while( byte >= 0 && bits_to_clear > 0 ) { if ( bits_to_clear >= 8 ) - { ma->addr[byte--] = 0; bits_to_clear -= 8; } + { + ma->v6.addr.s6_addr[byte--] = 0; + bits_to_clear -= 8; + } else - { ma->addr[byte--] &= (IPV4_NETMASK_HOST << bits_to_clear); bits_to_clear = 0; } + { + ma->v6.addr.s6_addr[byte--] &= (IPV4_NETMASK_HOST << bits_to_clear); + bits_to_clear = 0; + } } ASSERT( bits_to_clear == 0 ); } @@ -390,44 +397,42 @@ mroute_addr_print_ex (const struct mroute_addr *ma, switch (maddr.type & MR_ADDR_MASK) { case MR_ADDR_ETHER: - buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); + buf_printf (&out, "%s", format_hex_ex (ma->eth_addr, + sizeof(ma->eth_addr), 0, 1, ":", gc)); break; case MR_ADDR_IPV4: { - struct buffer buf; - in_addr_t addr; - int port; - bool status; - buf_set_read (&buf, maddr.addr, maddr.len); - addr = buf_read_u32 (&buf, &status); - if (status) + if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP)) + buf_printf (&out, "ARP/"); + buf_printf (&out, "%s", print_in_addr_t (ntohl (maddr.v4.addr), + (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc)); + if (maddr.type & MR_WITH_NETBITS) { - if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP)) - buf_printf (&out, "ARP/"); - buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc)); - if (maddr.type & MR_WITH_NETBITS) + if (flags & MAPF_SUBNET) { - if (flags & MAPF_SUBNET) - { - const in_addr_t netmask = netbits_to_netmask (maddr.netbits); - buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc)); - } - else - buf_printf (&out, "/%d", maddr.netbits); + const in_addr_t netmask = netbits_to_netmask (maddr.netbits); + buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc)); } + else + buf_printf (&out, "/%d", maddr.netbits); } if (maddr.type & MR_WITH_PORT) { - port = buf_read_u16 (&buf); - if (port >= 0) - buf_printf (&out, ":%d", port); + buf_printf (&out, ":%d", ntohs (maddr.v4.port)); } } break; case MR_ADDR_IPV6: { - buf_printf (&out, "%s", - print_in6_addr( *(struct in6_addr*)&maddr.addr, 0, gc)); + if ( IN6_IS_ADDR_V4MAPPED( &maddr.v6.addr ) ) + { + buf_printf (&out, "%s", print_in_addr_t (maddr.v4mappedv6.addr, + IA_NET_ORDER, gc)); + } + else + { + buf_printf (&out, "%s", print_in6_addr (maddr.v6.addr, 0, gc)); + } if (maddr.type & MR_WITH_NETBITS) { buf_printf (&out, "/%d", maddr.netbits); @@ -487,62 +492,28 @@ mroute_helper_regenerate (struct mroute_helper *mh) } void -mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir) -{ - if (ir->netbits >= 0) - { - ASSERT (ir->netbits < MR_HELPER_NET_LEN); - ++mh->cache_generation; - ++mh->net_len_refcount[ir->netbits]; - if (mh->net_len_refcount[ir->netbits] == 1) - mroute_helper_regenerate (mh); - } -} - -void -mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir) -{ - if (ir->netbits >= 0) - { - ASSERT (ir->netbits < MR_HELPER_NET_LEN); - ++mh->cache_generation; - --mh->net_len_refcount[ir->netbits]; - ASSERT (mh->net_len_refcount[ir->netbits] >= 0); - if (!mh->net_len_refcount[ir->netbits]) - mroute_helper_regenerate (mh); - } -} - -/* this is a bit inelegant, we really should have a helper to that - * is only passed the netbits value, and not the whole struct iroute * - * - thus one helper could do IPv4 and IPv6. For the sake of "not change - * code unrelated to IPv4" this is left for later cleanup, for now. - */ -void -mroute_helper_add_iroute6 (struct mroute_helper *mh, - const struct iroute_ipv6 *ir6) +mroute_helper_add_iroute46 (struct mroute_helper *mh, int netbits) { - if (ir6->netbits >= 0) + if (netbits >= 0) { - ASSERT (ir6->netbits < MR_HELPER_NET_LEN); + ASSERT (netbits < MR_HELPER_NET_LEN); ++mh->cache_generation; - ++mh->net_len_refcount[ir6->netbits]; - if (mh->net_len_refcount[ir6->netbits] == 1) + ++mh->net_len_refcount[netbits]; + if (mh->net_len_refcount[netbits] == 1) mroute_helper_regenerate (mh); } } void -mroute_helper_del_iroute6 (struct mroute_helper *mh, - const struct iroute_ipv6 *ir6) +mroute_helper_del_iroute46 (struct mroute_helper *mh, int netbits) { - if (ir6->netbits >= 0) + if (netbits >= 0) { - ASSERT (ir6->netbits < MR_HELPER_NET_LEN); + ASSERT (netbits < MR_HELPER_NET_LEN); ++mh->cache_generation; - --mh->net_len_refcount[ir6->netbits]; - ASSERT (mh->net_len_refcount[ir6->netbits] >= 0); - if (!mh->net_len_refcount[ir6->netbits]) + --mh->net_len_refcount[netbits]; + ASSERT (mh->net_len_refcount[netbits] >= 0); + if (!mh->net_len_refcount[netbits]) mroute_helper_regenerate (mh); } } diff --git a/src/openvpn/mroute.h b/src/openvpn/mroute.h index b72b5ff..8f7a064 100644 --- a/src/openvpn/mroute.h +++ b/src/openvpn/mroute.h @@ -31,6 +31,8 @@ #include "list.h" #include "route.h" +#include + #define IP_MCAST_SUBNET_MASK ((in_addr_t)240<<24) #define IP_MCAST_NETWORK ((in_addr_t)224<<24) @@ -79,9 +81,45 @@ struct mroute_addr { uint8_t type; /* MR_ADDR/MR_WITH flags */ uint8_t netbits; /* number of bits in network part of address, valid if MR_WITH_NETBITS is set */ - uint8_t addr[MR_MAX_ADDR_LEN]; /* actual address */ + union { + uint8_t raw_addr[MR_MAX_ADDR_LEN]; /* actual address */ + uint8_t eth_addr[OPENVPN_ETH_ALEN]; + struct { + in_addr_t addr; /* _network order_ IPv4 address */ + in_port_t port; /* _network order_ TCP/UDP port */ + } v4; + struct { + struct in6_addr addr; + in_port_t port; /* _network order_ TCP/UDP port */ + } v6; + struct { + uint8_t prefix[12]; + in_addr_t addr; /* _network order_ IPv4 address */ + } v4mappedv6; + } +#ifndef HAVE_ANONYMOUS_UNION_SUPPORT +/* Wrappers to support compilers that do not grok anonymous unions */ + mroute_union +#define raw_addr mroute_union.raw_addr +#define eth_addr mroute_union.eth_addr +#define v4 mroute_union.v4 +#define v6 mroute_union.v6 +#define v4mappedv6 mroute_union.v4mappedv6 +#endif + ; }; +/* Double-check that struct packing works as expected */ +static_assert (offsetof(struct mroute_addr, v4.port) == + offsetof(struct mroute_addr, v4) + 4, + "Unexpected struct packing of v4"); +static_assert (offsetof(struct mroute_addr, v6.port) == + offsetof(struct mroute_addr, v6) + 16, + "Unexpected struct packing of v6"); +static_assert (offsetof(struct mroute_addr, v4mappedv6.addr) == + offsetof(struct mroute_addr, v4mappedv6) + 12, + "Unexpected struct packing of v4mappedv6"); + /* * Number of bits in an address. Should be raised for IPv6. */ @@ -125,10 +163,8 @@ void mroute_addr_mask_host_bits (struct mroute_addr *ma); struct mroute_helper *mroute_helper_init (int ageable_ttl_secs); void mroute_helper_free (struct mroute_helper *mh); -void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir); -void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir); -void mroute_helper_add_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6); -void mroute_helper_del_iroute6 (struct mroute_helper *mh, const struct iroute_ipv6 *ir6); +void mroute_helper_add_iroute46 (struct mroute_helper *mh, int netbits); +void mroute_helper_del_iroute46 (struct mroute_helper *mh, int netbits); /* * Given a raw packet in buf, return the src and dest @@ -169,7 +205,7 @@ mroute_addr_equal (const struct mroute_addr *a1, const struct mroute_addr *a2) return false; if (a1->len != a2->len) return false; - return memcmp (a1->addr, a2->addr, a1->len) == 0; + return memcmp (a1->raw_addr, a2->raw_addr, a1->len) == 0; } static inline const uint8_t * @@ -191,16 +227,17 @@ mroute_extract_in_addr_t (struct mroute_addr *dest, const in_addr_t src) dest->type = MR_ADDR_IPV4; dest->netbits = 0; dest->len = 4; - *(in_addr_t*)dest->addr = htonl (src); + dest->v4.addr = htonl (src); } static inline in_addr_t in_addr_t_from_mroute_addr (const struct mroute_addr *addr) { - if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4) - return ntohl(*(in_addr_t*)addr->addr); - else + if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4) { + return ntohl(addr->v4.addr); + } else { return 0; + } } static inline void diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c index dc15f09..78e5ccd 100644 --- a/src/openvpn/mtcp.c +++ b/src/openvpn/mtcp.c @@ -37,6 +37,10 @@ #include "memdbg.h" +#ifdef HAVE_SYS_INOTIFY_H +#include +#endif + /* * TCP States */ @@ -62,6 +66,10 @@ # define MTCP_MANAGEMENT ((void*)4) #endif +#ifdef ENABLE_ASYNC_PUSH +#define MTCP_FILE_CLOSE_WRITE ((void*)5) +#endif + #define MTCP_N ((void*)16) /* upper bound on MTCP_x */ struct ta_iow_flags @@ -245,6 +253,12 @@ multi_tcp_wait (const struct context *c, if (management) management_socket_set (management, mtcp->es, MTCP_MANAGEMENT, &mtcp->management_persist_flags); #endif + +#ifdef ENABLE_ASYNC_PUSH + /* arm inotify watcher */ + event_ctl (mtcp->es, c->c2.inotify_fd, EVENT_READ, MTCP_FILE_CLOSE_WRITE); +#endif + status = event_wait (mtcp->es, &c->c2.timeval, mtcp->esr, mtcp->maxevents); update_time (); mtcp->n_esr = 0; @@ -636,6 +650,12 @@ multi_tcp_process_io (struct multi_context *m) { get_signal (&m->top.sig->signal_received); } +#ifdef ENABLE_ASYNC_PUSH + else if (e->arg == MTCP_FILE_CLOSE_WRITE) + { + multi_process_file_closed (m, MPP_PRE_SELECT | MPP_RECORD_TOUCH); + } +#endif } if (IS_SIG (&m->top)) break; @@ -676,7 +696,7 @@ tunnel_server_tcp (struct context *top) multi_init (&multi, top, true, MC_SINGLE_THREADED); /* initialize our cloned top object */ - multi_top_init (&multi, top, true); + multi_top_init (&multi, top); /* initialize management interface */ init_management_callback_multi (&multi); @@ -684,6 +704,14 @@ tunnel_server_tcp (struct context *top) /* finished with initialization */ initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto tcp-server */ +#ifdef ENABLE_ASYNC_PUSH + multi.top.c2.inotify_fd = inotify_init(); + if (multi.top.c2.inotify_fd < 0) + { + msg (D_MULTI_ERRORS, "MULTI: inotify_init error: %s", strerror(errno)); + } +#endif + /* per-packet event loop */ while (true) { @@ -712,6 +740,10 @@ tunnel_server_tcp (struct context *top) perf_pop (); } +#ifdef ENABLE_ASYNC_PUSH + close(top->c2.inotify_fd); +#endif + /* shut down management interface */ uninit_management_callback_multi (&multi); diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index 13f3f6c..8cbaa86 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -35,6 +35,7 @@ #include "error.h" #include "integer.h" #include "mtu.h" +#include "options.h" #include "memdbg.h" @@ -78,8 +79,6 @@ frame_finalize (struct frame *frame, } frame->link_mtu_dynamic = frame->link_mtu; - - frame->extra_buffer += PAYLOAD_ALIGN; } /* @@ -126,6 +125,15 @@ frame_subtract_extra (struct frame *frame, const struct frame *src) frame->extra_tun += src->extra_frame; } +void +frame_init_mssfix (struct frame *frame, const struct options *options) +{ + if (options->ce.mssfix) + { + frame_set_mtu_dynamic (frame, options->ce.mssfix, SET_MTU_UPPER_BOUND); + } +} + void frame_print (const struct frame *frame, int level, @@ -153,18 +161,32 @@ frame_print (const struct frame *frame, #define MTUDISC_NOT_SUPPORTED_MSG "--mtu-disc is not supported on this OS" void -set_mtu_discover_type (int sd, int mtu_type) +set_mtu_discover_type (int sd, int mtu_type, sa_family_t proto_af) { if (mtu_type >= 0) { -#if defined(HAVE_SETSOCKOPT) && defined(SOL_IP) && defined(IP_MTU_DISCOVER) - if (setsockopt - (sd, SOL_IP, IP_MTU_DISCOVER, &mtu_type, sizeof (mtu_type))) - msg (M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket", - mtu_type); -#else - msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG); + switch (proto_af) + { +#if defined(HAVE_SETSOCKOPT) && defined(IP_MTU_DISCOVER) + case AF_INET: + if (setsockopt + (sd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu_type, sizeof (mtu_type))) + msg (M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket", + mtu_type); + break; +#endif +#if defined(HAVE_SETSOCKOPT) && defined(IPV6_MTU_DISCOVER) + case AF_INET6: + if (setsockopt + (sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &mtu_type, sizeof (mtu_type))) + msg (M_ERR, "Error setting IPV6_MTU_DISCOVER type=%d on TCP6/UDP6 socket", + mtu_type); + break; #endif + default: + msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG); + break; + } } } @@ -288,7 +310,7 @@ void set_sock_extended_error_passing (int sd) { int on = 1; - if (setsockopt (sd, SOL_IP, IP_RECVERR, &on, sizeof (on))) + if (setsockopt (sd, SOL_IP, IP_RECVERR, (void *) &on, sizeof (on))) msg (M_WARN | M_ERRNO, "Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)"); } diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index bccd681..0320545 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -135,6 +135,9 @@ struct frame { int align_adjust; }; +/* Forward declarations, to prevent includes */ +struct options; + /* Routines which read struct frame should use the macros below */ /* @@ -207,7 +210,7 @@ void frame_print (const struct frame *frame, int level, const char *prefix); -void set_mtu_discover_type (int sd, int mtu_type); +void set_mtu_discover_type (int sd, int mtu_type, sa_family_t proto_af); int translate_mtu_discover_type_name (const char *name); /* @@ -227,6 +230,9 @@ void alloc_buf_sock_tun (struct buffer *buf, const bool tuntap_buffer, const unsigned int align_mask); +/** Set the --mssfix option. */ +void frame_init_mssfix (struct frame *frame, const struct options *options); + /* * EXTENDED_SOCKET_ERROR_CAPABILITY functions -- print extra error info * on socket errors, such as PMTU size. As of 2003.05.11, only works diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c index 3468dab..fec5e8d 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -33,10 +33,15 @@ #if P2MP_SERVER #include "multi.h" +#include #include "forward-inline.h" #include "memdbg.h" +#ifdef HAVE_SYS_INOTIFY_H +#include +#endif + /* * Get a client instance based on real address. If * the instance doesn't exist, create it while @@ -44,26 +49,54 @@ */ struct multi_instance * -multi_get_create_instance_udp (struct multi_context *m) +multi_get_create_instance_udp (struct multi_context *m, bool *floated) { struct gc_arena gc = gc_new (); struct mroute_addr real; struct multi_instance *mi = NULL; struct hash *hash = m->hash; - if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) + if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true) && + m->top.c2.buf.len > 0) { struct hash_element *he; const uint32_t hv = hash_value (hash, &real); struct hash_bucket *bucket = hash_bucket (hash, hv); - - he = hash_lookup_fast (hash, bucket, &real, hv); + uint8_t* ptr = BPTR(&m->top.c2.buf); + uint8_t op = ptr[0] >> P_OPCODE_SHIFT; + bool v2 = (op == P_DATA_V2) && (m->top.c2.buf.len >= (1 + 3)); + bool peer_id_disabled = false; - if (he) + /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */ + if (v2) { - mi = (struct multi_instance *) he->value; + uint32_t peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF; + peer_id_disabled = (peer_id == MAX_PEER_ID); + + if (!peer_id_disabled && (peer_id < m->max_clients) && (m->instances[peer_id])) + { + mi = m->instances[peer_id]; + + *floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from); + + if (*floated) + { + /* reset prefix, since here we are not sure peer is the one it claims to be */ + ungenerate_prefix(mi); + msg (D_MULTI_MEDIUM, "Float requested for peer %" PRIu32 " to %s", peer_id, + mroute_addr_print (&real, &gc)); + } + } } - else + if (!v2 || peer_id_disabled) + { + he = hash_lookup_fast (hash, bucket, &real, hv); + if (he) + { + mi = (struct multi_instance *) he->value; + } + } + if (!mi) { if (!m->top.c2.tls_auth_standalone || tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf)) @@ -73,8 +106,27 @@ multi_get_create_instance_udp (struct multi_context *m) mi = multi_create_instance (m, &real); if (mi) { + int i; + hash_add_fast (hash, bucket, &mi->real, hv, mi); mi->did_real_hash = true; + + /* max_clients must be less then max peer-id value */ + ASSERT(m->max_clients < MAX_PEER_ID); + + for (i = 0; i < m->max_clients; ++i) + { + if (!m->instances[i]) + { + mi->context.c2.tls_multi->peer_id = i; + m->instances[i] = mi; + break; + } + } + + /* should not really end up here, since multi_create_instance returns null + * if amount of clients exceeds max_clients */ + ASSERT(i < m->max_clients); } } else @@ -89,15 +141,8 @@ multi_get_create_instance_udp (struct multi_context *m) #ifdef ENABLE_DEBUG if (check_debug_level (D_MULTI_DEBUG)) { - const char *status; - - if (he && mi) - status = "[succeeded]"; - else if (!he && mi) - status = "[created]"; - else - status = "[failed]"; - + const char *status = mi ? "[ok]" : "[failed]"; + dmsg (D_MULTI_DEBUG, "GET INST BY REAL: %s %s", mroute_addr_print (&real, &gc), status); @@ -143,6 +188,10 @@ multi_process_io_udp (struct multi_context *m) strcat (buf, "TR/"); else if (status & TUN_WRITE) strcat (buf, "TW/"); +#ifdef ENABLE_ASYNC_PUSH + else if (status & FILE_CLOSED) + strcat (buf, "FC/"); +#endif printf ("IO %s\n", buf); #endif @@ -168,7 +217,6 @@ multi_process_io_udp (struct multi_context *m) else if (status & SOCKET_READ) { read_incoming_link (&m->top); - multi_release_io_lock (m); if (!IS_SIG (&m->top)) multi_process_incoming_link (m, NULL, mpp_flags); } @@ -176,10 +224,16 @@ multi_process_io_udp (struct multi_context *m) else if (status & TUN_READ) { read_incoming_tun (&m->top); - multi_release_io_lock (m); if (!IS_SIG (&m->top)) multi_process_incoming_tun (m, mpp_flags); } +#ifdef ENABLE_ASYNC_PUSH + /* INOTIFY callback */ + else if (status & FILE_CLOSED) + { + multi_process_file_closed(m, mpp_flags); + } +#endif } /* @@ -234,7 +288,7 @@ tunnel_server_udp_single_threaded (struct context *top) multi_init (&multi, top, false, MC_SINGLE_THREADED); /* initialize our cloned top object */ - multi_top_init (&multi, top, true); + multi_top_init (&multi, top); /* initialize management interface */ init_management_callback_multi (&multi); @@ -242,6 +296,14 @@ tunnel_server_udp_single_threaded (struct context *top) /* finished with initialization */ initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto udp */ +#ifdef ENABLE_ASYNC_PUSH + multi.top.c2.inotify_fd = inotify_init(); + if (multi.top.c2.inotify_fd < 0) + { + msg (D_MULTI_ERRORS, "MULTI: inotify_init error: %s", strerror(errno)); + } +#endif + /* per-packet event loop */ while (true) { @@ -270,6 +332,10 @@ tunnel_server_udp_single_threaded (struct context *top) perf_pop (); } +#ifdef ENABLE_ASYNC_PUSH + close(top->c2.inotify_fd); +#endif + /* shut down management interface */ uninit_management_callback_multi (&multi); diff --git a/src/openvpn/mudp.h b/src/openvpn/mudp.h index 97f961b..1f15d9d 100644 --- a/src/openvpn/mudp.h +++ b/src/openvpn/mudp.h @@ -65,7 +65,7 @@ void tunnel_server_udp (struct context *top); * packet's source address or if one was a newly created successfully. * NULL if one did not yet exist and a new one was not created. */ -struct multi_instance *multi_get_create_instance_udp (struct multi_context *m); +struct multi_instance *multi_get_create_instance_udp (struct multi_context *m, bool *floated); #endif #endif diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 577c5d3..8f3d34e 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -28,6 +28,11 @@ #include "config-msvc.h" #endif +#ifdef HAVE_SYS_INOTIFY_H +#include +#define INOTIFY_EVENT_BUFFER_SIZE 16384 +#endif + #include "syshead.h" #if P2MP_SERVER @@ -38,6 +43,8 @@ #include "otime.h" #include "gremlin.h" #include "mstats.h" +#include "ssl_verify.h" +#include #include "memdbg.h" @@ -119,10 +126,8 @@ learn_address_script (const struct multi_context *m, { struct argv argv = argv_new (); setenv_str (es, "script_type", "learn-address"); - argv_printf (&argv, "%sc %s %s", - m->top.options.learn_address_script, - op, - mroute_addr_print (addr, &gc)); + argv_parse_cmd (&argv, m->top.options.learn_address_script); + argv_printf_cat (&argv, "%s %s", op, mroute_addr_print (addr, &gc)); if (mi) argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false)); if (!openvpn_run_script (&argv, es, 0, "--learn-address")) @@ -241,6 +246,23 @@ cid_compare_function (const void *key1, const void *key2) #endif +#ifdef ENABLE_ASYNC_PUSH +static uint32_t +/* + * inotify watcher descriptors are used as hash value + */ +int_hash_function (const void *key, uint32_t iv) +{ + return (unsigned long)key; +} + +static bool +int_compare_function (const void *key1, const void *key2) +{ + return (unsigned long)key1 == (unsigned long)key2; +} +#endif + /* * Main initialization function, init multi_context object. */ @@ -302,6 +324,17 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa cid_compare_function); #endif +#ifdef ENABLE_ASYNC_PUSH + /* + * Mapping between inotify watch descriptors and + * multi_instances. + */ + m->inotify_watchers = hash_init (t->options.real_hash_size, + get_random(), + int_hash_function, + int_compare_function); +#endif + /* * This is our scheduler, for time-based wakeup * events. @@ -372,6 +405,8 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa */ m->max_clients = t->options.max_clients; + m->instances = calloc(m->max_clients, sizeof(struct multi_instance*)); + /* * Initialize multi-socket TCP I/O wait object */ @@ -392,6 +427,8 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa t->options.stale_routes_check_interval, t->options.stale_routes_ageing_time); event_timeout_init (&m->stale_routes_check_et, t->options.stale_routes_check_interval, 0); } + + m->deferred_shutdown_signal.signal_received = 0; } const char * @@ -399,7 +436,7 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are { if (mi) { - struct buffer out = alloc_buf_gc (256, gc); + struct buffer out = alloc_buf_gc (MULTI_PREFIX_MAX_LENGTH, gc); const char *cn = tls_common_name (mi->context.c2.tls_multi, true); if (cn) @@ -416,21 +453,27 @@ multi_instance_string (const struct multi_instance *mi, bool null, struct gc_are void generate_prefix (struct multi_instance *mi) { - mi->msg_prefix = multi_instance_string (mi, true, &mi->gc); + struct gc_arena gc = gc_new(); + const char *prefix = multi_instance_string (mi, true, &gc); + if (prefix) + strncpynt(mi->msg_prefix, prefix, sizeof(mi->msg_prefix)); + else + mi->msg_prefix[0] = '\0'; set_prefix (mi); + gc_free(&gc); } void ungenerate_prefix (struct multi_instance *mi) { - mi->msg_prefix = NULL; + mi->msg_prefix[0] = '\0'; set_prefix (mi); } static const char * mi_prefix (const struct multi_instance *mi) { - if (mi && mi->msg_prefix) + if (mi && mi->msg_prefix[0]) return mi->msg_prefix; else return "UNDEF_I"; @@ -450,10 +493,10 @@ multi_del_iroutes (struct multi_context *m, if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN) { for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next) - mroute_helper_del_iroute (m->route_helper, ir); + mroute_helper_del_iroute46 (m->route_helper, ir->netbits); for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next ) - mroute_helper_del_iroute6 (m->route_helper, ir6); + mroute_helper_del_iroute46 (m->route_helper, ir6->netbits); } } @@ -500,7 +543,7 @@ multi_client_disconnect_script (struct multi_context *m, { struct argv argv = argv_new (); setenv_str (mi->context.c2.es, "script_type", "client-disconnect"); - argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script); + argv_parse_cmd (&argv, mi->context.options.client_disconnect_script); openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-disconnect"); argv_reset (&argv); } @@ -552,6 +595,17 @@ multi_close_instance (struct multi_context *m, } #endif +#ifdef ENABLE_ASYNC_PUSH + if (mi->inotify_watch != -1) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch); + mi->inotify_watch = -1; + } +#endif + + if (mi->context.c2.tls_multi->peer_id != MAX_PEER_ID) + m->instances[mi->context.c2.tls_multi->peer_id] = NULL; + schedule_remove_entry (m->schedule, (struct schedule_entry *) mi); ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, false); @@ -628,6 +682,13 @@ multi_uninit (struct multi_context *m) #endif m->hash = NULL; + free(m->instances); + +#ifdef ENABLE_ASYNC_PUSH + hash_free (m->inotify_watchers); + m->inotify_watchers = NULL; +#endif + schedule_free (m->schedule); mbuf_free (m->mbuf); ifconfig_pool_free (m->ifconfig_pool); @@ -704,6 +765,11 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real) mi->context.c2.push_reply_deferred = true; +#ifdef ENABLE_ASYNC_PUSH + mi->context.c2.push_request_received = false; + mi->inotify_watch = -1; +#endif + if (!multi_process_post (m, mi, MPP_PRE_SELECT)) { msg (D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization"); @@ -807,8 +873,8 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int */ status_printf (so, "TITLE%c%s", sep, title_string); status_printf (so, "TIME%c%s%c%u", sep, time_string (now, 0, false, &gc_top), sep, (unsigned int)now); - status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername", - sep, sep, sep, sep, sep, sep, sep, sep, sep); + status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cVirtual IPv6 Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)%cUsername%cClient ID%cPeer ID", + sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep); hash_iterator_init (m->hash, &hi); while ((he = hash_iterator_next (&hi))) { @@ -817,15 +883,28 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int if (!mi->halt) { - status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s", + status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u%c%s%c" +#ifdef MANAGEMENT_DEF_AUTH + "%lu" +#else + "" +#endif + "%c%"PRIu32, sep, tls_common_name (mi->context.c2.tls_multi, false), sep, mroute_addr_print (&mi->real, &gc), sep, print_in_addr_t (mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc), + sep, print_in6_addr (mi->reporting_addr_ipv6, IA_EMPTY_IF_UNDEF, &gc), sep, mi->context.c2.link_read_bytes, sep, mi->context.c2.link_write_bytes, sep, time_string (mi->created, 0, false, &gc), sep, (unsigned int)mi->created, - sep, tls_username (mi->context.c2.tls_multi, false)); + sep, tls_username (mi->context.c2.tls_multi, false), +#ifdef MANAGEMENT_DEF_AUTH + sep, mi->context.c2.mda_context.cid, +#else + sep, +#endif + sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX); } gc_free (&gc); } @@ -896,6 +975,13 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int status_flush (so); gc_free (&gc_top); } + +#ifdef ENABLE_ASYNC_PUSH + if (m->inotify_watchers) + { + msg (D_MULTI_DEBUG, "inotify watchers count: %d\n", hash_n_elements(m->inotify_watchers)); + } +#endif } /* @@ -1113,7 +1199,7 @@ multi_learn_in6_addr (struct multi_context *m, addr.len = 16; addr.type = MR_ADDR_IPV6; addr.netbits = 0; - memcpy( &addr.addr, &a6, sizeof(a6) ); + addr.v6.addr = a6; if (netbits >= 0) { @@ -1158,23 +1244,18 @@ multi_add_iroutes (struct multi_context *m, print_in_addr_t (ir->network, 0, &gc), multi_instance_string (mi, false, &gc)); - mroute_helper_add_iroute (m->route_helper, ir); + mroute_helper_add_iroute46 (m->route_helper, ir->netbits); multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false); } for ( ir6 = mi->context.options.iroutes_ipv6; ir6 != NULL; ir6 = ir6->next ) { - if (ir6->netbits >= 0) - msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s", + msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s", print_in6_addr (ir6->network, 0, &gc), ir6->netbits, multi_instance_string (mi, false, &gc)); - else - msg (D_MULTI_LOW, "MULTI: internal route %s -> %s", - print_in6_addr (ir6->network, 0, &gc), - multi_instance_string (mi, false, &gc)); - mroute_helper_add_iroute6 (m->route_helper, ir6); + mroute_helper_add_iroute46 (m->route_helper, ir6->netbits); multi_learn_in6_addr (m, mi, ir6->network, ir6->netbits, false); } @@ -1289,16 +1370,13 @@ 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 * session, though, as we don't even know if this client WANTS IPv6 */ - if ( mi->context.c1.tuntap->ipv6 && - mi->context.options.ifconfig_ipv6_pool_defined && + if ( mi->context.options.ifconfig_ipv6_pool_defined && ! mi->context.options.push_ifconfig_ipv6_defined ) { msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work. Use --ifconfig-ipv6-push for IPv6 then." ); @@ -1351,10 +1429,10 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) if ( mi->context.options.ifconfig_ipv6_pool_defined ) { mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6; - mi->context.c2.push_ifconfig_ipv6_remote = + mi->context.c2.push_ifconfig_ipv6_remote = mi->context.c1.tuntap->local_ipv6; - mi->context.c2.push_ifconfig_ipv6_netbits = - mi->context.options.ifconfig_ipv6_pool_netbits; + mi->context.c2.push_ifconfig_ipv6_netbits = + mi->context.options.ifconfig_ipv6_netbits; mi->context.c2.push_ifconfig_ipv6_defined = true; } } @@ -1371,8 +1449,7 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi) * way round ("dynamic IPv4, static IPv6") or "both static" makes sense * -> and so it's implemented right now */ - if ( mi->context.c1.tuntap->ipv6 && - mi->context.options.push_ifconfig_ipv6_defined ) + if ( mi->context.options.push_ifconfig_ipv6_defined ) { mi->context.c2.push_ifconfig_ipv6_local = mi->context.options.push_ifconfig_ipv6_local; @@ -1430,7 +1507,7 @@ multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi) setenv_del (mi->context.c2.es, "ifconfig_pool_remote_ip6"); setenv_del (mi->context.c2.es, "ifconfig_pool_ip6_netbits"); - if (mi->context.c1.tuntap->ipv6 && mi->context.c2.push_ifconfig_ipv6_defined) + if (mi->context.c2.push_ifconfig_ipv6_defined) { setenv_in6_addr (mi->context.c2.es, "ifconfig_pool_remote", @@ -1755,9 +1832,8 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi goto script_failed; } - argv_printf (&argv, "%sc %s", - mi->context.options.client_connect_script, - dc_file); + argv_parse_cmd (&argv, mi->context.options.client_connect_script); + argv_printf_cat (&argv, "%s", dc_file); if (openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-connect")) { @@ -1868,9 +1944,18 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi /* set our client's VPN endpoint for status reporting purposes */ mi->reporting_addr = mi->context.c2.push_ifconfig_local; + mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local; /* set context-level authentication flag */ mi->context.c2.context_auth = CAS_SUCCEEDED; + +#ifdef ENABLE_ASYNC_PUSH + /* authentication complete, send push reply */ + if (mi->context.c2.push_request_received) + { + process_incoming_push_request(&mi->context); + } +#endif } else { @@ -1900,6 +1985,58 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi mi->context.c2.push_reply_deferred = false; } +#ifdef ENABLE_ASYNC_PUSH +/* + * Called when inotify event is fired, which happens when acf file is closed or deleted. + * Continues authentication and sends push_reply. + */ +void +multi_process_file_closed (struct multi_context *m, const unsigned int mpp_flags) +{ + char buffer[INOTIFY_EVENT_BUFFER_SIZE]; + size_t buffer_i = 0; + int r = read (m->top.c2.inotify_fd, buffer, INOTIFY_EVENT_BUFFER_SIZE); + + while (buffer_i < r) + { + /* parse inotify events */ + struct inotify_event *pevent = (struct inotify_event *) &buffer[buffer_i]; + size_t event_size = sizeof (struct inotify_event) + pevent->len; + buffer_i += event_size; + + msg(D_MULTI_DEBUG, "MULTI: modified fd %d, mask %d", pevent->wd, pevent->mask); + + struct multi_instance* mi = hash_lookup(m->inotify_watchers, (void*) (unsigned long) pevent->wd); + + if (pevent->mask & IN_CLOSE_WRITE) + { + if (mi) + { + /* continue authentication and send push_reply */ + multi_process_post (m, mi, mpp_flags); + } + else + { + msg(D_MULTI_ERRORS, "MULTI: multi_instance not found!"); + } + } + else if (pevent->mask & IN_IGNORED) + { + /* this event is _always_ fired when watch is removed or file is deleted */ + if (mi) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long) pevent->wd); + mi->inotify_watch = -1; + } + } + else + { + msg(D_MULTI_ERRORS, "MULTI: unknown mask %d", pevent->mask); + } + } +} +#endif + /* * Add a mbuf buffer to a particular * instance. @@ -2060,19 +2197,50 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un if (!IS_SIG (&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT (&mi->context)))) { +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) + bool was_authenticated = false; + struct key_state *ks = NULL; + if (mi->context.c2.tls_multi) + { + ks = &mi->context.c2.tls_multi->session[TM_ACTIVE].key[KS_PRIMARY]; + was_authenticated = ks->authenticated; + } +#endif + /* figure timeouts and fetch possible outgoing to_link packets (such as ping or TLS control) */ pre_select (&mi->context); - if (!IS_SIG (&mi->context)) +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) + if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated) { - /* tell scheduler to wake us up at some point in the future */ - multi_schedule_context_wakeup(m, mi); + /* watch acf file */ + long watch_descriptor = inotify_add_watch(m->top.c2.inotify_fd, ks->auth_control_file, IN_CLOSE_WRITE | IN_ONESHOT); + if (watch_descriptor >= 0) + { + if (mi->inotify_watch != -1) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch); + } + hash_add (m->inotify_watchers, (const uintptr_t*)watch_descriptor, mi, true); + mi->inotify_watch = watch_descriptor; + } + else + { + msg(M_NONFATAL, "MULTI: inotify_add_watch error: %s", strerror(errno)); + } + } +#endif + if (!IS_SIG (&mi->context)) + { /* connection is "established" when SSL/TLS key negotiation succeeds and (if specified) auth user/pass succeeds */ if (!mi->connection_established_flag && CONNECTION_ESTABLISHED (&mi->context)) multi_connection_established (m, mi); + + /* tell scheduler to wake us up at some point in the future */ + multi_schedule_context_wakeup(m, mi); } } @@ -2107,6 +2275,72 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un return ret; } +void multi_process_float (struct multi_context* m, struct multi_instance* mi) +{ + struct mroute_addr real; + struct hash *hash = m->hash; + struct gc_arena gc = gc_new (); + + if (!mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) + goto done; + + const uint32_t hv = hash_value (hash, &real); + struct hash_bucket *bucket = hash_bucket (hash, hv); + + /* make sure that we don't float to an address taken by another client */ + struct hash_element *he = hash_lookup_fast (hash, bucket, &real, hv); + if (he) + { + struct multi_instance *ex_mi = (struct multi_instance *) he->value; + + struct tls_multi *m1 = mi->context.c2.tls_multi; + struct tls_multi *m2 = ex_mi->context.c2.tls_multi; + + /* do not float if target address is taken by client with another cert */ + if (!cert_hash_compare(m1->locked_cert_hash_set, m2->locked_cert_hash_set)) + { + msg (D_MULTI_LOW, "Disallow float to an address taken by another client %s", + multi_instance_string (ex_mi, false, &gc)); + + mi->context.c2.buf.len = 0; + + goto done; + } + + msg (D_MULTI_MEDIUM, "closing instance %s", multi_instance_string (ex_mi, false, &gc)); + multi_close_instance(m, ex_mi, false); + } + + msg (D_MULTI_MEDIUM, "peer %" PRIu32 " (%s) floated from %s to %s", + mi->context.c2.tls_multi->peer_id, + tls_common_name (mi->context.c2.tls_multi, false), + mroute_addr_print (&mi->real, &gc), + print_link_socket_actual (&m->top.c2.from, &gc)); + + /* change external network address of the remote peer */ + mi->real = real; + generate_prefix (mi); + + mi->context.c2.from = m->top.c2.from; + mi->context.c2.to_link_addr = &mi->context.c2.from; + + /* inherit parent link_socket and link_socket_info */ + mi->context.c2.link_socket = m->top.c2.link_socket; + mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from; + + tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from); + + ASSERT (hash_add (m->hash, &mi->real, mi, true)); + ASSERT (hash_add (m->iter, &mi->real, mi, true)); + +#ifdef MANAGEMENT_DEF_AUTH + ASSERT (hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, true)); +#endif + +done: + gc_free (&gc); +} + /* * Process packets in the TCP/UDP socket -> TUN/TAP interface direction, * i.e. client -> server direction. @@ -2121,6 +2355,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins unsigned int mroute_flags; struct multi_instance *mi; bool ret = true; + bool floated = false; if (m->pending) return true; @@ -2130,7 +2365,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins #ifdef MULTI_DEBUG_EVENT_LOOP printf ("TCP/UDP -> TUN [%d]\n", BLEN (&m->top.c2.buf)); #endif - multi_set_pending (m, multi_get_create_instance_udp (m)); + multi_set_pending (m, multi_get_create_instance_udp (m, &floated)); } else multi_set_pending (m, instance); @@ -2148,13 +2383,30 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins c->c2.buf = m->top.c2.buf; /* transfer from-addr from top-level context buffer to instance */ - c->c2.from = m->top.c2.from; + if (!floated) + c->c2.from = m->top.c2.from; } if (BLEN (&c->c2.buf) > 0) { + struct link_socket_info *lsi; + const uint8_t *orig_buf; + /* decrypt in instance context */ - process_incoming_link (c); + + perf_push (PERF_PROC_IN_LINK); + lsi = get_link_socket_info (c); + orig_buf = c->c2.buf.data; + if (process_incoming_link_part1(c, lsi, floated)) + { + if (floated) + { + multi_process_float (m, m->pending); + } + + process_incoming_link_part2(c, lsi, orig_buf); + } + perf_pop (); if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN) { @@ -2176,7 +2428,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins { /* IPv6 link-local address (fe80::xxx)? */ if ( (src.type & MR_ADDR_MASK) == MR_ADDR_IPV6 && - src.addr[0] == 0xfe && src.addr[1] == 0x80 ) + IN6_IS_ADDR_LINKLOCAL (&src.v6.addr) ) { /* do nothing, for now. TODO: add address learning */ } @@ -2478,10 +2730,18 @@ multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags) /* instance marked for wakeup? */ if (m->earliest_wakeup) { - set_prefix (m->earliest_wakeup); - ret = multi_process_post (m, m->earliest_wakeup, mpp_flags); + if (m->earliest_wakeup == (struct multi_instance*)&m->deferred_shutdown_signal) + { + schedule_remove_entry(m->schedule, (struct schedule_entry*) &m->deferred_shutdown_signal); + throw_signal(m->deferred_shutdown_signal.signal_received); + } + else + { + set_prefix (m->earliest_wakeup); + ret = multi_process_post (m, m->earliest_wakeup, mpp_flags); + clear_prefix (); + } m->earliest_wakeup = NULL; - clear_prefix (); } return ret; } @@ -2591,12 +2851,10 @@ multi_process_per_second_timers_dowork (struct multi_context *m) } void -multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers) +multi_top_init (struct multi_context *m, const struct context *top) { inherit_context_top (&m->top, top); - m->top.c2.buffers = NULL; - if (alloc_buffers) - m->top.c2.buffers = init_context_buffers (&top->c2.frame); + m->top.c2.buffers = init_context_buffers (&top->c2.frame); } void @@ -2606,6 +2864,48 @@ multi_top_free (struct multi_context *m) free_context_buffers (m->top.c2.buffers); } +static bool +is_exit_restart(int sig) +{ + return (sig == SIGUSR1 || sig == SIGTERM || sig == SIGHUP || sig == SIGINT); +} + +static void +multi_push_restart_schedule_exit(struct multi_context *m, bool next_server) +{ + struct hash_iterator hi; + struct hash_element *he; + struct timeval tv; + + /* tell all clients to restart */ + hash_iterator_init (m->iter, &hi); + while ((he = hash_iterator_next (&hi))) + { + struct multi_instance *mi = (struct multi_instance *) he->value; + if (!mi->halt) + { + send_control_channel_string (&mi->context, next_server ? "RESTART,[N]" : "RESTART", D_PUSH); + multi_schedule_context_wakeup(m, mi); + } + } + hash_iterator_free (&hi); + + /* reschedule signal */ + ASSERT (!openvpn_gettimeofday (&m->deferred_shutdown_signal.wakeup, NULL)); + tv.tv_sec = 2; + tv.tv_usec = 0; + tv_add (&m->deferred_shutdown_signal.wakeup, &tv); + + m->deferred_shutdown_signal.signal_received = m->top.sig->signal_received; + + schedule_add_entry (m->schedule, + (struct schedule_entry *) &m->deferred_shutdown_signal, + &m->deferred_shutdown_signal.wakeup, + compute_wakeup_sigma (&m->deferred_shutdown_signal.wakeup)); + + m->top.sig->signal_received = 0; +} + /* * Return true if event loop should break, * false if it should continue. @@ -2621,6 +2921,14 @@ multi_process_signal (struct multi_context *m) m->top.sig->signal_received = 0; return false; } + else if (proto_is_dgram(m->top.options.ce.proto) && + is_exit_restart(m->top.sig->signal_received) && + (m->deferred_shutdown_signal.signal_received == 0) && + m->top.options.ce.explicit_exit_notification != 0) + { + multi_push_restart_schedule_exit(m, m->top.options.ce.explicit_exit_notification == 2); + return false; + } return true; } diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index 2bc0c8a..0d369f3 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -42,6 +42,8 @@ #include "mtcp.h" #include "perf.h" +#define MULTI_PREFIX_MAX_LENGTH 256 + /* * Walk (don't run) through the routing table, * deleting old entries, and possibly multi_instance @@ -55,6 +57,13 @@ struct multi_reap }; +struct deferred_signal_schedule_entry +{ + struct schedule_entry se; + int signal_received; + struct timeval wakeup; +}; + /** * Server-mode state structure for one single VPN tunnel. * @@ -80,7 +89,7 @@ struct multi_instance { struct mroute_addr real; /**< External network address of the * remote peer. */ ifconfig_pool_handle vaddr_handle; - const char *msg_prefix; + char msg_prefix[MULTI_PREFIX_MAX_LENGTH]; /* queued outgoing data in Server/TCP mode */ unsigned int tcp_rwflags; @@ -88,6 +97,7 @@ struct multi_instance { bool socket_set_called; in_addr_t reporting_addr; /* IP address shown in status listing */ + struct in6_addr reporting_addr_ipv6; /* IPv6 address in status listing */ bool did_open_context; bool did_real_hash; @@ -102,6 +112,10 @@ struct multi_instance { struct context context; /**< The context structure storing state * for this VPN tunnel. */ + +#ifdef ENABLE_ASYNC_PUSH + int inotify_watch; /* watch descriptor for acf */ +#endif }; @@ -124,6 +138,9 @@ struct multi_context { # define MC_WORK_THREAD (MC_MULTI_THREADED_WORKER|MC_MULTI_THREADED_SCHEDULER) int thread_mode; + struct multi_instance** instances; /**< Array of multi_instances. An instance can be + * accessed using peer-id as an index. */ + struct hash *hash; /**< VPN tunnel instances indexed by real * address of the remote peer. */ struct hash *vhash; /**< VPN tunnel instances indexed by @@ -166,6 +183,13 @@ struct multi_context { * Timer object for stale route check */ struct event_timeout stale_routes_check_et; + +#ifdef ENABLE_ASYNC_PUSH + /* mapping between inotify watch descriptors and multi_instances */ + struct hash *inotify_watchers; +#endif + + struct deferred_signal_schedule_entry deferred_shutdown_signal; }; /* @@ -209,7 +233,7 @@ const char *multi_instance_string (const struct multi_instance *mi, bool null, s void multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int thread_mode); void multi_uninit (struct multi_context *m); -void multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers); +void multi_top_init (struct multi_context *m, const struct context *top); void multi_top_free (struct multi_context *m); struct multi_instance *multi_create_instance (struct multi_context *m, const struct mroute_addr *real); @@ -217,6 +241,16 @@ void multi_close_instance (struct multi_context *m, struct multi_instance *mi, b bool multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags); +/** + * Handles peer floating. + * + * If peer is floated to a taken address, either drops packet + * (if peer that owns address has different CN) or disconnects + * existing peer. Updates multi_instance with new address, + * updates hashtables in multi_context. + */ +void multi_process_float (struct multi_context* m, struct multi_instance* mi); + #define MPP_PRE_SELECT (1<<0) #define MPP_CONDITIONAL_PRE_SELECT (1<<1) #define MPP_CLOSE_ON_SIGNAL (1<<2) @@ -311,6 +345,18 @@ void multi_close_instance_on_signal (struct multi_context *m, struct multi_insta void init_management_callback_multi (struct multi_context *m); void uninit_management_callback_multi (struct multi_context *m); + +#ifdef ENABLE_ASYNC_PUSH +/** + * Called when inotify event is fired, which happens when acf file is closed or deleted. + * Continues authentication and sends push_repl + * + * @param m multi_context + * @param mpp_flags + */ +void multi_process_file_closed (struct multi_context *m, const unsigned int mpp_flags); +#endif + /* * Return true if our output queue is not full */ @@ -417,6 +463,12 @@ multi_route_defined (const struct multi_context *m, return true; } +/* + * Takes prefix away from multi_instance. + */ +void +ungenerate_prefix (struct multi_instance *mi); + /* * Set a msg() function prefix with our current client instance ID. */ @@ -425,10 +477,10 @@ static inline void set_prefix (struct multi_instance *mi) { #ifdef MULTI_DEBUG_EVENT_LOOP - if (mi->msg_prefix) + if (mi->msg_prefix[0]) printf ("[%s]\n", mi->msg_prefix); #endif - msg_set_prefix (mi->msg_prefix); + msg_set_prefix (mi->msg_prefix[0] ? mi->msg_prefix : NULL); } static inline void @@ -573,10 +625,5 @@ multi_set_pending (struct multi_context *m, struct multi_instance *mi) m->pending = mi; } -static inline void -multi_release_io_lock (struct multi_context *m) -{ -} - #endif /* P2MP_SERVER */ #endif /* MULTI_H */ diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c index ff48706..d71381d 100644 --- a/src/openvpn/occ.c +++ b/src/openvpn/occ.c @@ -379,7 +379,7 @@ process_received_occ_msg (struct context *c) && 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)) - msg (M_INFO, "NOTE: This connection is unable to accomodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.", + msg (M_INFO, "NOTE: This connection is unable to accommodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.", c->c2.max_send_size_local); } event_timeout_clear (&c->c2.occ_mtu_load_test_interval); diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index 823c3dd..5fb2fd9 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -138,7 +138,7 @@ openvpn_main (int argc, char *argv[]) return 1; #endif -#ifdef WIN32 +#ifdef _WIN32 SetConsoleOutputCP (CP_UTF8); #endif @@ -172,7 +172,7 @@ openvpn_main (int argc, char *argv[]) /* initialize environmental variable store */ c.es = env_set_create (NULL); -#ifdef WIN32 +#ifdef _WIN32 set_win_sys_path_via_env (c.es); #endif @@ -220,7 +220,7 @@ openvpn_main (int argc, char *argv[]) /* print version number */ msg (M_INFO, "%s", title_string); -#ifdef WIN32 +#ifdef _WIN32 show_windows_version(M_INFO); #endif show_library_versions(M_INFO); @@ -312,7 +312,7 @@ openvpn_main (int argc, char *argv[]) return 0; /* NOTREACHED */ } -#ifdef WIN32 +#ifdef _WIN32 int wmain (int argc, wchar_t *wargv[]) { char **argv; diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index 36c3100..fa5cc1d 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -31,7 +31,7 @@ #include "crypto.h" #include "ssl.h" #include "packet_id.h" -#include "lzo.h" +#include "comp.h" #include "tun.h" #include "interval.h" #include "status.h" @@ -62,14 +62,12 @@ struct key_schedule /* pre-shared static key, read from a file */ struct key_ctx_bi static_key; -#ifdef ENABLE_SSL /* our global SSL context */ struct tls_root_ctx ssl_ctx; - /* optional authentication HMAC key for TLS control channel */ - struct key_ctx_bi tls_auth_key; - -#endif /* ENABLE_SSL */ + /* optional TLS control channel wrapping */ + struct key_type tls_auth_key_type; + struct key_ctx_bi tls_wrap_key; #else /* ENABLE_CRYPTO */ int dummy; #endif /* ENABLE_CRYPTO */ @@ -104,10 +102,10 @@ struct context_buffers struct buffer decrypt_buf; #endif - /* workspace buffers for LZO compression */ -#ifdef ENABLE_LZO - struct buffer lzo_compress_buf; - struct buffer lzo_decompress_buf; + /* workspace buffers for compression */ +#ifdef USE_COMP + struct buffer compress_buf; + struct buffer decompress_buf; #endif /* @@ -165,6 +163,9 @@ struct context_1 /* tunnel session keys */ struct key_schedule ks; + /* preresolved and cached host names */ + struct cached_dns_entry *dns_cache; + /* persist crypto sequence number to/from file */ struct packet_id_persist pid_persist; @@ -184,17 +185,13 @@ struct context_1 struct status_output *status_output; bool status_output_owned; -#ifdef ENABLE_HTTP_PROXY /* HTTP proxy object */ struct http_proxy_info *http_proxy; bool http_proxy_owned; -#endif -#ifdef ENABLE_SOCKS /* SOCKS proxy object */ struct socks_proxy_info *socks_proxy; bool socks_proxy_owned; -#endif #if P2MP @@ -213,6 +210,10 @@ struct context_1 struct user_pass *auth_user_pass; /**< Username and password for * authentication. */ + + const char *ciphername; /**< Data channel cipher from config file */ + const char *authname; /**< Data channel auth from config file */ + int keysize; /**< Data channel keysize from config file */ #endif }; @@ -247,6 +248,9 @@ struct context_2 # define MANAGEMENT_READ (1<<6) # define MANAGEMENT_WRITE (1<<7) # endif +#ifdef ENABLE_ASYNC_PUSH +# define FILE_CLOSED (1<<8) +#endif unsigned int event_set_status; @@ -335,8 +339,6 @@ struct context_2 /* * TLS-mode crypto objects. */ -#ifdef ENABLE_SSL - struct tls_multi *tls_multi; /**< TLS state structure for this VPN * tunnel. */ @@ -357,23 +359,19 @@ struct context_2 /* throw this signal on TLS errors */ int tls_exit_signal; -#endif /* ENABLE_SSL */ - struct crypto_options crypto_options; /**< Security parameters and crypto state * used by the \link data_crypto Data * Channel Crypto module\endlink to * process data channel packet. */ - /* used to keep track of data channel packet sequence numbers */ - struct packet_id packet_id; struct event_timeout packet_id_persist_interval; #endif /* ENABLE_CRYPTO */ -#ifdef ENABLE_LZO - struct lzo_compress_workspace lzo_compwork; - /**< Compression workspace used by the +#ifdef USE_COMP + struct compress_context *comp_context; + /**< Compression context used by the * \link compression Data Channel * Compression module\endlink. */ #endif @@ -393,11 +391,6 @@ struct context_2 struct buffer to_tun; struct buffer to_link; - /* - * IPv4 TUN device? - */ - bool ipv4_tun; - /* should we print R|W|r|w to console on packet transfers? */ bool log_rw; @@ -423,6 +416,10 @@ struct context_2 time_t update_timeout_random_component; struct timeval timeout_random_component; + /* Timer for everything up to the first packet from the *OpenVPN* server + * socks, http proxy, and tcp packets do not count */ + struct event_timeout server_poll_interval; + /* indicates that the do_up_delay function has run */ bool do_up_ran; @@ -446,13 +443,14 @@ struct context_2 #if P2MP_SERVER /* --ifconfig endpoints to be pushed to client */ bool push_reply_deferred; +#ifdef ENABLE_ASYNC_PUSH + bool push_request_received; +#endif bool push_ifconfig_defined; time_t sent_push_reply_expiry; 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; @@ -474,11 +472,9 @@ struct context_2 /* hash of pulled options, so we can compare when options change */ bool pulled_options_md5_init_done; - struct md5_state pulled_options_state; + md_ctx_t pulled_options_state; struct md5_digest pulled_options_digest; - struct event_timeout server_poll_interval; - struct event_timeout scheduled_exit; int scheduled_exit_signal; #endif @@ -491,6 +487,10 @@ struct context_2 #ifdef MANAGEMENT_DEF_AUTH struct man_def_auth_context mda_context; #endif + +#ifdef ENABLE_ASYNC_PUSH + int inotify_fd; /* descriptor for monitoring file changes */ +#endif }; @@ -566,7 +566,7 @@ struct context * have been compiled in. */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #define TLS_MODE(c) ((c)->c2.tls_multi != NULL) #define PROTO_DUMP_FLAGS (check_debug_level (D_LINK_RW_VERBOSE) ? (PD_SHOW_DATA|PD_VERBOSE) : 0) #define PROTO_DUMP(buf, gc) protocol_dump((buf), \ @@ -591,4 +591,7 @@ struct context #define CIPHER_ENABLED(c) (false) #endif +/* this represents "disabled peer-id" */ +#define MAX_PEER_ID 0xFFFFFF + #endif diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj index d691500..8dfbea5 100644 --- a/src/openvpn/openvpn.vcxproj +++ b/src/openvpn/openvpn.vcxproj @@ -64,7 +64,7 @@ $(SOURCEBASE);%(AdditionalIncludeDirectories) - libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;rpcrt4.lib;%(AdditionalDependencies) + libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;%(AdditionalDependencies) $(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories) true Console @@ -89,7 +89,7 @@ $(SOURCEBASE);%(AdditionalIncludeDirectories) - libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;rpcrt4.lib;%(AdditionalDependencies) + libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;%(AdditionalDependencies) $(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories) true Console @@ -102,6 +102,9 @@ + + + @@ -171,6 +174,9 @@ + + + @@ -262,4 +268,4 @@ - \ No newline at end of file + diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters index dbed3cd..8b6a269 100644 --- a/src/openvpn/openvpn.vcxproj.filters +++ b/src/openvpn/openvpn.vcxproj.filters @@ -207,6 +207,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -227,6 +236,12 @@ Header Files + + Header Files + + + Header Files + Header Files @@ -449,9 +464,6 @@ Header Files - - Header Files - diff --git a/src/openvpn/options.c b/src/openvpn/options.c index a49a4fb..7f128c3 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -56,39 +56,38 @@ #include "helper.h" #include "manage.h" #include "forward.h" +#include "ssl_verify.h" +#include "platform.h" #include #include "memdbg.h" const char title_string[] = PACKAGE_STRING +#ifdef CONFIGURE_GIT_REVISION + " [git:" CONFIGURE_GIT_REVISION CONFIGURE_GIT_FLAGS "]" +#endif " " TARGET_ALIAS #ifdef ENABLE_CRYPTO -#ifdef ENABLE_SSL -#if defined(ENABLE_CRYPTO_POLARSSL) - " [SSL (PolarSSL)]" +#if defined(ENABLE_CRYPTO_MBEDTLS) + " [SSL (mbed TLS)]" #elif defined(ENABLE_CRYPTO_OPENSSL) " [SSL (OpenSSL)]" #else " [SSL]" -#endif /* defined(ENABLE_CRYPTO_POLARSSL) */ -#else /* ! ENABLE_SSL */ -#if defined(ENABLE_CRYPTO_POLARSSL) - " [CRYPTO (PolarSSL)]" -#elif defined(ENABLE_CRYPTO_OPENSSL) - " [CRYPTO (OpenSSL)]" -#else - " [CRYPTO]" -#endif /* defined(ENABLE_CRYPTO_POLARSSL) */ -#endif /* ENABLE_SSL */ +#endif /* defined(ENABLE_CRYPTO_MBEDTLS) */ #endif /* ENABLE_CRYPTO */ +#ifdef USE_COMP #ifdef ENABLE_LZO -#ifdef ENABLE_LZO_STUB - " [LZO (STUB)]" -#else " [LZO]" #endif +#ifdef ENABLE_LZ4 + " [LZ4]" +#endif +#ifdef ENABLE_COMP_STUB + " [COMP_STUB]" #endif +#endif /* USE_COMP */ #if EPOLL " [EPOLL]" #endif @@ -99,9 +98,15 @@ const char title_string[] = " [PKCS11]" #endif #if ENABLE_IP_PKTINFO - " [MH]" +# if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + " [MH/PKTINFO]" +# elif defined(IP_RECVDSTADDR) + " [MH/RECVDA]" +# endif +#endif +#ifdef HAVE_AEAD_CIPHER_MODES + " [AEAD]" #endif - " [IPv6]" " built on " __DATE__ ; @@ -125,11 +130,11 @@ static const char usage_message[] = " p = udp (default), tcp-server, or tcp-client\n" "--proto-force p : only consider protocol p in list of connection profiles.\n" " p = udp6, tcp6-server, or tcp6-client (ipv6)\n" - "--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" + "--connect-retry n [m] : For client, number of seconds to wait between\n" + " connection retries (default=%d). On repeated retries\n" + " the wait time is exponentially increased to a maximum of m\n" + " (default=%d).\n" "--connect-retry-max n : Maximum connection attempt retries, default infinite.\n" -#ifdef ENABLE_HTTP_PROXY "--http-proxy s p [up] [auth] : Connect to remote host\n" " through an HTTP proxy at address s and port p.\n" " If proxy authentication is required,\n" @@ -139,21 +144,16 @@ static const char usage_message[] = "--http-proxy s p 'auto[-nct]' : Like the above directive, but automatically\n" " determine auth method and query for username/password\n" " if needed. auto-nct disables weak proxy auth methods.\n" - "--http-proxy-retry : Retry indefinitely on HTTP proxy errors.\n" - "--http-proxy-timeout n : Proxy timeout in seconds, default=5.\n" "--http-proxy-option type [parm] : Set extended HTTP proxy options.\n" " Repeat to set multiple options.\n" " VERSION version (default=1.0)\n" " AGENT user-agent\n" -#endif -#ifdef ENABLE_SOCKS "--socks-proxy s [p] [up] : Connect to remote host through a Socks5 proxy at\n" " address s and port p (default port = 1080).\n" " If proxy authentication is required,\n" " up is a file containing username/password on 2 lines, or\n" " 'stdin' to prompt for console.\n" "--socks-proxy-retry : Retry indefinitely on Socks proxy errors.\n" -#endif "--resolv-retry n: If hostname resolve fails for --remote, retry\n" " resolve for n seconds before failing (disabled by default).\n" " Set n=\"infinite\" to retry indefinitely.\n" @@ -162,16 +162,12 @@ static const char usage_message[] = "--ipchange cmd : Run command cmd on remote ip address initial\n" " setting or change -- execute as: cmd ip-address port#\n" "--port port : TCP/UDP port # for both local and remote.\n" - "--lport port : TCP/UDP port # for local (default=%d). Implies --bind.\n" - "--rport port : TCP/UDP port # for remote (default=%d).\n" + "--lport port : TCP/UDP port # for local (default=%s). Implies --bind.\n" + "--rport port : TCP/UDP port # for remote (default=%s).\n" "--bind : Bind to local address and port. (This is the default unless\n" " --proto tcp-client" -#ifdef ENABLE_HTTP_PROXY " or --http-proxy" -#endif -#ifdef ENABLE_SOCKS " or --socks-proxy" -#endif " is used).\n" "--nobind : Do not bind to local address and port.\n" "--dev tunX|tapX : tun/tap device (X can be omitted for dynamic device.\n" @@ -182,7 +178,6 @@ static const char usage_message[] = " /dev/net/tun, /dev/tun, /dev/tap, etc.\n" "--lladdr hw : Set the link layer address of the tap device.\n" "--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n" - "--tun-ipv6 : Build tun link capable of forwarding IPv6 traffic.\n" #ifdef ENABLE_IPROUTE "--iproute cmd : Use this command instead of default " IPROUTE_PATH ".\n" #endif @@ -207,9 +202,7 @@ static const char usage_message[] = "--route-ipv6 network/bits [gateway] [metric] :\n" " Add IPv6 route to routing table after connection\n" " is established. Multiple routes can be specified.\n" - " gateway default: taken from --route-ipv6-gateway or --ifconfig\n" - "--max-routes n : Specify the maximum number of routes that may be defined\n" - " or pulled from a server.\n" + " gateway default: taken from 'remote' in --ifconfig-ipv6\n" "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n" "--route-metric m : Specify a default metric for use with --route.\n" "--route-delay n [w] : Delay n seconds after connection initiation before\n" @@ -234,9 +227,7 @@ 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 @@ -335,6 +326,7 @@ static const char usage_message[] = "--log file : Output log to file which is created/truncated on open.\n" "--log-append file : Append log to file, or create file if nonexistent.\n" "--suppress-timestamps : Don't log timestamps to stdout/stderr.\n" + "--machine-readable-output : Always log timestamp, message flags to stdout/stderr.\n" "--writepid file : Write main process ID to file.\n" "--nice n : Change process priority (>0 = lower, <0 = higher).\n" "--echo [parms ...] : Echo parameters to log output.\n" @@ -359,12 +351,15 @@ static const char usage_message[] = #ifdef ENABLE_DEBUG "--gremlin mask : Special stress testing mode (for debugging only).\n" #endif -#ifdef ENABLE_LZO - "--comp-lzo : Use fast LZO compression -- may add up to 1 byte per\n" +#if defined(USE_COMP) + "--compress alg : Use compression algorithm alg\n" +#if defined(ENABLE_LZO) + "--comp-lzo : Use LZO compression -- may add up to 1 byte per\n" " packet for uncompressible data.\n" "--comp-noadapt : Don't use adaptive compression when --comp-lzo\n" " is specified.\n" #endif +#endif #ifdef ENABLE_MANAGEMENT "--management ip port [pass] : Enable a TCP server on ip:port to handle\n" " management functions. pass is a password file\n" @@ -442,6 +437,9 @@ static const char usage_message[] = " Only valid in a client-specific config file.\n" "--client-cert-not-required : Don't require client certificate, client\n" " will authenticate using username/password.\n" + "--verify-client-cert [none|optional|require] : perform no, optional or\n" + " mandatory client certificate verification.\n" + " Default is to require the client to supply a certificate.\n" "--username-as-common-name : For auth-user-pass authentication, use\n" " the authenticated username as the common name,\n" " rather than the common name from the client cert.\n" @@ -449,6 +447,11 @@ static const char usage_message[] = " run command cmd to verify. If method='via-env', pass\n" " user/pass via environment, if method='via-file', pass\n" " user/pass via temporary file.\n" + "--auth-gen-token [lifetime] Generate a random authentication token which is pushed\n" + " to each client, replacing the password. Usefull when\n" + " OTP based two-factor auth mechanisms are in use and\n" + " --reneg-* options are enabled. Optionally a lifetime in seconds\n" + " for generated tokens can be set.\n" "--opt-verify : Clients that connect with options that are incompatible\n" " with those of the server will be disconnected.\n" "--auth-user-pass-optional : Allow connections by clients that don't\n" @@ -476,6 +479,9 @@ static const char usage_message[] = "--stale-routes-check n [t] : Remove routes with a last activity timestamp\n" " older than n seconds. Run this check every t\n" " seconds (defaults to n).\n" + "--explicit-exit-notify [n] : In UDP server mode send [RESTART] command on exit/restart to connected\n" + " clients. n = 1 - reconnect to same server,\n" + " 2 - advance to next server, default=1.\n" #if PORT_SHARE "--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" @@ -493,13 +499,20 @@ static const char usage_message[] = "--pull : Accept certain config file options from the peer as if they\n" " were part of the local config file. Must be specified\n" " when connecting to a '--mode server' remote host.\n" + "--pull-filter accept|ignore|reject t : Filter each option received from the\n" + " server if it starts with the text t. The action flag accept,\n" + " ignore or reject causes the option to be allowed, removed or\n" + " rejected with error. May be specified multiple times, and\n" + " each filter is applied in the order of appearance.\n" "--auth-retry t : How to handle auth failures. Set t to\n" " none (default), interact, or nointeract.\n" "--static-challenge t e : Enable static challenge/response protocol using\n" " challenge text t, with e indicating echo flag (0|1)\n" - "--server-poll-timeout n : when polling possible remote servers to connect to\n" + "--connect-timeout n : when polling possible remote servers to connect to\n" " in a round-robin fashion, spend no more than n seconds\n" " waiting for a response before trying the next server.\n" + "--allow-recursive-routing : When this option is set, OpenVPN will not drop\n" + " incoming tun packets with same destination as host.\n" #endif #ifdef ENABLE_OCC "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n" @@ -522,13 +535,15 @@ static const char usage_message[] = "--cipher alg : Encrypt packets with cipher algorithm alg\n" " (default=%s).\n" " Set alg=none to disable encryption.\n" + "--ncp-ciphers list : List of ciphers that are allowed to be negotiated.\n" + "--ncp-disable : Disable cipher negotiation.\n" "--prng alg [nsl] : For PRNG, use digest algorithm alg, and\n" " nonce_secret_len=nsl. Set alg=none to disable PRNG.\n" #ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH "--keysize n : Size of cipher key in bits (optional).\n" " If unspecified, defaults to cipher-specific default.\n" #endif -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS "--engine [name] : Enable OpenSSL hardware crypto engine functionality.\n" #endif "--no-replay : Disable replay protection.\n" @@ -545,7 +560,6 @@ static const char usage_message[] = "--use-prediction-resistance: Enable prediction resistance on the random\n" " number generator.\n" #endif -#ifdef ENABLE_SSL "\n" "TLS Key Negotiation Options:\n" "(These options are meaningful only for TLS-mode)\n" @@ -555,15 +569,10 @@ static const char usage_message[] = " number, such as 1 (default), 2, etc.\n" "--ca file : Certificate authority file in .pem format containing\n" " root certificate.\n" -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS "--capath dir : A directory of trusted certificates (CAs" -#if OPENSSL_VERSION_NUMBER >= 0x00907000L " and CRLs).\n" -#else /* OPENSSL_VERSION_NUMBER >= 0x00907000L */ - ").\n" - " WARNING: no support of CRL available with this version.\n" -#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L */ -#endif /* ENABLE_CRYPTO_POLARSSL */ +#endif /* ENABLE_CRYPTO_MBEDTLS */ "--dh file : File containing Diffie Hellman parameters\n" " in .pem format (for --tls-server only).\n" " Use \"openssl dhparam -out dh1024.pem 1024\" to generate.\n" @@ -575,7 +584,7 @@ static const char usage_message[] = " will accept from the peer. If version is unrecognized and 'or-highest'\n" " is specified, require max TLS version supported by SSL implementation.\n" "--tls-version-max : sets the maximum TLS version we will use.\n" -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS "--pkcs12 file : PKCS#12 file containing local private key, local certificate\n" " and optionally the root CA certificate.\n" #endif @@ -584,7 +593,7 @@ static const char usage_message[] = " Default is CN in the Subject field.\n" #endif "--verify-hash : Specify SHA1 fingerprint for level-1 cert.\n" -#ifdef WIN32 +#ifdef _WIN32 "--cryptoapicert select-string : Load the certificate and private key from the\n" " Windows Certificate System Store.\n" #endif @@ -602,10 +611,17 @@ static const char usage_message[] = "--single-session: Allow only one session (reset state on restart).\n" "--tls-exit : Exit on TLS negotiation failure.\n" "--tls-auth f [d]: Add an additional layer of authentication on top of the TLS\n" - " control channel to protect against DoS attacks.\n" - " f (required) is a shared-secret passphrase file.\n" + " control channel to protect against attacks on the TLS stack\n" + " and DoS attacks.\n" + " f (required) is a shared-secret key file.\n" " The optional d parameter controls key directionality,\n" " see --secret option for more info.\n" + "--tls-crypt key : Add an additional layer of authenticated encryption on top\n" + " of the TLS control channel to hide the TLS certificate,\n" + " provide basic post-quantum security and protect against\n" + " attacks on the TLS stack and DoS attacks.\n" + " key (required) provides the pre-shared key file.\n" + " 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 ['dir']: Check peer certificate against a CRL.\n" @@ -622,11 +638,12 @@ 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" -#ifdef ENABLE_X509_TRACK "--x509-track x : Save peer X509 attribute x in environment for use by\n" " plugins and management interface.\n" +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 + "--keying-material-exporter label len : Save Exported Keying Material (RFC5705)\n" + " of len bytes (min. 16 bytes) using label in environment for use by plugins.\n" #endif -#if OPENSSL_VERSION_NUMBER >= 0x00907000L || ENABLE_CRYPTO_POLARSSL "--remote-cert-ku v ... : Require that the peer certificate was signed with\n" " explicit key usage, you can specify more than one value.\n" " value should be given in hex format.\n" @@ -636,8 +653,6 @@ static const char usage_message[] = "--remote-cert-tls t: Require that peer certificate was signed with explicit\n" " key usage and extended key usage based on RFC3280 TLS rules.\n" " t = 'client' | 'server'.\n" -#endif /* OPENSSL_VERSION_NUMBER || ENABLE_CRYPTO_POLARSSL */ -#endif /* ENABLE_SSL */ #ifdef ENABLE_PKCS11 "\n" "PKCS#11 Options:\n" @@ -662,10 +677,8 @@ static const char usage_message[] = "--show-ciphers : Show cipher algorithms to use with --cipher option.\n" "--show-digests : Show message digest algorithms to use with --auth option.\n" "--show-engines : Show hardware crypto accelerator engines (if available).\n" -#ifdef ENABLE_SSL "--show-tls : Show all TLS ciphers (TLS used only as a control channel).\n" -#endif -#ifdef WIN32 +#ifdef _WIN32 "\n" "Windows Specific:\n" "--win-sys path : Pathname of Windows system directory. Default is the pathname\n" @@ -715,7 +728,9 @@ static const char usage_message[] = " optional parameter controls the initial state of ex.\n" "--show-net-up : Show " PACKAGE_NAME "'s view of routing table and net adapter list\n" " after TAP adapter is up and routes have been added.\n" +#ifdef _WIN32 "--block-outside-dns : Block DNS on other network adapters to prevent DNS leaks\n" +#endif "Windows Standalone Options:\n" "\n" "--show-adapters : Show all TAP-Windows adapters.\n" @@ -775,10 +790,13 @@ init_options (struct options *o, const bool init_gc) } o->mode = MODE_POINT_TO_POINT; o->topology = TOP_NET30; - o->ce.proto = PROTO_UDPv4; + o->ce.proto = PROTO_UDP; + o->ce.af = AF_UNSPEC; + o->ce.bind_ipv6_only = false; o->ce.connect_retry_seconds = 5; - o->ce.connect_timeout = 10; - o->ce.connect_retry_max = 0; + o->ce.connect_retry_seconds_max = 300; + o->ce.connect_timeout = 120; + o->connect_retry_max = 0; o->ce.local_port = o->ce.remote_port = OPENVPN_PORT; o->verbosity = 1; o->status_file_update_freq = 60; @@ -789,8 +807,8 @@ init_options (struct options *o, const bool init_gc) o->ce.mtu_discover_type = -1; o->ce.mssfix = MSSFIX_DEFAULT; o->route_delay_window = 30; - o->max_routes = MAX_ROUTES_DEFAULT; o->resolve_retry_seconds = RESOLV_RETRY_INFINITE; + o->resolve_in_advance = false; o->proto_force = -1; #ifdef ENABLE_OCC o->occ = true; @@ -806,7 +824,7 @@ init_options (struct options *o, const bool init_gc) #ifdef TARGET_LINUX o->tuntap_options.txqueuelen = 100; #endif -#ifdef WIN32 +#ifdef _WIN32 #if 0 o->tuntap_options.ip_win32_type = IPW32_SET_ADAPTIVE; #else @@ -829,13 +847,16 @@ init_options (struct options *o, const bool init_gc) #endif #if P2MP o->scheduled_exit_interval = 5; - o->server_poll_timeout = 0; #endif #ifdef ENABLE_CRYPTO o->ciphername = "BF-CBC"; - o->ciphername_defined = true; +#ifdef HAVE_AEAD_CIPHER_MODES /* IV_NCP=2 requires GCM support */ + o->ncp_enabled = true; +#else + o->ncp_enabled = false; +#endif + o->ncp_ciphers = "AES-256-GCM:AES-128-GCM"; o->authname = "SHA1"; - o->authname_defined = true; o->prng_hash = "SHA1"; o->prng_nonce_secret_len = 16; o->replay = true; @@ -846,25 +867,27 @@ init_options (struct options *o, const bool init_gc) #ifdef ENABLE_PREDICTION_RESISTANCE o->use_prediction_resistance = false; #endif -#ifdef ENABLE_SSL o->key_method = 2; o->tls_timeout = 2; + o->renegotiate_bytes = -1; o->renegotiate_seconds = 3600; o->handshake_window = 60; o->transition_window = 3600; + o->ecdh_curve = NULL; #ifdef ENABLE_X509ALTUSERNAME o->x509_username_field = X509_USERNAME_FIELD_DEFAULT; #endif -#endif /* ENABLE_SSL */ #endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 o->pkcs11_pin_cache_period = -1; #endif /* ENABLE_PKCS11 */ -/* tmp is only used in P2MP server context */ +/* P2MP server context features */ #if P2MP_SERVER + o->auth_token_generate = false; + /* Set default --tmp-dir */ -#ifdef WIN32 +#ifdef _WIN32 /* On Windows, find temp dir via enviroment variables */ o->tmp_dir = win_get_tempdir(); #else @@ -873,8 +896,9 @@ init_options (struct options *o, const bool init_gc) if( !o->tmp_dir ) { o->tmp_dir = "/tmp"; } -#endif /* WIN32 */ +#endif /* _WIN32 */ #endif /* P2MP_SERVER */ + o->allow_recursive_routing = false; } void @@ -886,6 +910,37 @@ uninit_options (struct options *o) } } +struct pull_filter +{ +# define PUF_TYPE_UNDEF 0 /** undefined filter type */ +# define PUF_TYPE_ACCEPT 1 /** filter type to accept a matching option */ +# define PUF_TYPE_IGNORE 2 /** filter type to ignore a matching option */ +# define PUF_TYPE_REJECT 3 /** filter type to reject and trigger SIGUSR1 */ + int type; + int size; + char *pattern; + struct pull_filter *next; +}; + +struct pull_filter_list +{ + struct pull_filter *head; + struct pull_filter *tail; +}; + +static const char * +pull_filter_type_name (int type) +{ + if (type == PUF_TYPE_ACCEPT) + return "accept"; + if (type == PUF_TYPE_IGNORE) + return "ignore"; + if (type == PUF_TYPE_REJECT) + return "reject"; + else + return "???"; +} + #ifndef ENABLE_SMALL #define SHOW_PARM(name, value, format) msg(D_SHOW_PARMS, " " #name " = " format, (value)) @@ -902,26 +957,22 @@ setenv_connection_entry (struct env_set *es, const struct connection_entry *e, const int i) { - setenv_str_i (es, "proto", proto2ascii (e->proto, false), i); + setenv_str_i (es, "proto", proto2ascii (e->proto, e->af, false), i); setenv_str_i (es, "local", e->local, i); - setenv_int_i (es, "local_port", e->local_port, i); + setenv_str_i (es, "local_port", e->local_port, i); setenv_str_i (es, "remote", e->remote, i); - setenv_int_i (es, "remote_port", e->remote_port, i); + setenv_str_i (es, "remote_port", e->remote_port, i); -#ifdef ENABLE_HTTP_PROXY if (e->http_proxy_options) { setenv_str_i (es, "http_proxy_server", e->http_proxy_options->server, i); - setenv_int_i (es, "http_proxy_port", e->http_proxy_options->port, i); + setenv_str_i (es, "http_proxy_port", e->http_proxy_options->port, i); } -#endif -#ifdef ENABLE_SOCKS if (e->socks_proxy_server) { setenv_str_i (es, "socks_proxy_server", e->socks_proxy_server, i); - setenv_int_i (es, "socks_proxy_port", e->socks_proxy_port, i); + setenv_str_i (es, "socks_proxy_port", e->socks_proxy_port, i); } -#endif } void @@ -1064,7 +1115,7 @@ string_substitute (const char *src, int from, int to, struct gc_arena *gc) return ret; } -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO static uint8_t * parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_arena *gc) { @@ -1098,7 +1149,7 @@ parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_aren } #endif -#ifdef WIN32 +#ifdef _WIN32 #ifndef ENABLE_SMALL @@ -1140,8 +1191,10 @@ show_tuntap_options (const struct tuntap_options *o) show_dhcp_option_addrs ("NBDD", o->nbdd, o->nbdd_len); } +#endif #endif +#if defined(_WIN32) || defined(TARGET_ANDROID) static void dhcp_option_address_parse (const char *name, const char *parm, in_addr_t *array, int *len, int msglevel) { @@ -1231,9 +1284,11 @@ 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_BOOL (auth_token_generate); + SHOW_INT (auth_token_lifetime); #if PORT_SHARE SHOW_STR (port_share_host); - SHOW_INT (port_share_port); + SHOW_STR (port_share_port); #endif #endif /* P2MP_SERVER */ @@ -1298,19 +1353,27 @@ option_iroute_ipv6 (struct options *o, #endif /* P2MP_SERVER */ #endif /* P2MP */ -#if defined(ENABLE_HTTP_PROXY) && !defined(ENABLE_SMALL) +#ifndef ENABLE_SMALL static void show_http_proxy_options (const struct http_proxy_options *o) { + int i; msg (D_SHOW_PARMS, "BEGIN http_proxy"); SHOW_STR (server); - SHOW_INT (port); + SHOW_STR (port); SHOW_STR (auth_method_string); SHOW_STR (auth_file); - SHOW_BOOL (retry); - SHOW_INT (timeout); SHOW_STR (http_version); SHOW_STR (user_agent); + for (i=0; i < MAX_CUSTOM_HTTP_HEADER && o->custom_headers[i].name;i++) + { + if (o->custom_headers[i].content) + msg (D_SHOW_PARMS, " custom_header[%d] = %s: %s", i, + o->custom_headers[i].name, o->custom_headers[i].content); + else + msg (D_SHOW_PARMS, " custom_header[%d] = %s", i, + o->custom_headers[i].name); + } msg (D_SHOW_PARMS, "END http_proxy"); } #endif @@ -1320,9 +1383,7 @@ 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 @@ -1332,50 +1393,43 @@ void rol_check_alloc (struct options *options) { if (!options->routes) - options->routes = new_route_option_list (options->max_routes, &options->gc); + options->routes = new_route_option_list (&options->gc); } void rol6_check_alloc (struct options *options) { if (!options->routes_ipv6) - options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc); + options->routes_ipv6 = new_route_ipv6_option_list (&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 #ifndef ENABLE_SMALL static void show_connection_entry (const struct connection_entry *o) { - msg (D_SHOW_PARMS, " proto = %s", proto2ascii (o->proto, false)); + msg (D_SHOW_PARMS, " proto = %s", proto2ascii (o->proto, o->af, false)); SHOW_STR (local); - SHOW_INT (local_port); + SHOW_STR (local_port); SHOW_STR (remote); - SHOW_INT (remote_port); + SHOW_STR (remote_port); SHOW_BOOL (remote_float); SHOW_BOOL (bind_defined); SHOW_BOOL (bind_local); + SHOW_BOOL (bind_ipv6_only); SHOW_INT (connect_retry_seconds); SHOW_INT (connect_timeout); - SHOW_INT (connect_retry_max); -#ifdef ENABLE_HTTP_PROXY if (o->http_proxy_options) show_http_proxy_options (o->http_proxy_options); -#endif -#ifdef ENABLE_SOCKS SHOW_STR (socks_proxy_server); - SHOW_INT (socks_proxy_port); - SHOW_BOOL (socks_proxy_retry); -#endif + SHOW_STR (socks_proxy_port); SHOW_INT (tun_mtu); SHOW_BOOL (tun_mtu_defined); SHOW_INT (link_mtu); @@ -1399,8 +1453,6 @@ show_connection_entry (const struct connection_entry *o) static void show_connection_entries (const struct options *o) { - msg (D_SHOW_PARMS, "Connection profiles [default]:"); - show_connection_entry (&o->ce); if (o->connection_list) { const struct connection_list *l = o->connection_list; @@ -1411,9 +1463,28 @@ show_connection_entries (const struct options *o) show_connection_entry (l->array[i]); } } + else + { + msg (D_SHOW_PARMS, "Connection profiles [default]:"); + show_connection_entry (&o->ce); + } msg (D_SHOW_PARMS, "Connection profiles END"); } +static void +show_pull_filter_list (const struct pull_filter_list *l) +{ + struct pull_filter *f; + if (!l) + return; + + msg (D_SHOW_PARMS, " Pull filters:"); + for (f = l->head; f; f = f->next) + { + msg (D_SHOW_PARMS, " %s \"%s\"", pull_filter_type_name(f->type), f->pattern); + } +} + #endif void @@ -1436,12 +1507,11 @@ show_settings (const struct options *o) SHOW_BOOL (show_digests); SHOW_BOOL (show_engines); SHOW_BOOL (genkey); -#ifdef ENABLE_SSL SHOW_STR (key_pass_file); SHOW_BOOL (show_tls_ciphers); -#endif #endif + SHOW_INT (connect_retry_max); show_connection_entries (o); SHOW_BOOL (remote_random); @@ -1452,7 +1522,6 @@ show_settings (const struct options *o) SHOW_STR (dev_node); SHOW_STR (lladdr); SHOW_INT (topology); - SHOW_BOOL (tun_ipv6); SHOW_STR (ifconfig_local); SHOW_STR (ifconfig_remote_netmask); SHOW_BOOL (ifconfig_noexec); @@ -1488,6 +1557,7 @@ show_settings (const struct options *o) #endif SHOW_INT (resolve_retry_seconds); + SHOW_BOOL (resolve_in_advance); SHOW_STR (username); SHOW_STR (groupname); @@ -1506,6 +1576,7 @@ show_settings (const struct options *o) SHOW_INT (inetd); SHOW_BOOL (log); SHOW_BOOL (suppress_timestamps); + SHOW_BOOL (machine_readable_output); SHOW_INT (nice); SHOW_INT (verbosity); SHOW_INT (mute); @@ -1528,8 +1599,9 @@ show_settings (const struct options *o) SHOW_BOOL (fast_io); -#ifdef ENABLE_LZO - SHOW_INT (lzo); +#ifdef USE_COMP + SHOW_INT (comp.alg); + SHOW_INT (comp.flags); #endif SHOW_STR (route_script); @@ -1541,19 +1613,18 @@ show_settings (const struct options *o) SHOW_BOOL (route_delay_defined); SHOW_BOOL (route_nopull); SHOW_BOOL (route_gateway_via_dhcp); - SHOW_INT (max_routes); SHOW_BOOL (allow_pull_fqdn); + show_pull_filter_list (o->pull_filter_list); + 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); - SHOW_INT (management_port); + SHOW_STR (management_port); SHOW_STR (management_user_pass); SHOW_INT (management_log_history_cache); SHOW_INT (management_echo_buffer_size); @@ -1570,16 +1641,14 @@ show_settings (const struct options *o) #ifdef ENABLE_CRYPTO SHOW_STR (shared_secret_file); SHOW_INT (key_direction); - SHOW_BOOL (ciphername_defined); SHOW_STR (ciphername); - SHOW_BOOL (authname_defined); SHOW_STR (authname); SHOW_STR (prng_hash); SHOW_INT (prng_nonce_secret_len); SHOW_INT (keysize); -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS SHOW_BOOL (engine); -#endif /* ENABLE_CRYPTO_POLARSSL */ +#endif /* ENABLE_CRYPTO_MBEDTLS */ SHOW_BOOL (replay); SHOW_BOOL (mute_replay_warnings); SHOW_INT (replay_window); @@ -1591,13 +1660,17 @@ show_settings (const struct options *o) SHOW_BOOL (use_prediction_resistance); #endif -#ifdef ENABLE_SSL SHOW_BOOL (tls_server); SHOW_BOOL (tls_client); SHOW_INT (key_method); SHOW_STR (ca_file); SHOW_STR (ca_path); SHOW_STR (dh_file); +#ifdef MANAGMENT_EXTERNAL_KEY + if((o->management_flags & MF_EXTERNAL_CERT)) + SHOW_PARM ("cert_file","EXTERNAL_CERT","%s"); + else +#endif SHOW_STR (cert_file); SHOW_STR (extra_certs_file); @@ -1607,7 +1680,7 @@ show_settings (const struct options *o) else #endif SHOW_STR (priv_key_file); -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS SHOW_STR (pkcs12_file); #endif #ifdef ENABLE_CRYPTOAPI @@ -1644,8 +1717,8 @@ show_settings (const struct options *o) SHOW_BOOL (tls_exit); SHOW_STR (tls_auth_file); -#endif -#endif + SHOW_STR (tls_crypt_file); +#endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 { @@ -1677,7 +1750,7 @@ show_settings (const struct options *o) show_p2mp_parms (o); #endif -#ifdef WIN32 +#ifdef _WIN32 SHOW_BOOL (show_net_up); SHOW_INT (route_method); SHOW_BOOL (block_outside_dns); @@ -1691,7 +1764,7 @@ show_settings (const struct options *o) #undef SHOW_INT #undef SHOW_BOOL -#if HTTP_PROXY_OVERRIDE +#ifdef ENABLE_MANAGEMENT static struct http_proxy_options * parse_http_proxy_override (const char *server, @@ -1703,19 +1776,9 @@ parse_http_proxy_override (const char *server, if (server && port) { struct http_proxy_options *ho; - const int int_port = atoi(port); - - if (!legal_ipv4_port (int_port)) - { - msg (msglevel, "Bad http-proxy port number: %s", port); - return NULL; - } - ALLOC_OBJ_CLEAR_GC (ho, struct http_proxy_options, gc); ho->server = string_alloc(server, gc); - ho->port = int_port; - ho->retry = true; - ho->timeout = 5; + ho->port = port; if (flags && !strcmp(flags, "nct")) ho->auth_retry = PAR_NCT; else @@ -1732,32 +1795,31 @@ void options_postprocess_http_proxy_override (struct options *o) { const struct connection_list *l = o->connection_list; - if (l) + int i; + bool succeed = false; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_TCP_CLIENT || ce->proto == PROTO_TCP) + { + ce->http_proxy_options = o->http_proxy_override; + succeed = true; + } + } + if (succeed) { - int i; - bool succeed = false; for (i = 0; i < l->len; ++i) - { - struct connection_entry *ce = l->array[i]; - if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4) - { - ce->http_proxy_options = o->http_proxy_override; - succeed = true; - } - } - if (succeed) - { - for (i = 0; i < l->len; ++i) - { - struct connection_entry *ce = l->array[i]; - if (ce->proto == PROTO_UDPv4) - { - ce->flags |= CE_DISABLED; - } - } - } - else - msg (M_WARN, "Note: option http-proxy-override ignored because no TCP-based connection profiles are defined"); + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_UDP) + { + ce->flags |= CE_DISABLED; + } + } + } + else + { + msg (M_WARN, "Note: option http-proxy-override ignored because no TCP-based connection profiles are defined"); } } @@ -1811,15 +1873,46 @@ alloc_remote_entry (struct options *options, const int msglevel) return e; } +static struct pull_filter_list * +alloc_pull_filter_list (struct options *o) +{ + if (!o->pull_filter_list) + ALLOC_OBJ_CLEAR_GC (o->pull_filter_list, struct pull_filter_list, &o->gc); + return o->pull_filter_list; +} + +static struct pull_filter * +alloc_pull_filter (struct options *o, const int msglevel) +{ + struct pull_filter_list *l = alloc_pull_filter_list (o); + struct pull_filter *f; + + ALLOC_OBJ_CLEAR_GC (f, struct pull_filter, &o->gc); + if (l->head) + { + ASSERT (l->tail); + l->tail->next = f; + } + else + { + ASSERT (!l->tail); + l->head = f; + } + l->tail = f; + return f; +} + void connection_entry_load_re (struct connection_entry *ce, const struct remote_entry *re) { if (re->remote) ce->remote = re->remote; - if (re->remote_port >= 0) + if (re->remote_port) ce->remote_port = re->remote_port; if (re->proto >= 0) ce->proto = re->proto; + if (re->af > 0) + ce->af = re->af; } static void @@ -1849,10 +1942,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne * If "proto tcp" is specified, make sure we know whether it is * tcp-client or tcp-server. */ - if (ce->proto == PROTO_TCPv4) + if (ce->proto == PROTO_TCP) msg (M_USAGE, "--proto tcp is ambiguous in this context. Please specify --proto tcp-server or --proto tcp-client"); - if (ce->proto == PROTO_TCPv6) - msg (M_USAGE, "--proto tcp6 is ambiguous in this context. Please specify --proto tcp6-server or --proto tcp6-client"); /* * Sanity check on daemon/inetd modes @@ -1864,14 +1955,14 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (options->inetd && (ce->local || ce->remote)) msg (M_USAGE, "--local or --remote cannot be used with --inetd"); - if (options->inetd && ce->proto == PROTO_TCPv4_CLIENT) + if (options->inetd && ce->proto == PROTO_TCP_CLIENT) msg (M_USAGE, "--proto tcp-client cannot be used with --inetd"); - if (options->inetd == INETD_NOWAIT && ce->proto != PROTO_TCPv4_SERVER) + if (options->inetd == INETD_NOWAIT && ce->proto != PROTO_TCP_SERVER) msg (M_USAGE, "--inetd nowait can only be used with --proto tcp-server"); if (options->inetd == INETD_NOWAIT -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO && !(options->tls_server || options->tls_client) #endif ) @@ -1884,20 +1975,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (options->lladdr && dev != DEV_TYPE_TAP) msg (M_USAGE, "--lladdr can only be used in --dev tap mode"); - /* - * Sanity check on TCP mode options - */ - - if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT - && ce->proto != PROTO_TCPv6_CLIENT) - msg (M_USAGE, "--connect-retry doesn't make sense unless also used with " - "--proto tcp-client or tcp6-client"); - - if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT - && ce->proto != PROTO_TCPv6_CLIENT) - msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with " - "--proto tcp-client or tcp6-client"); - /* * Sanity check on MTU parameters */ @@ -1920,7 +1997,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (proto_is_net(ce->proto) && string_defined_equal (ce->local, ce->remote) - && ce->local_port == ce->remote_port) + && string_defined_equal (ce->local_port, ce->remote_port)) msg (M_USAGE, "--remote and --local addresses are the same"); if (string_defined_equal (ce->remote, options->ifconfig_local) @@ -1965,7 +2042,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne * Windows-specific options. */ -#ifdef WIN32 +#ifdef _WIN32 if (dev == DEV_TYPE_TUN && !(pull || (options->ifconfig_local && options->ifconfig_remote_netmask))) msg (M_USAGE, "On Windows, --ifconfig is required when --dev tun is used"); @@ -1993,29 +2070,21 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp"); #endif - if (!ce->remote && (ce->proto == PROTO_TCPv4_CLIENT - || ce->proto == PROTO_TCPv6_CLIENT)) + if (!ce->remote && ce->proto == PROTO_TCP_CLIENT) msg (M_USAGE, "--remote MUST be used in TCP Client mode"); -#ifdef ENABLE_HTTP_PROXY - if ((ce->http_proxy_options) && ce->proto != PROTO_TCPv4_CLIENT) + if ((ce->http_proxy_options) && ce->proto != PROTO_TCP_CLIENT) msg (M_USAGE, "--http-proxy MUST be used in TCP Client mode (i.e. --proto tcp-client)"); if ((ce->http_proxy_options) && !ce->http_proxy_options->server) msg (M_USAGE, "--http-proxy not specified but other http proxy options present"); -#endif -#if defined(ENABLE_HTTP_PROXY) && defined(ENABLE_SOCKS) if (ce->http_proxy_options && ce->socks_proxy_server) msg (M_USAGE, "--http-proxy can not be used together with --socks-proxy"); -#endif -#ifdef ENABLE_SOCKS - if (ce->socks_proxy_server && ce->proto == PROTO_TCPv4_SERVER) + if (ce->socks_proxy_server && ce->proto == PROTO_TCP_SERVER) msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode"); -#endif - if ((ce->proto == PROTO_TCPv4_SERVER || ce->proto == PROTO_TCPv6_SERVER) - && connection_list_defined (options)) + if (ce->proto == PROTO_TCP_SERVER && (options->connection_list->len > 1)) msg (M_USAGE, "TCP server mode allows at most one --remote address"); #if P2MP_SERVER @@ -2029,13 +2098,14 @@ 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 (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCPv4_SERVER - || ce->proto == PROTO_TCPv6_SERVER)) + if (options->pull_filter_list) + msg (M_USAGE, "--pull-filter cannot be used with --mode server"); + if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCP_SERVER)) msg (M_USAGE, "--mode server currently only supports " "--proto udp or --proto tcp-server or proto tcp6-server"); #if PORT_SHARE if ((options->port_share_host || options->port_share_port) && - (ce->proto != PROTO_TCPv4_SERVER && ce->proto != PROTO_TCPv6_SERVER)) + (ce->proto != PROTO_TCP_SERVER)) msg (M_USAGE, "--port-share only works in TCP server mode " "(--proto tcp-server or tcp6-server)"); #endif @@ -2045,38 +2115,29 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--remote cannot be used with --mode server"); if (!ce->bind_local) msg (M_USAGE, "--nobind cannot be used with --mode server"); -#ifdef ENABLE_HTTP_PROXY if (ce->http_proxy_options) msg (M_USAGE, "--http-proxy cannot be used with --mode server"); -#endif -#ifdef ENABLE_SOCKS if (ce->socks_proxy_server) msg (M_USAGE, "--socks-proxy cannot be used with --mode server"); -#endif - if (options->connection_list) - msg (M_USAGE, " cannot be used with --mode server"); -#if 0 - if (options->tun_ipv6) - msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server"); -#endif + /* blocks force to have a remote embedded, so we check for the + * --remote and bail out if it is present */ + if (options->connection_list->len >1 || + options->connection_list->array[0]->remote) + msg (M_USAGE, " cannot be used with --mode server"); + if (options->shaper) msg (M_USAGE, "--shaper cannot be used with --mode server"); if (options->inetd) 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 (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER - || ce->proto == PROTO_TCPv6_SERVER)) + if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCP_SERVER)) msg (M_USAGE, "--mode server currently only supports " "--proto udp or --proto tcp-server or --proto tcp6-server"); 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"); -#ifdef ENABLE_OCC - if (ce->explicit_exit_notification) - msg (M_USAGE, "--explicit-exit-notify cannot be used with --mode server"); -#endif if (options->routes && (options->routes->flags & RG_ENABLE)) msg (M_USAGE, "--redirect-gateway cannot be used with --mode server (however --push \"redirect-gateway\" is fine)"); if (options->route_delay_defined) @@ -2087,9 +2148,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool"); if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local ) msg (M_USAGE, "--ifconfig-ipv6-pool needs --ifconfig-ipv6"); - if (options->ifconfig_ipv6_local && !options->tun_ipv6 ) - msg (M_INFO, "Warning: --ifconfig-ipv6 without --tun-ipv6 will not do IPv6"); - + if (options->allow_recursive_routing) + msg (M_USAGE, "--allow-recursive-routing cannot be used with --mode server"); if (options->auth_user_pass_file) msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)"); if (options->ccd_exclusive && !options->client_config_dir) @@ -2102,8 +2162,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne || PLUGIN_OPTION_LIST (options) || MAN_CLIENT_AUTH_ENABLED (options)); const char *postfix = "must be used with --management-client-auth, an --auth-user-pass-verify script, or plugin"; - if ((options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) && !ccnr) - msg (M_USAGE, "--client-cert-not-required %s", postfix); + if ((options->ssl_flags & (SSLF_CLIENT_CERT_NOT_REQUIRED|SSLF_CLIENT_CERT_OPTIONAL)) && !ccnr) + msg (M_USAGE, "--verify-client-cert none|optional %s", postfix); if ((options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && !ccnr) msg (M_USAGE, "--username-as-common-name %s", postfix); if ((options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) && !ccnr) @@ -2137,8 +2197,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--duplicate-cn requires --mode server"); if (options->cf_max || options->cf_per) msg (M_USAGE, "--connect-freq requires --mode server"); - if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) - msg (M_USAGE, "--client-cert-not-required requires --mode server"); + if (options->ssl_flags & (SSLF_CLIENT_CERT_NOT_REQUIRED|SSLF_CLIENT_CERT_OPTIONAL)) + msg (M_USAGE, "--client-cert-not-required and --verify-client-cert require --mode server"); if (options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) msg (M_USAGE, "--username-as-common-name requires --mode server"); if (options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) @@ -2151,6 +2211,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne "tcp-nodelay in the server configuration instead."); if (options->auth_user_pass_verify_script) msg (M_USAGE, "--auth-user-pass-verify requires --mode server"); + if (options->auth_token_generate) + msg (M_USAGE, "--auth-gen-token requires --mode server"); #if PORT_SHARE if (options->port_share_host || options->port_share_port) msg (M_USAGE, "--port-share requires TCP server mode (--mode server --proto tcp-server)"); @@ -2165,14 +2227,14 @@ options_postprocess_verify_ce (const struct options *options, const struct conne #ifdef ENABLE_CRYPTO + if (options->ncp_enabled && !tls_check_ncp_cipher_list(options->ncp_ciphers)) + { + msg (M_USAGE, "NCP cipher list contains unsupported ciphers."); + } + /* * Check consistency of replay options */ - 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"); - if (!options->replay && (options->replay_window != defaults.replay_window || options->replay_time != defaults.replay_time)) @@ -2181,16 +2243,24 @@ options_postprocess_verify_ce (const struct options *options, const struct conne /* * SSL/TLS mode sanity checks. */ - -#ifdef ENABLE_SSL if (options->tls_server + options->tls_client + (options->shared_secret_file != NULL) > 1) msg (M_USAGE, "specify only one of --tls-server, --tls-client, or --secret"); - if (options->tls_server) + if (options->ssl_flags & (SSLF_CLIENT_CERT_NOT_REQUIRED|SSLF_CLIENT_CERT_OPTIONAL)) + { + msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION " + "--verify-client-cert none|optional (or --client-cert-not-required) " + "may accept clients which do not present a certificate"); + } + + if (options->key_method == 1) { - notnull (options->dh_file, "DH file (--dh)"); + msg (M_WARN, "WARNING: --key-method 1 is deprecated and will be removed " + "in OpenVPN 2.5. By default --key-method 2 will be used if not set " + "in the configuration file, which is the recommended approach."); } + if (options->tls_server || options->tls_client) { #ifdef ENABLE_PKCS11 @@ -2209,6 +2279,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne #ifdef MANAGMENT_EXTERNAL_KEY if (options->management_flags & MF_EXTERNAL_KEY) msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs11-provider is also specified."); + if (options->management_flags & MF_EXTERNAL_CERT) + msg(M_USAGE, "Parameter --management-external-cert cannot be used when --pkcs11-provider is also specified."); #endif if (options->pkcs12_file) msg(M_USAGE, "Parameter --pkcs12 cannot be used when --pkcs11-provider is also specified."); @@ -2224,6 +2296,13 @@ options_postprocess_verify_ce (const struct options *options, const struct conne { msg (M_USAGE, "--key and --management-external-key are mutually exclusive"); } + else if((options->management_flags & MF_EXTERNAL_CERT)) + { + if (options->cert_file) + msg (M_USAGE, "--cert and --management-external-cert are mutually exclusive"); + else if(!(options->management_flags & MF_EXTERNAL_KEY)) + msg (M_USAGE, "--management-external-cert must be used with --management-external-key"); + } else #endif #ifdef ENABLE_CRYPTOAPI @@ -2240,14 +2319,16 @@ options_postprocess_verify_ce (const struct options *options, const struct conne #ifdef MANAGMENT_EXTERNAL_KEY if (options->management_flags & MF_EXTERNAL_KEY) msg(M_USAGE, "Parameter --management-external-key cannot be used when --cryptoapicert is also specified."); + if (options->management_flags & MF_EXTERNAL_CERT) + msg(M_USAGE, "Parameter --management-external-cert cannot be used when --cryptoapicert is also specified."); #endif } else #endif if (options->pkcs12_file) { -#ifdef ENABLE_CRYPTO_POLARSSL - msg(M_USAGE, "Parameter --pkcs12 cannot be used with the PolarSSL version version of OpenVPN."); +#ifdef ENABLE_CRYPTO_MBEDTLS + msg(M_USAGE, "Parameter --pkcs12 cannot be used with the mbed TLS version version of OpenVPN."); #else if (options->ca_path) msg(M_USAGE, "Parameter --capath cannot be used when --pkcs12 is also specified."); @@ -2257,17 +2338,19 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg(M_USAGE, "Parameter --key cannot be used when --pkcs12 is also specified."); #ifdef MANAGMENT_EXTERNAL_KEY if (options->management_flags & MF_EXTERNAL_KEY) - msg(M_USAGE, "Parameter --external-management-key cannot be used when --pkcs12 is also specified."); + msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs12 is also specified."); + if (options->management_flags & MF_EXTERNAL_CERT) + msg(M_USAGE, "Parameter --management-external-cert cannot be used when --pkcs12 is also specified."); #endif #endif } else { -#ifdef ENABLE_CRYPTO_POLARSSL +#ifdef ENABLE_CRYPTO_MBEDTLS if (!(options->ca_file)) msg(M_USAGE, "You must define CA file (--ca)"); if (options->ca_path) - msg(M_USAGE, "Parameter --capath cannot be used with the PolarSSL version version of OpenVPN."); + msg(M_USAGE, "Parameter --capath cannot be used with the mbed TLS version version of OpenVPN."); #else if ((!(options->ca_file)) && (!(options->ca_path))) msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)"); @@ -2275,14 +2358,14 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if (pull) { - const int sum = (options->cert_file != NULL) + + const int sum = #ifdef MANAGMENT_EXTERNAL_KEY - ((options->priv_key_file != NULL) || (options->management_flags & MF_EXTERNAL_KEY)); + ((options->cert_file != NULL) || (options->management_flags & MF_EXTERNAL_CERT)) + + ((options->priv_key_file != NULL) || (options->management_flags & MF_EXTERNAL_KEY)); #else - (options->priv_key_file != NULL); + (options->cert_file != NULL) + (options->priv_key_file != NULL); #endif - if (sum == 0) { #if P2MP @@ -2299,6 +2382,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne } else { +#ifdef MANAGMENT_EXTERNAL_KEY + if (!(options->management_flags & MF_EXTERNAL_CERT)) +#endif notnull (options->cert_file, "certificate file (--cert) or PKCS#12 file (--pkcs12)"); #ifdef MANAGMENT_EXTERNAL_KEY if (!(options->management_flags & MF_EXTERNAL_KEY)) @@ -2306,6 +2392,10 @@ options_postprocess_verify_ce (const struct options *options, const struct conne notnull (options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)"); } } + if (options->tls_auth_file && options->tls_crypt_file) + { + msg (M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive"); + } } else { @@ -2323,7 +2413,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (dh_file); MUST_BE_UNDEF (cert_file); MUST_BE_UNDEF (priv_key_file); -#ifndef ENABLE_CRYPTO_POLARSSL +#ifndef ENABLE_CRYPTO_MBEDTLS MUST_BE_UNDEF (pkcs12_file); #endif MUST_BE_UNDEF (cipher_list); @@ -2337,6 +2427,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (handshake_window); MUST_BE_UNDEF (transition_window); MUST_BE_UNDEF (tls_auth_file); + MUST_BE_UNDEF (tls_crypt_file); MUST_BE_UNDEF (single_session); #ifdef ENABLE_PUSH_PEER_INFO MUST_BE_UNDEF (push_peer_info); @@ -2353,16 +2444,12 @@ options_postprocess_verify_ce (const struct options *options, const struct conne MUST_BE_UNDEF (pkcs11_id); MUST_BE_UNDEF (pkcs11_id_management); #endif -#if P2MP - MUST_BE_UNDEF (server_poll_timeout); -#endif if (pull) msg (M_USAGE, err, "--pull"); } #undef MUST_BE_UNDEF #endif /* ENABLE_CRYPTO */ -#endif /* ENABLE_SSL */ #if P2MP if (options->auth_user_pass_file && !options->pull) @@ -2380,35 +2467,29 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce) #if P2MP_SERVER if (o->server_defined || o->server_bridge_defined || o->server_bridge_proxy_dhcp) { - if (ce->proto == PROTO_TCPv4) - ce->proto = PROTO_TCPv4_SERVER; - else if (ce->proto == PROTO_TCPv6) - ce->proto = PROTO_TCPv6_SERVER; + if (ce->proto == PROTO_TCP) + ce->proto = PROTO_TCP_SERVER; } #endif #if P2MP if (o->client) { - if (ce->proto == PROTO_TCPv4) - ce->proto = PROTO_TCPv4_CLIENT; - else if (ce->proto == PROTO_TCPv6) - ce->proto = PROTO_TCPv6_CLIENT; + if (ce->proto == PROTO_TCP) + ce->proto = PROTO_TCP_CLIENT; } #endif - if (ce->proto == PROTO_TCPv4_CLIENT && !ce->local && !ce->local_port_defined && !ce->bind_defined) + if (ce->proto == PROTO_TCP_CLIENT && !ce->local && !ce->local_port_defined && !ce->bind_defined) ce->bind_local = false; -#ifdef ENABLE_SOCKS - if (ce->proto == PROTO_UDPv4 && ce->socks_proxy_server && !ce->local && !ce->local_port_defined && !ce->bind_defined) + if (ce->proto == PROTO_UDP && ce->socks_proxy_server && !ce->local && !ce->local_port_defined && !ce->bind_defined) ce->bind_local = false; -#endif if (!ce->bind_local) - ce->local_port = 0; + ce->local_port = NULL; /* if protocol forcing is enabled, disable all protocols except for the forced one */ - if (o->proto_force >= 0 && proto_is_tcp(o->proto_force) != proto_is_tcp(ce->proto)) + if (o->proto_force >= 0 && o->proto_force != ce->proto) ce->flags |= CE_DISABLED; /* @@ -2445,7 +2526,9 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce) static void options_postprocess_mutate_invariant (struct options *options) { +#ifdef _WIN32 const int dev = dev_type_enum (options->dev, options->dev_type); +#endif /* * In forking TCP server mode, you don't need to ifconfig @@ -2454,7 +2537,7 @@ options_postprocess_mutate_invariant (struct options *options) if (options->inetd == INETD_NOWAIT) options->ifconfig_noexec = true; -#ifdef WIN32 +#ifdef _WIN32 if ((dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP) && !options->route_delay_defined) { if (options->mode == MODE_POINT_TO_POINT) @@ -2477,7 +2560,7 @@ options_postprocess_mutate_invariant (struct options *options) */ if (options->mode == MODE_SERVER) { -#ifdef WIN32 +#ifdef _WIN32 /* * We need to explicitly set --tap-sleep because * we do not schedule event timers in the top-level context. @@ -2516,6 +2599,7 @@ options_postprocess_verify (const struct options *o) static void options_postprocess_mutate (struct options *o) { + int i; /* * Process helper-type options which map to other, more complex * sequences of options. @@ -2529,48 +2613,57 @@ options_postprocess_mutate (struct options *o) if (o->remote_list && !o->connection_list) { /* - * For compatibility with 2.0.x, map multiple --remote options - * into connection list (connection lists added in 2.1). + * Convert remotes into connection list */ - if (o->remote_list->len > 1 || o->force_connection_list) - { - const struct remote_list *rl = o->remote_list; - int i; - for (i = 0; i < rl->len; ++i) - { - const struct remote_entry *re = rl->array[i]; - struct connection_entry ce = o->ce; - struct connection_entry *ace; - - ASSERT (re->remote); - connection_entry_load_re (&ce, re); - ace = alloc_connection_entry (o, M_USAGE); - ASSERT (ace); - *ace = ce; - } - } - else if (o->remote_list->len == 1) /* one --remote option specified */ - { - connection_entry_load_re (&o->ce, o->remote_list->array[0]); - } - else - { - ASSERT (0); - } + const struct remote_list *rl = o->remote_list; + for (i = 0; i < rl->len; ++i) + { + const struct remote_entry *re = rl->array[i]; + struct connection_entry ce = o->ce; + struct connection_entry *ace; + + ASSERT (re->remote); + connection_entry_load_re (&ce, re); + ace = alloc_connection_entry (o, M_USAGE); + ASSERT (ace); + *ace = ce; + } } - if (o->connection_list) + else if(!o->remote_list && !o->connection_list) { - int i; - for (i = 0; i < o->connection_list->len; ++i) + struct connection_entry *ace; + ace = alloc_connection_entry (o, M_USAGE); + ASSERT (ace); + *ace = o->ce; + } + + ASSERT (o->connection_list); + for (i = 0; i < o->connection_list->len; ++i) options_postprocess_mutate_ce (o, o->connection_list->array[i]); -#if HTTP_PROXY_OVERRIDE - if (o->http_proxy_override) +#ifdef ENABLE_CRYPTO + if (o->tls_server) + { + /* Check that DH file is specified, or explicitly disabled */ + notnull (o->dh_file, "DH file (--dh)"); + if (streq (o->dh_file, "none")) + o->dh_file = NULL; + } + + /* cipher negotiation (NCP) currently assumes --pull or --mode server */ + if ( o->ncp_enabled && + ! (o->pull || o->mode == MODE_SERVER) ) + { + msg( M_WARN, "disabling NCP mode (--ncp-disable) because not " + "in P2MP client or server mode" ); + o->ncp_enabled = false; + } +#endif + +#if ENABLE_MANAGEMENT + if (o->http_proxy_override) options_postprocess_http_proxy_override(o); #endif - } - else - options_postprocess_mutate_ce (o, &o->ce); #ifdef ENABLE_CRYPTOAPI if (o->cryptoapi_cert) @@ -2609,6 +2702,7 @@ options_postprocess_mutate (struct options *o) #define CHKACC_FILEXSTWR (1<<2) /** If file exists, is it writable? */ #define CHKACC_INLINE (1<<3) /** File is present if it's an inline file */ #define CHKACC_ACPTSTDIN (1<<4) /** If filename is stdin, it's allowed and "exists" */ +#define CHKACC_PRIVATE (1<<5) /** Warn if this (private) file is group/others accessible */ static bool check_file_access(const int type, const char *file, const int mode, const char *opt) @@ -2649,6 +2743,23 @@ check_file_access(const int type, const char *file, const int mode, const char * if (platform_access (file, W_OK) != 0) errcode = errno; + /* Warn if a given private file is group/others accessible. */ + if (type & CHKACC_PRIVATE) + { + platform_stat_t st; + if (platform_stat (file, &st)) + { + msg (M_WARN | M_ERRNO, "WARNING: cannot stat file '%s'", file); + } +#ifndef _WIN32 + else + { + if (st.st_mode & (S_IRWXG|S_IRWXO)) + msg (M_WARN, "WARNING: file '%s' is group or others accessible", file); + } +#endif + } + /* Scream if an error is found */ if( errcode > 0 ) msg (M_NOPREFIX|M_OPTERR, "%s fails with '%s': %s", @@ -2724,7 +2835,7 @@ check_cmd_access(const char *command, const char *opt, const char *chroot) /* Extract executable path and arguments */ argv = argv_new (); - argv_printf (&argv, "%sc", command); + argv_parse_cmd (&argv, command); /* if an executable is specified then check it; otherwise, complain */ if (argv.argv[0]) @@ -2754,8 +2865,8 @@ options_postprocess_filechecks (struct options *options) { bool errs = false; +#ifdef ENABLE_CRYPTO /* ** SSL/TLS/crypto related files ** */ -#ifdef ENABLE_SSL errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->dh_file, R_OK, "--dh"); errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->ca_file, R_OK, "--ca"); errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->ca_path, R_OK, "--capath"); @@ -2765,41 +2876,40 @@ options_postprocess_filechecks (struct options *options) #ifdef MANAGMENT_EXTERNAL_KEY if(!(options->management_flags & MF_EXTERNAL_KEY)) #endif - errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->priv_key_file, R_OK, - "--key"); - errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->pkcs12_file, R_OK, - "--pkcs12"); + { + errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, + options->priv_key_file, R_OK, "--key"); + } + errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, + options->pkcs12_file, R_OK, "--pkcs12"); if (options->ssl_flags & SSLF_CRL_VERIFY_DIR) errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->crl_file, R_OK|X_OK, "--crl-verify directory"); else - errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->crl_file, R_OK, - "--crl-verify"); - - errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->tls_auth_file, R_OK, - "--tls-auth"); -#endif /* ENABLE_SSL */ -#ifdef ENABLE_CRYPTO - errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE, options->shared_secret_file, R_OK, - "--secret"); + errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE|CHKACC_INLINE, + options->crl_file, R_OK, "--crl-verify"); + + errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, + options->tls_auth_file, R_OK, "--tls-auth"); + errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, + options->tls_crypt_file, R_OK, "--tls-crypt"); + errs |= check_file_access (CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, + options->shared_secret_file, R_OK, "--secret"); errs |= check_file_access (CHKACC_DIRPATH|CHKACC_FILEXSTWR, - options->packet_id_file, R_OK|W_OK, "--replay-persist"); -#endif /* ENABLE_CRYPTO */ - + options->packet_id_file, R_OK|W_OK, "--replay-persist"); /* ** Password files ** */ -#ifdef ENABLE_SSL - errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN, - options->key_pass_file, R_OK, "--askpass"); -#endif /* ENABLE_SSL */ + errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE, + options->key_pass_file, R_OK, "--askpass"); +#endif /* ENABLE_CRYPTO */ #ifdef ENABLE_MANAGEMENT - errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN, + errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE, options->management_user_pass, R_OK, "--management user/password file"); #endif /* ENABLE_MANAGEMENT */ #if P2MP - errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN, + errs |= check_file_access (CHKACC_FILE|CHKACC_ACPTSTDIN|CHKACC_PRIVATE, options->auth_user_pass_file, R_OK, "--auth-user-pass"); #endif /* P2MP */ @@ -2815,10 +2925,10 @@ options_postprocess_filechecks (struct options *options) R_OK|W_OK, "--status"); /* ** Config related ** */ -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->tls_export_cert, R_OK|W_OK|X_OK, "--tls-export-cert"); -#endif /* ENABLE_SSL */ +#endif /* ENABLE_CRYPTO */ #if P2MP_SERVER errs |= check_file_access_chroot (options->chroot_dir, CHKACC_FILE, options->client_config_dir, R_OK|X_OK, "--client-config-dir"); @@ -2872,18 +2982,16 @@ pre_pull_save (struct options *o) o->pre_pull->routes_ipv6 = clone_route_ipv6_option_list(o->routes_ipv6, &o->gc); o->pre_pull->routes_ipv6_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 } } void -pre_pull_restore (struct options *o) +pre_pull_restore (struct options *o, struct gc_arena *gc) { const struct options_pre_pull *pp = o->pre_pull; if (pp) @@ -2895,7 +3003,7 @@ pre_pull_restore (struct options *o) if (pp->routes_defined) { rol_check_alloc (o); - copy_route_option_list (o->routes, pp->routes); + copy_route_option_list (o->routes, pp->routes, gc); } else o->routes = NULL; @@ -2903,12 +3011,11 @@ pre_pull_restore (struct options *o) if (pp->routes_ipv6_defined) { rol6_check_alloc (o); - copy_route_ipv6_option_list (o->routes_ipv6, pp->routes_ipv6); + copy_route_ipv6_option_list (o->routes_ipv6, pp->routes_ipv6, gc); } else o->routes_ipv6 = NULL; -#ifdef ENABLE_CLIENT_NAT if (pp->client_nat_defined) { cnol_check_alloc (o); @@ -2916,7 +3023,6 @@ pre_pull_restore (struct options *o) } else o->client_nat = NULL; -#endif o->foreign_option_index = pp->foreign_option_index; } @@ -2929,6 +3035,37 @@ pre_pull_restore (struct options *o) #ifdef ENABLE_OCC +/** + * Calculate the link-mtu to advertise to our peer. The actual value is not + * relevant, because we will possibly perform data channel cipher negotiation + * after this, but older clients will log warnings if we do not supply them the + * value they expect. This assumes that the traditional cipher/auth directives + * in the config match the config of the peer. + */ +static size_t +calc_options_string_link_mtu(const struct options *o, const struct frame *frame) +{ + size_t link_mtu = EXPANDED_SIZE (frame); +#ifdef ENABLE_CRYPTO + if (o->pull || o->mode == MODE_SERVER) + { + struct frame fake_frame = *frame; + struct key_type fake_kt; + init_key_type (&fake_kt, o->ciphername, o->authname, o->keysize, true, + false); + frame_add_to_extra_frame (&fake_frame, -(crypto_max_overhead())); + crypto_adjust_frame_parameters (&fake_frame, &fake_kt, o->use_iv, + o->replay, cipher_kt_mode_ofb_cfb (fake_kt.cipher)); + frame_finalize(&fake_frame, o->ce.link_mtu_defined, o->ce.link_mtu, + o->ce.tun_mtu_defined, o->ce.tun_mtu); + msg (D_MTU_DEBUG, "%s: link-mtu %u -> %d", __func__, (unsigned int) link_mtu, + EXPANDED_SIZE (&fake_frame)); + link_mtu = EXPANDED_SIZE (&fake_frame); + } +#endif + return link_mtu; +} + /* * Build an options string to represent data channel encryption options. * This string must match exactly between peers. The keysize is checked @@ -2953,6 +3090,7 @@ pre_pull_restore (struct options *o) * the other end of the connection] * * --comp-lzo + * --compress alg * --fragment * * Crypto Options: @@ -2972,7 +3110,6 @@ pre_pull_restore (struct options *o) * --tls-server [matched with --tls-client on * the other end of the connection] */ - char * options_string (const struct options *o, const struct frame *frame, @@ -2990,14 +3127,14 @@ options_string (const struct options *o, */ buf_printf (&out, ",dev-type %s", dev_type_string (o->dev, o->dev_type)); - buf_printf (&out, ",link-mtu %d", EXPANDED_SIZE (frame)); + buf_printf (&out, ",link-mtu %u", (unsigned int) calc_options_string_link_mtu(o, frame)); buf_printf (&out, ",tun-mtu %d", PAYLOAD_SIZE (frame)); - buf_printf (&out, ",proto %s", proto2ascii (proto_remote (o->ce.proto, remote), true)); + buf_printf (&out, ",proto %s", proto_remote (o->ce.proto, remote)); /* send tun_ipv6 only in peer2peer mode - in client/server mode, it * is usually pushed by the server, triggering a non-helpful warning */ - if (o->tun_ipv6 && o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o)) + if (o->ifconfig_ipv6_local && o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o)) buf_printf (&out, ",tun-ipv6"); /* @@ -3014,8 +3151,8 @@ options_string (const struct options *o, o->ifconfig_ipv6_local, o->ifconfig_ipv6_netbits, o->ifconfig_ipv6_remote, - (in_addr_t)0, - (in_addr_t)0, + NULL, + NULL, false, NULL); if (tt) @@ -3034,9 +3171,9 @@ options_string (const struct options *o, tt = NULL; } -#ifdef ENABLE_LZO - if (o->lzo & LZO_SELECTED) - buf_printf (&out, ",comp-lzo"); +#ifdef USE_COMP + if (o->comp.alg != COMP_ALG_UNDEF) + buf_printf (&out, ",comp-lzo"); /* for compatibility, this simply indicates that compression context is active, not necessarily LZO per-se */ #endif #ifdef ENABLE_FRAGMENT @@ -3046,13 +3183,8 @@ options_string (const struct options *o, #ifdef ENABLE_CRYPTO -#ifdef ENABLE_SSL #define TLS_CLIENT (o->tls_client) #define TLS_SERVER (o->tls_server) -#else -#define TLS_CLIENT (false) -#define TLS_SERVER (false) -#endif /* * Key direction @@ -3075,11 +3207,11 @@ options_string (const struct options *o, + (TLS_SERVER == true) <= 1); - init_key_type (&kt, o->ciphername, o->ciphername_defined, - o->authname, o->authname_defined, - o->keysize, true, false); + init_key_type (&kt, o->ciphername, o->authname, o->keysize, true, + false); - buf_printf (&out, ",cipher %s", cipher_kt_name (kt.cipher)); + buf_printf (&out, ",cipher %s", + translate_cipher_name_to_openvpn(cipher_kt_name (kt.cipher))); buf_printf (&out, ",auth %s", md_kt_name (kt.digest)); buf_printf (&out, ",keysize %d", kt.cipher_length * 8); if (o->shared_secret_file) @@ -3095,7 +3227,6 @@ options_string (const struct options *o, #endif } -#ifdef ENABLE_SSL /* * SSL Options */ @@ -3104,6 +3235,9 @@ options_string (const struct options *o, { if (o->tls_auth_file) buf_printf (&out, ",tls-auth"); + /* Not adding tls-crypt here, because we won't reach this code if + * tls-auth/tls-crypt does not match. Removing tls-auth here would + * break stuff, so leaving that in place. */ if (o->key_method > 1) buf_printf (&out, ",key-method %d", o->key_method); @@ -3124,7 +3258,6 @@ options_string (const struct options *o, buf_printf (&out, ",tls-server"); } } -#endif /* ENABLE_SSL */ #undef TLS_CLIENT #undef TLS_SERVER @@ -3447,10 +3580,11 @@ usage (void) struct options o; init_options (&o, true); -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO fprintf (fp, usage_message, title_string, o.ce.connect_retry_seconds, + o.ce.connect_retry_seconds_max, o.ce.local_port, o.ce.remote_port, TUN_MTU_DEFAULT, TAP_MTU_EXTRA_DEFAULT, o.verbosity, @@ -3458,15 +3592,6 @@ usage (void) o.replay_window, o.replay_time, o.tls_timeout, o.renegotiate_seconds, o.handshake_window, o.transition_window); -#elif defined(ENABLE_CRYPTO) - fprintf (fp, usage_message, - title_string, - o.ce.connect_retry_seconds, - o.ce.local_port, o.ce.remote_port, - TUN_MTU_DEFAULT, TAP_MTU_EXTRA_DEFAULT, - o.verbosity, - o.authname, o.ciphername, - o.replay_window, o.replay_time); #else fprintf (fp, usage_message, title_string, @@ -3489,7 +3614,7 @@ usage_small (void) openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ } -#ifdef WIN32 +#ifdef _WIN32 void show_windows_version(const unsigned int flags) { struct gc_arena gc = gc_new (); @@ -3501,7 +3626,7 @@ void show_windows_version(const unsigned int flags) void show_library_versions(const unsigned int flags) { -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO #define SSL_LIB_VER_STR get_ssl_library_version() #else #define SSL_LIB_VER_STR "" @@ -3523,7 +3648,7 @@ usage_version (void) { msg (M_INFO|M_NOPREFIX, "%s", title_string); show_library_versions( M_INFO|M_NOPREFIX ); -#ifdef WIN32 +#ifdef _WIN32 show_windows_version( M_INFO|M_NOPREFIX ); #endif msg (M_INFO|M_NOPREFIX, "Originally developed by James Yonan"); @@ -3535,9 +3660,6 @@ usage_version (void) #ifdef CONFIGURE_SPECIAL_BUILD msg (M_INFO|M_NOPREFIX, "special build: %s", CONFIGURE_SPECIAL_BUILD); #endif -#ifdef CONFIGURE_GIT_REVISION - msg (M_INFO|M_NOPREFIX, "git revision: %s", CONFIGURE_GIT_REVISION); -#endif #endif openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ } @@ -3573,7 +3695,7 @@ positive_atoi (const char *str) return i < 0 ? 0 : i; } -#ifdef WIN32 /* This function is only used when compiling on Windows */ +#ifdef _WIN32 /* This function is only used when compiling on Windows */ static unsigned int atou (const char *str) { @@ -3809,7 +3931,7 @@ read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc) buf_printf (&buf, "%s", line); } if (!endtagfound) - msg (M_WARN, "WARNING: Endtag %s missing", close_tag); + msg (M_FATAL, "ERROR: Endtag %s missing", close_tag); ret = string_alloc (BSTR (&buf), gc); buf_clear (&buf); free_buf (&buf); @@ -4019,6 +4141,45 @@ parse_argv (struct options *options, } } +/** + * Filter an option line by all pull filters. + * + * If a match is found, the line is modified depending on + * the filter type, and returns true. If the filter type is + * reject, SIGUSR1 is triggered and the return value is false. + * In that case the caller must end the push processing. + */ +static bool +apply_pull_filter (const struct options *o, char *line) +{ + struct pull_filter *f; + + if (!o->pull_filter_list) return true; + + for (f = o->pull_filter_list->head; f; f = f->next) + { + if (f->type == PUF_TYPE_ACCEPT && strncmp (line, f->pattern, f->size) == 0) + { + msg (D_LOW, "Pushed option accepted by filter: '%s'", line); + return true; + } + else if (f->type == PUF_TYPE_IGNORE && strncmp (line, f->pattern, f->size) == 0) + { + msg (D_PUSH, "Pushed option removed by filter: '%s'", line); + *line = '\0'; + return true; + } + else if (f->type == PUF_TYPE_REJECT && strncmp (line, f->pattern, f->size) == 0) + { + msg (M_WARN, "Pushed option rejected by filter: '%s'. Restarting.", line); + *line = '\0'; + throw_signal_soft (SIGUSR1, "Offending option received from server"); + return false; + } + } + return true; +} + bool apply_push_options (struct options *options, struct buffer *buf, @@ -4036,6 +4197,10 @@ apply_push_options (struct options *options, char *p[MAX_PARMS]; CLEAR (p); ++line_num; + if (!apply_pull_filter(options, line)) + { + return false; /* Cause push/pull error and stop push processing */ + } if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc)) { add_option (options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es); @@ -4222,13 +4387,18 @@ add_option (struct options *options, { VERIFY_PERMISSION (OPT_P_GENERAL); usage (); + if (p[1]) + { + msg (msglevel, "--help does not accept any parameters"); + goto err; + } } - if (streq (p[0], "version")) + if (streq (p[0], "version") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); usage_version (); } - else if (streq (p[0], "config") && p[1]) + else if (streq (p[0], "config") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_CONFIG); @@ -4239,12 +4409,17 @@ add_option (struct options *options, read_config_file (options, p[1], level, file, line, msglevel, permission_mask, option_types_found, es); } #if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL) - else if (streq (p[0], "show-gateway")) + else if (streq (p[0], "show-gateway") && !p[2]) { struct route_gateway_info rgi; + struct route_ipv6_gateway_info rgi6; + struct in6_addr remote = IN6ADDR_ANY_INIT; VERIFY_PERMISSION (OPT_P_GENERAL); + if (p[1]) + get_ipv6_addr (p[1], &remote, NULL, M_WARN); get_default_gateway(&rgi); - print_default_gateway(M_INFO, &rgi); + get_default_gateway_ipv6(&rgi6, &remote); + print_default_gateway(M_INFO, &rgi, &rgi6); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } #endif @@ -4289,10 +4464,8 @@ add_option (struct options *options, msg (M_WARN, "echo/parameter option overflow"); } #ifdef ENABLE_MANAGEMENT - else if (streq (p[0], "management") && p[1] && p[2]) + else if (streq (p[0], "management") && p[1] && p[2] && !p[4]) { - int port = 0; - VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[2], "unix")) { @@ -4303,104 +4476,93 @@ add_option (struct options *options, goto err; #endif } - else - { - port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "port number associated with --management directive is out of range"); - goto err; - } - } options->management_addr = p[1]; - options->management_port = port; + options->management_port = p[2]; if (p[3]) { options->management_user_pass = p[3]; } } - else if (streq (p[0], "management-client-user") && p[1]) + else if (streq (p[0], "management-client-user") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_client_user = p[1]; } - else if (streq (p[0], "management-client-group") && p[1]) + else if (streq (p[0], "management-client-group") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_client_group = p[1]; } - else if (streq (p[0], "management-query-passwords")) + else if (streq (p[0], "management-query-passwords") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_QUERY_PASSWORDS; } - else if (streq (p[0], "management-query-remote")) + else if (streq (p[0], "management-query-remote") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_QUERY_REMOTE; } - else if (streq (p[0], "management-query-proxy")) + else if (streq (p[0], "management-query-proxy") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_QUERY_PROXY; - options->force_connection_list = true; } - else if (streq (p[0], "management-hold")) + else if (streq (p[0], "management-hold") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_HOLD; } - else if (streq (p[0], "management-signal")) + else if (streq (p[0], "management-signal") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_SIGNAL; } - else if (streq (p[0], "management-forget-disconnect")) + else if (streq (p[0], "management-forget-disconnect") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_FORGET_DISCONNECT; } - else if (streq (p[0], "management-up-down")) + else if (streq (p[0], "management-up-down") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_UP_DOWN; } - else if (streq (p[0], "management-client")) + else if (streq (p[0], "management-client") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_CONNECT_AS_CLIENT; options->management_write_peer_info_file = p[1]; } #ifdef MANAGMENT_EXTERNAL_KEY - else if (streq (p[0], "management-external-key")) + else if (streq (p[0], "management-external-key") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= MF_EXTERNAL_KEY; } -#endif -#ifdef MANAGEMENT_DEF_AUTH - else if (streq (p[0], "management-client-auth")) + else if (streq (p[0], "management-external-cert") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); - options->management_flags |= MF_CLIENT_AUTH; + options->management_flags |= MF_EXTERNAL_CERT; + options->management_certificate = p[1]; } #endif -#ifdef ENABLE_X509_TRACK - else if (streq (p[0], "x509-track") && p[1]) +#ifdef MANAGEMENT_DEF_AUTH + else if (streq (p[0], "management-client-auth") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); - x509_track_add (&options->x509_track, p[1], msglevel, &options->gc); + options->management_flags |= MF_CLIENT_AUTH; } #endif #ifdef MANAGEMENT_PF - else if (streq (p[0], "management-client-pf")) + else if (streq (p[0], "management-client-pf") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_flags |= (MF_CLIENT_PF | MF_CLIENT_AUTH); } #endif - else if (streq (p[0], "management-log-cache") && p[1]) + else if (streq (p[0], "management-log-cache") && p[1] && !p[2]) { int cache; @@ -4415,7 +4577,7 @@ add_option (struct options *options, } #endif #ifdef ENABLE_PLUGIN - else if (streq (p[0], "plugin") && p[1]) + else if (streq (p[0], "plugin") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_PLUGIN); if (!options->plugin_list) @@ -4427,7 +4589,7 @@ add_option (struct options *options, } } #endif - else if (streq (p[0], "mode") && p[1]) + else if (streq (p[0], "mode") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "p2p")) @@ -4442,22 +4604,22 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "dev") && p[1]) + else if (streq (p[0], "dev") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->dev = p[1]; } - else if (streq (p[0], "dev-type") && p[1]) + else if (streq (p[0], "dev-type") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->dev_type = p[1]; } - else if (streq (p[0], "dev-node") && p[1]) + else if (streq (p[0], "dev-node") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->dev_node = p[1]; } - else if (streq (p[0], "lladdr") && p[1]) + else if (streq (p[0], "lladdr") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_UP); if (mac_addr_safe (p[1])) /* MAC address only */ @@ -4468,24 +4630,24 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "topology") && p[1]) + else if (streq (p[0], "topology") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_UP); options->topology = parse_topology (p[1], msglevel); } - else if (streq (p[0], "tun-ipv6")) + else if (streq (p[0], "tun-ipv6") && !p[1]) { VERIFY_PERMISSION (OPT_P_UP); - options->tun_ipv6 = true; + msg (M_WARN, "Note: option tun-ipv6 is ignored because modern operating systems do not need special IPv6 tun handling anymore."); } #ifdef ENABLE_IPROUTE - else if (streq (p[0], "iproute") && p[1]) + else if (streq (p[0], "iproute") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); iproute_path = p[1]; } #endif - else if (streq (p[0], "ifconfig") && p[1] && p[2]) + else if (streq (p[0], "ifconfig") && p[1] && p[2] && !p[3]) { VERIFY_PERMISSION (OPT_P_UP); if (ip_or_dns_addr_safe (p[1], options->allow_pull_fqdn) && ip_or_dns_addr_safe (p[2], options->allow_pull_fqdn)) /* FQDN -- may be DNS name */ @@ -4499,7 +4661,7 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] ) + else if (streq (p[0], "ifconfig-ipv6") && p[1] && p[2] && !p[3]) { unsigned int netbits; @@ -4523,27 +4685,27 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "ifconfig-noexec")) + else if (streq (p[0], "ifconfig-noexec") && !p[1]) { VERIFY_PERMISSION (OPT_P_UP); options->ifconfig_noexec = true; } - else if (streq (p[0], "ifconfig-nowarn")) + else if (streq (p[0], "ifconfig-nowarn") && !p[1]) { VERIFY_PERMISSION (OPT_P_UP); options->ifconfig_nowarn = true; } - else if (streq (p[0], "local") && p[1]) + else if (streq (p[0], "local") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.local = p[1]; } - else if (streq (p[0], "remote-random")) + else if (streq (p[0], "remote-random") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->remote_random = true; } - else if (streq (p[0], "connection") && p[1]) + else if (streq (p[0], "connection") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], INLINE_FILE_TAG) && p[2]) @@ -4605,47 +4767,38 @@ add_option (struct options *options, options->ignore_unknown_option[i] = NULL; } - else if (streq (p[0], "remote-ip-hint") && p[1]) - { - VERIFY_PERMISSION (OPT_P_GENERAL); - options->remote_ip_hint = p[1]; - } -#if HTTP_PROXY_OVERRIDE - else if (streq (p[0], "http-proxy-override") && p[1] && p[2]) +#if ENABLE_MANAGEMENT + else if (streq (p[0], "http-proxy-override") && p[1] && p[2] && !p[4]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->http_proxy_override = parse_http_proxy_override(p[1], p[2], p[3], msglevel, &options->gc); if (!options->http_proxy_override) goto err; - options->force_connection_list = true; } #endif - else if (streq (p[0], "remote") && p[1]) + else if (streq (p[0], "remote") && p[1] && !p[4]) { struct remote_entry re; - re.remote = NULL; - re.remote_port = re.proto = -1; + re.remote = re.remote_port= NULL; + re.proto = -1; + re.af=0; VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); re.remote = p[1]; if (p[2]) { - const int port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "remote: port number associated with host %s is out of range", p[1]); - goto err; - } - re.remote_port = port; + re.remote_port = p[2]; if (p[3]) { const int proto = ascii2proto (p[3]); + const sa_family_t af = ascii2af (p[3]); if (proto < 0) { msg (msglevel, "remote: bad protocol associated with host %s: '%s'", p[1], p[3]); goto err; } re.proto = proto; + re.af = af; } } if (permission_mask & OPT_P_GENERAL) @@ -4660,7 +4813,7 @@ add_option (struct options *options, connection_entry_load_re (&options->ce, &re); } } - else if (streq (p[0], "resolv-retry") && p[1]) + else if (streq (p[0], "resolv-retry") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "infinite")) @@ -4668,22 +4821,44 @@ add_option (struct options *options, else options->resolve_retry_seconds = positive_atoi (p[1]); } - else if (streq (p[0], "connect-retry") && p[1]) + else if ((streq (p[0], "preresolve") || streq (p[0], "ip-remote-hint")) && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->resolve_in_advance = true; + /* Note the ip-remote-hint and the argument p[1] are for + backward compatibility */ + if (p[1]) + options->ip_remote_hint=p[1]; + } + else if (streq (p[0], "connect-retry") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.connect_retry_seconds = positive_atoi (p[1]); - options->ce.connect_retry_defined = true; + /* + * Limit the base value of retry wait interval to 16 bits to avoid + * overflow when scaled up for exponential backoff + */ + if (options->ce.connect_retry_seconds > 0xFFFF) + { + options->ce.connect_retry_seconds = 0xFFFF; + msg (M_WARN, "connect retry wait interval truncated to %d", + options->ce.connect_retry_seconds); + } + + if (p[2]) + options->ce.connect_retry_seconds_max = + max_int (positive_atoi (p[2]), options->ce.connect_retry_seconds); } - else if (streq (p[0], "connect-timeout") && p[1]) + else if ((streq (p[0], "connect-timeout") || streq (p[0], "server-poll-timeout")) + && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.connect_timeout = positive_atoi (p[1]); - options->ce.connect_timeout_defined = true; } - else if (streq (p[0], "connect-retry-max") && p[1]) + else if (streq (p[0], "connect-retry-max") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - options->ce.connect_retry_max = positive_atoi (p[1]); + options->connect_retry_max = positive_atoi (p[1]); } else if (streq (p[0], "ipchange") && p[1]) { @@ -4695,24 +4870,24 @@ add_option (struct options *options, string_substitute (p[1], ',', ' ', &options->gc), "ipchange", true); } - else if (streq (p[0], "float")) + else if (streq (p[0], "float") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.remote_float = true; } #ifdef ENABLE_DEBUG - else if (streq (p[0], "gremlin") && p[1]) + else if (streq (p[0], "gremlin") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->gremlin = positive_atoi (p[1]); } #endif - else if (streq (p[0], "chroot") && p[1]) + else if (streq (p[0], "chroot") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->chroot_dir = p[1]; } - else if (streq (p[0], "cd") && p[1]) + else if (streq (p[0], "cd") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (platform_chdir (p[1])) @@ -4723,13 +4898,13 @@ add_option (struct options *options, options->cd_dir = p[1]; } #ifdef ENABLE_SELINUX - else if (streq (p[0], "setcon") && p[1]) + else if (streq (p[0], "setcon") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->selinux_context = p[1]; } #endif - else if (streq (p[0], "writepid") && p[1]) + else if (streq (p[0], "writepid") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->writepid = p[1]; @@ -4748,27 +4923,27 @@ add_option (struct options *options, goto err; set_user_script (options, &options->down_script, p[1], "down", true); } - else if (streq (p[0], "down-pre")) + else if (streq (p[0], "down-pre") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->down_pre = true; } - else if (streq (p[0], "up-delay")) + else if (streq (p[0], "up-delay") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->up_delay = true; } - else if (streq (p[0], "up-restart")) + else if (streq (p[0], "up-restart") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->up_restart = true; } - else if (streq (p[0], "syslog")) + else if (streq (p[0], "syslog") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); open_syslog (p[1], false); } - else if (streq (p[0], "daemon")) + else if (streq (p[0], "daemon") && !p[2]) { bool didit = false; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -4786,7 +4961,7 @@ add_option (struct options *options, } } } - else if (streq (p[0], "inetd")) + else if (streq (p[0], "inetd") && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (!options->inetd) @@ -4841,44 +5016,50 @@ add_option (struct options *options, open_syslog (name, true); } } - else if (streq (p[0], "log") && p[1]) + else if (streq (p[0], "log") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->log = true; redirect_stdout_stderr (p[1], false); } - else if (streq (p[0], "suppress-timestamps")) + else if (streq (p[0], "suppress-timestamps") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->suppress_timestamps = true; set_suppress_timestamps(true); } - else if (streq (p[0], "log-append") && p[1]) + else if (streq (p[0], "machine-readable-output") && !p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->machine_readable_output = true; + set_machine_readable_output(true); + } + else if (streq (p[0], "log-append") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->log = true; redirect_stdout_stderr (p[1], true); } #ifdef ENABLE_MEMSTATS - else if (streq (p[0], "memstats") && p[1]) + else if (streq (p[0], "memstats") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->memstats_fn = p[1]; } #endif - else if (streq (p[0], "mlock")) + else if (streq (p[0], "mlock") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->mlock = true; } #if ENABLE_IP_PKTINFO - else if (streq (p[0], "multihome")) + else if (streq (p[0], "multihome") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->sockflags |= SF_USE_IP_PKTINFO; } #endif - else if (streq (p[0], "verb") && p[1]) + else if (streq (p[0], "verb") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MESSAGES); options->verbosity = positive_atoi (p[1]); @@ -4889,17 +5070,17 @@ add_option (struct options *options, options->verbosity); #endif } - else if (streq (p[0], "mute") && p[1]) + else if (streq (p[0], "mute") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MESSAGES); options->mute = positive_atoi (p[1]); } - else if (streq (p[0], "errors-to-stderr")) + else if (streq (p[0], "errors-to-stderr") && !p[1]) { VERIFY_PERMISSION (OPT_P_MESSAGES); errors_to_stderr(); } - else if (streq (p[0], "status") && p[1]) + else if (streq (p[0], "status") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->status_file = p[1]; @@ -4908,7 +5089,7 @@ add_option (struct options *options, options->status_file_update_freq = positive_atoi (p[2]); } } - else if (streq (p[0], "status-version") && p[1]) + else if (streq (p[0], "status-version") && p[1] && !p[2]) { int version; @@ -4921,7 +5102,7 @@ add_option (struct options *options, } options->status_file_version = version; } - else if (streq (p[0], "remap-usr1") && p[1]) + else if (streq (p[0], "remap-usr1") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "SIGHUP")) @@ -4934,19 +5115,19 @@ add_option (struct options *options, goto err; } } - else if ((streq (p[0], "link-mtu") || streq (p[0], "udp-mtu")) && p[1]) + else if ((streq (p[0], "link-mtu") || streq (p[0], "udp-mtu")) && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MTU|OPT_P_CONNECTION); options->ce.link_mtu = positive_atoi (p[1]); options->ce.link_mtu_defined = true; } - else if (streq (p[0], "tun-mtu") && p[1]) + else if (streq (p[0], "tun-mtu") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MTU|OPT_P_CONNECTION); options->ce.tun_mtu = positive_atoi (p[1]); options->ce.tun_mtu_defined = true; } - else if (streq (p[0], "tun-mtu-extra") && p[1]) + else if (streq (p[0], "tun-mtu-extra") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MTU|OPT_P_CONNECTION); options->ce.tun_mtu_extra = positive_atoi (p[1]); @@ -4959,41 +5140,41 @@ add_option (struct options *options, msg (msglevel, "--mtu-dynamic has been replaced by --fragment"); goto err; } - else if (streq (p[0], "fragment") && p[1]) + else if (streq (p[0], "fragment") && p[1] && !p[2]) { /* VERIFY_PERMISSION (OPT_P_MTU); */ VERIFY_PERMISSION (OPT_P_MTU|OPT_P_CONNECTION); options->ce.fragment = positive_atoi (p[1]); } #endif - else if (streq (p[0], "mtu-disc") && p[1]) + else if (streq (p[0], "mtu-disc") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_MTU|OPT_P_CONNECTION); options->ce.mtu_discover_type = translate_mtu_discover_type_name (p[1]); } #ifdef ENABLE_OCC - else if (streq (p[0], "mtu-test")) + else if (streq (p[0], "mtu-test") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->mtu_test = true; } #endif - else if (streq (p[0], "nice") && p[1]) + else if (streq (p[0], "nice") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_NICE); options->nice = atoi (p[1]); } - else if (streq (p[0], "rcvbuf") && p[1]) + else if (streq (p[0], "rcvbuf") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_SOCKBUF); options->rcvbuf = positive_atoi (p[1]); } - else if (streq (p[0], "sndbuf") && p[1]) + else if (streq (p[0], "sndbuf") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_SOCKBUF); options->sndbuf = positive_atoi (p[1]); } - else if (streq (p[0], "mark") && p[1]) + else if (streq (p[0], "mark") && p[1] && !p[2]) { #if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK VERIFY_PERMISSION (OPT_P_GENERAL); @@ -5012,7 +5193,7 @@ add_option (struct options *options, msg (msglevel, "unknown socket flag: %s", p[j]); } } - else if (streq (p[0], "txqueuelen") && p[1]) + else if (streq (p[0], "txqueuelen") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); #ifdef TARGET_LINUX @@ -5022,7 +5203,7 @@ add_option (struct options *options, goto err; #endif } - else if (streq (p[0], "shaper") && p[1]) + else if (streq (p[0], "shaper") && p[1] && !p[2]) { #ifdef ENABLE_FEATURE_SHAPER int shaper; @@ -5042,73 +5223,54 @@ add_option (struct options *options, goto err; #endif /* ENABLE_FEATURE_SHAPER */ } - else if (streq (p[0], "port") && p[1]) + else if (streq (p[0], "port") && p[1] && !p[2]) { - int port; - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - port = atoi (p[1]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "Bad port number: %s", p[1]); - goto err; - } - options->ce.local_port = options->ce.remote_port = port; + options->ce.local_port = options->ce.remote_port = p[1]; } - else if (streq (p[0], "lport") && p[1]) + else if (streq (p[0], "lport") && p[1] && !p[2]) { - int port; - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - port = atoi (p[1]); - if ((port != 0) && !legal_ipv4_port (port)) - { - msg (msglevel, "Bad local port number: %s", p[1]); - goto err; - } options->ce.local_port_defined = true; - options->ce.local_port = port; + options->ce.local_port = p[1]; } - else if (streq (p[0], "rport") && p[1]) + else if (streq (p[0], "rport") && p[1] && !p[2]) { - int port; - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - port = atoi (p[1]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "Bad remote port number: %s", p[1]); - goto err; - } - options->ce.remote_port = port; + options->ce.remote_port = p[1]; } - else if (streq (p[0], "bind")) + else if (streq (p[0], "bind") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.bind_defined = true; + if (p[1] && streq (p[1], "ipv6only")) + options->ce.bind_ipv6_only=true; + } - else if (streq (p[0], "nobind")) + else if (streq (p[0], "nobind") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); options->ce.bind_local = false; } - else if (streq (p[0], "fast-io")) + else if (streq (p[0], "fast-io") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->fast_io = true; } - else if (streq (p[0], "inactive") && p[1]) + else if (streq (p[0], "inactive") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_TIMER); options->inactivity_timeout = positive_atoi (p[1]); if (p[2]) options->inactivity_minimum_bytes = positive_atoi (p[2]); } - else if (streq (p[0], "proto") && p[1]) + else if (streq (p[0], "proto") && p[1] && !p[2]) { int proto; + sa_family_t af; VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); proto = ascii2proto (p[1]); + af = ascii2af(p[1]); if (proto < 0) { msg (msglevel, "Bad protocol: '%s'. Allowed protocols with --proto option: %s", @@ -5117,8 +5279,9 @@ add_option (struct options *options, goto err; } options->ce.proto = proto; + options->ce.af = af; } - else if (streq (p[0], "proto-force") && p[1]) + else if (streq (p[0], "proto-force") && p[1] && !p[2]) { int proto_force; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -5129,33 +5292,24 @@ add_option (struct options *options, goto err; } options->proto_force = proto_force; - options->force_connection_list = true; } -#ifdef ENABLE_HTTP_PROXY - else if (streq (p[0], "http-proxy") && p[1]) + else if (streq (p[0], "http-proxy") && p[1] && !p[5]) { struct http_proxy_options *ho; VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); { - int port; if (!p[2]) { msg (msglevel, "http-proxy port number not defined"); goto err; } - port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "Bad http-proxy port number: %s", p[2]); - goto err; - } ho = init_http_proxy_options_once (&options->ce.http_proxy_options, &options->gc); ho->server = p[1]; - ho->port = port; + ho->port = p[2]; } if (p[3]) @@ -5183,101 +5337,124 @@ add_option (struct options *options, ho->auth_method_string = "none"; } } - else if (streq (p[0], "http-proxy-retry")) + else if (streq (p[0], "http-proxy-user-pass") && p[1]) { struct http_proxy_options *ho; - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); + VERIFY_PERMISSION (OPT_P_GENERAL); ho = init_http_proxy_options_once (&options->ce.http_proxy_options, &options->gc); - ho->retry = true; + if (streq (p[1], INLINE_FILE_TAG) && p[2]) + { + ho->auth_file = p[2]; + ho->inline_creds = true; + } + else + ho->auth_file = p[1]; } - else if (streq (p[0], "http-proxy-timeout") && p[1]) + else if (streq (p[0], "http-proxy-retry") || streq (p[0], "socks-proxy-retry")) { - struct http_proxy_options *ho; - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - ho = init_http_proxy_options_once (&options->ce.http_proxy_options, &options->gc); - ho->timeout = positive_atoi (p[1]); + msg (M_WARN, "DEPRECATED OPTION: http-proxy-retry and socks-proxy-retry: " + "In OpenVPN 2.4 proxy connection retries are handled like regular connections. " + "Use connect-retry-max 1 to get a similar behavior as before."); } - else if (streq (p[0], "http-proxy-option") && p[1]) + else if (streq (p[0], "http-proxy-timeout") && p[1] && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); + msg (M_WARN, "DEPRECATED OPTION: http-proxy-timeout: In OpenVPN 2.4 the timeout until a connection to a " + "server is established is managed with a single timeout set by connect-timeout"); + } + else if (streq (p[0], "http-proxy-option") && p[1] && !p[4]) { struct http_proxy_options *ho; VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); ho = init_http_proxy_options_once (&options->ce.http_proxy_options, &options->gc); - if (streq (p[1], "VERSION") && p[2]) + if (streq (p[1], "VERSION") && p[2] && !p[3]) { ho->http_version = p[2]; } - else if (streq (p[1], "AGENT") && p[2]) + else if (streq (p[1], "AGENT") && p[2] && !p[3]) { ho->user_agent = p[2]; } + else if ((streq (p[1], "EXT1") || streq(p[1], "EXT2") || streq(p[1], "CUSTOM-HEADER")) + && p[2]) + { + /* In the wild patched versions use both EXT1/2 and CUSTOM-HEADER + * with either two argument or one */ + + struct http_custom_header *custom_header = NULL; + int i; + /* Find the first free header */ + for (i=0; i < MAX_CUSTOM_HTTP_HEADER; i++) { + if (!ho->custom_headers[i].name) { + custom_header = &ho->custom_headers[i]; + break; + } + } + if (!custom_header) + { + msg (msglevel, "Cannot use more than %d http-proxy-option CUSTOM-HEADER : '%s'", MAX_CUSTOM_HTTP_HEADER, p[1]); + } + else + { + /* We will save p[2] and p[3], the proxy code will detect if + * p[3] is NULL */ + custom_header->name = p[2]; + custom_header->content = p[3]; + } + } else { - msg (msglevel, "Bad http-proxy-option or missing parameter: '%s'", p[1]); + msg (msglevel, "Bad http-proxy-option or missing or extra parameter: '%s'", p[1]); } } -#endif -#ifdef ENABLE_SOCKS - else if (streq (p[0], "socks-proxy") && p[1]) + else if (streq (p[0], "socks-proxy") && p[1] && !p[4]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); if (p[2]) - { - int port; - port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "Bad socks-proxy port number: %s", p[2]); - goto err; - } - options->ce.socks_proxy_port = port; + { + options->ce.socks_proxy_port = p[2]; } else { - options->ce.socks_proxy_port = 1080; + options->ce.socks_proxy_port = "1080"; } options->ce.socks_proxy_server = p[1]; options->ce.socks_proxy_authfile = p[3]; /* might be NULL */ } - else if (streq (p[0], "socks-proxy-retry")) - { - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - options->ce.socks_proxy_retry = true; - } -#endif - else if (streq (p[0], "keepalive") && p[1] && p[2]) + else if (streq (p[0], "keepalive") && p[1] && p[2] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->keepalive_ping = atoi (p[1]); options->keepalive_timeout = atoi (p[2]); } - else if (streq (p[0], "ping") && p[1]) + else if (streq (p[0], "ping") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TIMER); options->ping_send_timeout = positive_atoi (p[1]); } - else if (streq (p[0], "ping-exit") && p[1]) + else if (streq (p[0], "ping-exit") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TIMER); options->ping_rec_timeout = positive_atoi (p[1]); options->ping_rec_timeout_action = PING_EXIT; } - else if (streq (p[0], "ping-restart") && p[1]) + else if (streq (p[0], "ping-restart") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TIMER); options->ping_rec_timeout = positive_atoi (p[1]); options->ping_rec_timeout_action = PING_RESTART; } - else if (streq (p[0], "ping-timer-rem")) + else if (streq (p[0], "ping-timer-rem") && !p[1]) { VERIFY_PERMISSION (OPT_P_TIMER); options->ping_timer_remote = true; } #ifdef ENABLE_OCC - else if (streq (p[0], "explicit-exit-notify")) + else if (streq (p[0], "explicit-exit-notify") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION|OPT_P_EXPLICIT_NOTIFY); if (p[1]) @@ -5290,35 +5467,33 @@ add_option (struct options *options, } } #endif - else if (streq (p[0], "persist-tun")) + else if (streq (p[0], "persist-tun") && !p[1]) { VERIFY_PERMISSION (OPT_P_PERSIST); options->persist_tun = true; } - else if (streq (p[0], "persist-key")) + else if (streq (p[0], "persist-key") && !p[1]) { VERIFY_PERMISSION (OPT_P_PERSIST); options->persist_key = true; } - else if (streq (p[0], "persist-local-ip")) + else if (streq (p[0], "persist-local-ip") && !p[1]) { VERIFY_PERMISSION (OPT_P_PERSIST_IP); options->persist_local_ip = true; } - else if (streq (p[0], "persist-remote-ip")) + else if (streq (p[0], "persist-remote-ip") && !p[1]) { 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]) + else if (streq (p[0], "client-nat") && p[1] && p[2] && p[3] && p[4] && !p[5]) { 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]) + else if (streq (p[0], "route") && p[1] && !p[5]) { VERIFY_PERMISSION (OPT_P_ROUTE); rol_check_alloc (options); @@ -5342,7 +5517,7 @@ add_option (struct options *options, } add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]); } - else if (streq (p[0], "route-ipv6") && p[1]) + else if (streq (p[0], "route-ipv6") && p[1] && !p[4]) { VERIFY_PERMISSION (OPT_P_ROUTE); rol6_check_alloc (options); @@ -5362,25 +5537,14 @@ add_option (struct options *options, } add_route_ipv6_to_option_list (options->routes_ipv6, p[1], p[2], p[3]); } - else if (streq (p[0], "max-routes") && p[1]) + else if (streq (p[0], "max-routes") && !p[2]) { - int max_routes; - - VERIFY_PERMISSION (OPT_P_GENERAL); - max_routes = atoi (p[1]); - if (max_routes < 0 || max_routes > 100000000) - { - msg (msglevel, "--max-routes parameter is out of range"); - goto err; - } - if (options->routes || options->routes_ipv6) - { - msg (msglevel, "--max-routes must to be specifed before any route/route-ipv6/redirect-gateway option"); - goto err; - } - options->max_routes = max_routes; + msg (M_WARN, "DEPRECATED OPTION: --max-routes option ignored." + "The number of routes is unlimited as of version 2.4. " + "This option will be removed in a future version, " + "please remove it from your configuration."); } - else if (streq (p[0], "route-gateway") && p[1]) + else if (streq (p[0], "route-gateway") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); if (streq (p[1], "dhcp")) @@ -5400,12 +5564,12 @@ add_option (struct options *options, } } } - else if (streq (p[0], "route-metric") && p[1]) + else if (streq (p[0], "route-metric") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_ROUTE); options->route_default_metric = positive_atoi (p[1]); } - else if (streq (p[0], "route-delay")) + else if (streq (p[0], "route-delay") && !p[3]) { VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); options->route_delay_defined = true; @@ -5439,17 +5603,37 @@ add_option (struct options *options, p[1], "route-pre-down", true); } - else if (streq (p[0], "route-noexec")) + else if (streq (p[0], "route-noexec") && !p[1]) { VERIFY_PERMISSION (OPT_P_SCRIPT); options->route_noexec = true; } - else if (streq (p[0], "route-nopull")) + else if (streq (p[0], "route-nopull") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->route_nopull = true; } - else if (streq (p[0], "allow-pull-fqdn")) + else if (streq (p[0], "pull-filter") && p[1] && p[2] && !p[3]) + { + struct pull_filter *f; + VERIFY_PERMISSION (OPT_P_GENERAL) + f = alloc_pull_filter (options, msglevel); + + if (strcmp ("accept", p[1]) == 0) + f->type = PUF_TYPE_ACCEPT; + else if (strcmp ("ignore", p[1]) == 0) + f->type = PUF_TYPE_IGNORE; + else if (strcmp ("reject", p[1]) == 0) + f->type = PUF_TYPE_REJECT; + else + { + msg (msglevel, "Unknown --pull-filter type: %s", p[1]); + goto err; + } + f->pattern = p[2]; + f->size = strlen(p[2]); + } + else if (streq (p[0], "allow-pull-fqdn") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->allow_pull_fqdn = true; @@ -5475,6 +5659,13 @@ add_option (struct options *options, options->routes->flags |= RG_BYPASS_DNS; else if (streq (p[j], "block-local")) options->routes->flags |= RG_BLOCK_LOCAL; + else if (streq (p[j], "ipv6")) + { + rol6_check_alloc (options); + options->routes_ipv6->flags |= RG_REROUTE_GW; + } + else if (streq (p[j], "!ipv4")) + options->routes->flags &= ~RG_REROUTE_GW; else { msg (msglevel, "unknown --%s flag: %s", p[0], p[j]); @@ -5483,15 +5674,15 @@ add_option (struct options *options, } options->routes->flags |= RG_ENABLE; } - else if (streq (p[0], "remote-random-hostname")) + else if (streq (p[0], "remote-random-hostname") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->sockflags |= SF_HOST_RANDOMIZE; } - else if (streq (p[0], "setenv") && p[1]) + else if (streq (p[0], "setenv") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); - if (streq (p[1], "REMOTE_RANDOM_HOSTNAME")) + if (streq (p[1], "REMOTE_RANDOM_HOSTNAME") && !p[2]) { options->sockflags |= SF_HOST_RANDOMIZE; } @@ -5501,17 +5692,15 @@ add_option (struct options *options, goto err; } #ifdef ENABLE_PUSH_PEER_INFO - else if (streq (p[1], "PUSH_PEER_INFO")) + else if (streq (p[1], "PUSH_PEER_INFO") && !p[2]) { options->push_peer_info = true; } #endif -#if P2MP else if (streq (p[1], "SERVER_POLL_TIMEOUT") && p[2]) { - options->server_poll_timeout = positive_atoi(p[2]); + options->ce.connect_timeout = positive_atoi(p[2]); } -#endif else { if (streq (p[1], "FORWARD_COMPATIBLE") && p[2] && streq (p[2], "1")) @@ -5522,17 +5711,17 @@ add_option (struct options *options, setenv_str (es, p[1], p[2] ? p[2] : ""); } } - else if (streq (p[0], "setenv-safe") && p[1]) + else if (streq (p[0], "setenv-safe") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_SETENV); setenv_str_safe (es, p[1], p[2] ? p[2] : ""); } - else if (streq (p[0], "script-security") && p[1]) + else if (streq (p[0], "script-security") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); script_security = atoi (p[1]); } - else if (streq (p[0], "mssfix")) + else if (streq (p[0], "mssfix") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); if (p[1]) @@ -5544,7 +5733,7 @@ add_option (struct options *options, } #ifdef ENABLE_OCC - else if (streq (p[0], "disable-occ")) + else if (streq (p[0], "disable-occ") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->occ = false; @@ -5552,7 +5741,7 @@ add_option (struct options *options, #endif #if P2MP #if P2MP_SERVER - else if (streq (p[0], "server") && p[1] && p[2]) + else if (streq (p[0], "server") && p[1] && p[2] && !p[4]) { const int lev = M_WARN; bool error = false; @@ -5581,7 +5770,7 @@ add_option (struct options *options, } } } - else if (streq (p[0], "server-ipv6") && p[1] ) + else if (streq (p[0], "server-ipv6") && p[1] && !p[3]) { const int lev = M_WARN; struct in6_addr network; @@ -5608,7 +5797,7 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4]) + else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4] && !p[5]) { const int lev = M_WARN; bool error = false; @@ -5630,7 +5819,7 @@ add_option (struct options *options, options->server_bridge_pool_start = pool_start; options->server_bridge_pool_end = pool_end; } - else if (streq (p[0], "server-bridge") && p[1] && streq (p[1], "nogw")) + else if (streq (p[0], "server-bridge") && p[1] && streq (p[1], "nogw") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->server_bridge_proxy_dhcp = true; @@ -5641,17 +5830,23 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_GENERAL); options->server_bridge_proxy_dhcp = true; } - else if (streq (p[0], "push") && p[1]) + else if (streq (p[0], "push") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_PUSH); push_options (options, &p[1], msglevel, &options->gc); } - else if (streq (p[0], "push-reset")) + else if (streq (p[0], "push-reset") && !p[1]) { VERIFY_PERMISSION (OPT_P_INSTANCE); push_reset (options); } - else if (streq (p[0], "ifconfig-pool") && p[1] && p[2]) + else if (streq (p[0], "push-remove") && p[1] && !p[2]) + { + VERIFY_PERMISSION (OPT_P_INSTANCE); + msg (D_PUSH, "PUSH_REMOVE '%s'", p[1]); + push_remove_option (options,p[1]); + } + else if (streq (p[0], "ifconfig-pool") && p[1] && p[2] && !p[4]) { const int lev = M_WARN; bool error = false; @@ -5678,7 +5873,7 @@ add_option (struct options *options, if (netmask) options->ifconfig_pool_netmask = netmask; } - else if (streq (p[0], "ifconfig-pool-persist") && p[1]) + else if (streq (p[0], "ifconfig-pool-persist") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ifconfig_pool_persist_filename = p[1]; @@ -5687,12 +5882,12 @@ add_option (struct options *options, options->ifconfig_pool_persist_refresh_freq = positive_atoi (p[2]); } } - else if (streq (p[0], "ifconfig-pool-linear")) + else if (streq (p[0], "ifconfig-pool-linear") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->topology = TOP_P2P; } - else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] ) + else if (streq (p[0], "ifconfig-ipv6-pool") && p[1] && !p[2]) { const int lev = M_WARN; struct in6_addr network; @@ -5714,7 +5909,7 @@ add_option (struct options *options, options->ifconfig_ipv6_pool_base = network; options->ifconfig_ipv6_pool_netbits = netbits; } - else if (streq (p[0], "hash-size") && p[1] && p[2]) + else if (streq (p[0], "hash-size") && p[1] && p[2] && !p[3]) { int real, virtual; @@ -5729,7 +5924,7 @@ add_option (struct options *options, options->real_hash_size = real; options->virtual_hash_size = real; } - else if (streq (p[0], "connect-freq") && p[1] && p[2]) + else if (streq (p[0], "connect-freq") && p[1] && p[2] && !p[3]) { int cf_max, cf_per; @@ -5744,7 +5939,7 @@ add_option (struct options *options, options->cf_max = cf_max; options->cf_per = cf_per; } - else if (streq (p[0], "max-clients") && p[1]) + else if (streq (p[0], "max-clients") && p[1] && !p[2]) { int max_clients; @@ -5755,29 +5950,55 @@ add_option (struct options *options, msg (msglevel, "--max-clients must be at least 1"); goto err; } + if (max_clients >= MAX_PEER_ID) /* max peer-id value */ + { + msg (msglevel, "--max-clients must be less than %d", MAX_PEER_ID); + goto err; + } options->max_clients = max_clients; } - else if (streq (p[0], "max-routes-per-client") && p[1]) + else if (streq (p[0], "max-routes-per-client") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_INHERIT); options->max_routes_per_client = max_int (atoi (p[1]), 1); } - else if (streq (p[0], "client-cert-not-required")) + else if (streq (p[0], "client-cert-not-required") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_CLIENT_CERT_NOT_REQUIRED; + msg (M_WARN, "DEPRECATED OPTION: --client-cert-not-required, use --verify-client-cert instead"); + } + else if (streq (p[0], "verify-client-cert") && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + + /* Reset any existing flags */ + options->ssl_flags &= ~SSLF_CLIENT_CERT_OPTIONAL; + options->ssl_flags &= ~SSLF_CLIENT_CERT_NOT_REQUIRED; + if (p[1]) + { + if (streq (p[1], "none")) + options->ssl_flags |= SSLF_CLIENT_CERT_NOT_REQUIRED; + else if (streq (p[1], "optional")) + options->ssl_flags |= SSLF_CLIENT_CERT_OPTIONAL; + else if (!streq (p[1], "require")) + { + msg (msglevel, "parameter to --verify-client-cert must be 'none', 'optional' or 'require'"); + goto err; + } + } } - else if (streq (p[0], "username-as-common-name")) + else if (streq (p[0], "username-as-common-name") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_USERNAME_AS_COMMON_NAME; } - else if (streq (p[0], "auth-user-pass-optional")) + else if (streq (p[0], "auth-user-pass-optional") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_AUTH_USER_PASS_OPTIONAL; } - else if (streq (p[0], "opt-verify")) + else if (streq (p[0], "opt-verify") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_OPT_VERIFY; @@ -5808,6 +6029,12 @@ add_option (struct options *options, &options->auth_user_pass_verify_script, p[1], "auth-user-pass-verify", true); } + else if (streq (p[0], "auth-gen-token")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->auth_token_generate = true; + options->auth_token_lifetime = p[1] ? positive_atoi (p[1]) : 0; + } else if (streq (p[0], "client-connect") && p[1]) { VERIFY_PERMISSION (OPT_P_SCRIPT); @@ -5832,22 +6059,22 @@ add_option (struct options *options, set_user_script (options, &options->learn_address_script, p[1], "learn-address", true); } - else if (streq (p[0], "tmp-dir") && p[1]) + else if (streq (p[0], "tmp-dir") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->tmp_dir = p[1]; } - else if (streq (p[0], "client-config-dir") && p[1]) + else if (streq (p[0], "client-config-dir") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->client_config_dir = p[1]; } - else if (streq (p[0], "ccd-exclusive")) + else if (streq (p[0], "ccd-exclusive") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ccd_exclusive = true; } - else if (streq (p[0], "bcast-buffers") && p[1]) + else if (streq (p[0], "bcast-buffers") && p[1] && !p[2]) { int n_bcast_buf; @@ -5857,7 +6084,7 @@ add_option (struct options *options, msg (msglevel, "--bcast-buffers parameter must be > 0"); options->n_bcast_buf = n_bcast_buf; } - else if (streq (p[0], "tcp-queue-limit") && p[1]) + else if (streq (p[0], "tcp-queue-limit") && p[1] && !p[2]) { int tcp_queue_limit; @@ -5868,34 +6095,25 @@ add_option (struct options *options, options->tcp_queue_limit = tcp_queue_limit; } #if PORT_SHARE - else if (streq (p[0], "port-share") && p[1] && p[2]) + else if (streq (p[0], "port-share") && p[1] && p[2] && !p[4]) { - int port; - VERIFY_PERMISSION (OPT_P_GENERAL); - port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "port number associated with --port-share directive is out of range"); - goto err; - } - options->port_share_host = p[1]; - options->port_share_port = port; + options->port_share_port = p[2]; options->port_share_journal_dir = p[3]; } #endif - else if (streq (p[0], "client-to-client")) + else if (streq (p[0], "client-to-client") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->enable_c2c = true; } - else if (streq (p[0], "duplicate-cn")) + else if (streq (p[0], "duplicate-cn") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->duplicate_cn = true; } - else if (streq (p[0], "iroute") && p[1]) + else if (streq (p[0], "iroute") && p[1] && !p[3]) { const char *netmask = NULL; @@ -5906,12 +6124,12 @@ add_option (struct options *options, } option_iroute (options, p[1], netmask, msglevel); } - else if (streq (p[0], "iroute-ipv6") && p[1]) + else if (streq (p[0], "iroute-ipv6") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_INSTANCE); option_iroute_ipv6 (options, p[1], msglevel); } - else if (streq (p[0], "ifconfig-push") && p[1] && p[2]) + else if (streq (p[0], "ifconfig-push") && p[1] && p[2] && !p[4]) { in_addr_t local, remote_netmask; @@ -5923,10 +6141,8 @@ 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 { @@ -5934,7 +6150,7 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "ifconfig-push-constraint") && p[1] && p[2]) + else if (streq (p[0], "ifconfig-push-constraint") && p[1] && p[2] && !p[3]) { in_addr_t network, netmask; @@ -5953,7 +6169,7 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "ifconfig-ipv6-push") && p[1] ) + else if (streq (p[0], "ifconfig-ipv6-push") && p[1] && !p[3]) { struct in6_addr local, remote; unsigned int netbits; @@ -5989,18 +6205,19 @@ add_option (struct options *options, options->push_ifconfig_ipv6_local = local; options->push_ifconfig_ipv6_netbits = netbits; options->push_ifconfig_ipv6_remote = remote; + options->push_ifconfig_ipv6_blocked = false; } - else if (streq (p[0], "disable")) + else if (streq (p[0], "disable") && !p[1]) { VERIFY_PERMISSION (OPT_P_INSTANCE); options->disable = true; } - else if (streq (p[0], "tcp-nodelay")) + else if (streq (p[0], "tcp-nodelay") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->server_flags |= SF_TCP_NODELAY_HELPER; } - else if (streq (p[0], "stale-routes-check") && p[1]) + else if (streq (p[0], "stale-routes-check") && p[1] && !p[3]) { int ageing_time, check_interval; @@ -6021,27 +6238,22 @@ add_option (struct options *options, } #endif /* P2MP_SERVER */ - else if (streq (p[0], "client")) + else if (streq (p[0], "client") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->client = true; } - else if (streq (p[0], "pull")) + else if (streq (p[0], "pull") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->pull = true; } - else if (streq (p[0], "push-continuation") && p[1]) + else if (streq (p[0], "push-continuation") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_PULL_MODE); options->push_continuation = atoi(p[1]); } - else if (streq (p[0], "server-poll-timeout") && p[1]) - { - VERIFY_PERMISSION (OPT_P_GENERAL); - options->server_poll_timeout = positive_atoi(p[1]); - } - else if (streq (p[0], "auth-user-pass")) + else if (streq (p[0], "auth-user-pass") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (p[1]) @@ -6051,13 +6263,13 @@ add_option (struct options *options, else options->auth_user_pass_file = "stdin"; } - else if (streq (p[0], "auth-retry") && p[1]) + else if (streq (p[0], "auth-retry") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); auth_retry_set (msglevel, p[1]); } #ifdef ENABLE_CLIENT_CR - else if (streq (p[0], "static-challenge") && p[1] && p[2]) + else if (streq (p[0], "static-challenge") && p[1] && p[2] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->sc_info.challenge_text = p[1]; @@ -6066,8 +6278,26 @@ add_option (struct options *options, } #endif #endif -#ifdef WIN32 - else if (streq (p[0], "win-sys") && p[1]) + else if (streq (p[0], "msg-channel") && p[1]) + { +#ifdef _WIN32 + VERIFY_PERMISSION (OPT_P_GENERAL); + HANDLE process = GetCurrentProcess (); + HANDLE handle = (HANDLE) atoi (p[1]); + if (!DuplicateHandle (process, handle, process, &options->msg_channel, 0, + FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + msg (msglevel, "could not duplicate service pipe handle"); + goto err; + } + options->route_method = ROUTE_METHOD_SERVICE; +#else + msg (msglevel, "--msg-channel is only supported on Windows"); + goto err; +#endif + } +#ifdef _WIN32 + else if (streq (p[0], "win-sys") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "env")) @@ -6077,7 +6307,7 @@ add_option (struct options *options, else set_win_sys_path (p[1], es); } - else if (streq (p[0], "route-method") && p[1]) + else if (streq (p[0], "route-method") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); if (streq (p[1], "adaptive")) @@ -6092,7 +6322,7 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "ip-win32") && p[1]) + else if (streq (p[0], "ip-win32") && p[1] && !p[4]) { const int index = ascii2ipset (p[1]); struct tuntap_options *to = &options->tuntap_options; @@ -6146,7 +6376,9 @@ add_option (struct options *options, to->ip_win32_type = index; to->ip_win32_defined = true; } - else if (streq (p[0], "dhcp-option") && p[1]) +#endif +#if defined(_WIN32) || defined(TARGET_ANDROID) + else if (streq (p[0], "dhcp-option") && p[1] && !p[3]) { struct tuntap_options *o = &options->tuntap_options; VERIFY_PERMISSION (OPT_P_IPWIN32); @@ -6186,36 +6418,38 @@ add_option (struct options *options, { dhcp_option_address_parse ("NBDD", p[2], o->nbdd, &o->nbdd_len, msglevel); } - else if (streq (p[1], "DISABLE-NBT")) + else if (streq (p[1], "DISABLE-NBT") && !p[2]) { o->disable_nbt = 1; } else { - msg (msglevel, "--dhcp-option: unknown option type '%s' or missing parameter", p[1]); + msg (msglevel, "--dhcp-option: unknown option type '%s' or missing or unknown parameter", p[1]); goto err; } o->dhcp_options = true; } - else if (streq (p[0], "show-adapters")) +#endif +#ifdef _WIN32 + else if (streq (p[0], "show-adapters") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); show_tap_win_adapters (M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "show-net")) + else if (streq (p[0], "show-net") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); show_routes (M_INFO|M_NOPREFIX); show_adapters (M_INFO|M_NOPREFIX); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "show-net-up")) + else if (streq (p[0], "show-net-up") && !p[1]) { VERIFY_PERMISSION (OPT_P_UP); options->show_net_up = true; } - else if (streq (p[0], "tap-sleep") && p[1]) + else if (streq (p[0], "tap-sleep") && p[1] && !p[2]) { int s; VERIFY_PERMISSION (OPT_P_IPWIN32); @@ -6227,22 +6461,22 @@ add_option (struct options *options, } options->tuntap_options.tap_sleep = s; } - else if (streq (p[0], "dhcp-renew")) + else if (streq (p[0], "dhcp-renew") && !p[1]) { VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.dhcp_renew = true; } - else if (streq (p[0], "dhcp-pre-release")) + else if (streq (p[0], "dhcp-pre-release") && !p[1]) { VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.dhcp_pre_release = true; } - else if (streq (p[0], "dhcp-release")) + else if (streq (p[0], "dhcp-release") && !p[1]) { VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.dhcp_release = true; } - else if (streq (p[0], "dhcp-internal") && p[1]) /* standalone method for internal use */ + else if (streq (p[0], "dhcp-internal") && p[1] && !p[2]) /* standalone method for internal use */ { unsigned int adapter_index; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -6255,28 +6489,17 @@ add_option (struct options *options, dhcp_renew_by_adapter_index (adapter_index); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "register-dns")) + else if (streq (p[0], "register-dns") && !p[1]) { VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.register_dns = true; } -#ifdef WIN32 else if (streq (p[0], "block-outside-dns") && !p[1]) { VERIFY_PERMISSION (OPT_P_IPWIN32); - if (win_wfp_init_funcs()) - { - options->block_outside_dns = true; - } - else - { - msg (msglevel_fc, "Failed to enable --block-outside-dns. " - "Maybe WFP is not supported on your system?"); - goto err; - } + options->block_outside_dns = true; } -#endif - else if (streq (p[0], "rdns-internal")) + else if (streq (p[0], "rdns-internal") && !p[1]) /* standalone method for internal use * * (if --register-dns is set, openvpn needs to call itself in a @@ -6290,18 +6513,18 @@ add_option (struct options *options, ipconfig_register_dns (NULL); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "show-valid-subnets")) + else if (streq (p[0], "show-valid-subnets") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); show_valid_win32_tun_subnets (); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "pause-exit")) + else if (streq (p[0], "pause-exit") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); set_pause_exit_win32 (); } - else if (streq (p[0], "service") && p[1]) + else if (streq (p[0], "service") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->exit_event_name = p[1]; @@ -6310,62 +6533,75 @@ add_option (struct options *options, options->exit_event_initial_state = (atoi(p[2]) != 0); } } - else if (streq (p[0], "allow-nonadmin")) + else if (streq (p[0], "allow-nonadmin") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); tap_allow_nonadmin_access (p[1]); openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } - else if (streq (p[0], "user") && p[1]) + else if (streq (p[0], "user") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); msg (M_WARN, "NOTE: --user option is not implemented on Windows"); } - else if (streq (p[0], "group") && p[1]) + else if (streq (p[0], "group") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); msg (M_WARN, "NOTE: --group option is not implemented on Windows"); } #else - else if (streq (p[0], "user") && p[1]) + else if (streq (p[0], "user") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->username = p[1]; } - else if (streq (p[0], "group") && p[1]) + else if (streq (p[0], "group") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->groupname = p[1]; } - else if (streq (p[0], "dhcp-option") && p[1]) + else if (streq (p[0], "dhcp-option") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_IPWIN32); foreign_option (options, p, 3, es); } - else if (streq (p[0], "route-method") && p[1]) /* ignore when pushed to non-Windows OS */ + else if (streq (p[0], "route-method") && p[1] && !p[2]) /* ignore when pushed to non-Windows OS */ { VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS); } #endif #if PASSTOS_CAPABILITY - else if (streq (p[0], "passtos")) + else if (streq (p[0], "passtos") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->passtos = true; } #endif -#ifdef ENABLE_LZO - else if (streq (p[0], "comp-lzo")) +#if defined(USE_COMP) + else if (streq (p[0], "comp-lzo") && !p[2]) { VERIFY_PERMISSION (OPT_P_COMP); - if (p[1]) + +#if defined(ENABLE_LZO) + if (p[1] && streq (p[1], "no")) +#endif + { + options->comp.alg = COMP_ALG_STUB; + options->comp.flags = 0; + } +#if defined(ENABLE_LZO) + else if (p[1]) { if (streq (p[1], "yes")) - options->lzo = LZO_SELECTED|LZO_ON; - else if (streq (p[1], "no")) - options->lzo = LZO_SELECTED; + { + options->comp.alg = COMP_ALG_LZO; + options->comp.flags = 0; + } else if (streq (p[1], "adaptive")) - options->lzo = LZO_SELECTED|LZO_ON|LZO_ADAPTIVE; + { + options->comp.alg = COMP_ALG_LZO; + options->comp.flags = COMP_F_ADAPTIVE; + } else { msg (msglevel, "bad comp-lzo option: %s -- must be 'yes', 'no', or 'adaptive'", p[1]); @@ -6373,31 +6609,81 @@ add_option (struct options *options, } } else - options->lzo = LZO_SELECTED|LZO_ON|LZO_ADAPTIVE; + { + options->comp.alg = COMP_ALG_LZO; + options->comp.flags = COMP_F_ADAPTIVE; + } +#endif + } + else if (streq (p[0], "comp-noadapt") && !p[1]) + { + VERIFY_PERMISSION (OPT_P_COMP); + options->comp.flags &= ~COMP_F_ADAPTIVE; } - else if (streq (p[0], "comp-noadapt")) + else if (streq (p[0], "compress") && !p[2]) { VERIFY_PERMISSION (OPT_P_COMP); - options->lzo &= ~LZO_ADAPTIVE; + if (p[1]) + { + if (streq (p[1], "stub")) + { + options->comp.alg = COMP_ALG_STUB; + options->comp.flags = (COMP_F_SWAP|COMP_F_ADVERTISE_STUBS_ONLY); + } + else if (streq(p[1], "stub-v2")) + { + options->comp.alg = COMP_ALGV2_UNCOMPRESSED; + options->comp.flags = COMP_F_ADVERTISE_STUBS_ONLY; + } +#if defined(ENABLE_LZO) + else if (streq (p[1], "lzo")) + { + options->comp.alg = COMP_ALG_LZO; + options->comp.flags = 0; + } +#endif +#if defined(ENABLE_LZ4) + else if (streq (p[1], "lz4")) + { + options->comp.alg = COMP_ALG_LZ4; + options->comp.flags = COMP_F_SWAP; + } + else if (streq (p[1], "lz4-v2")) + { + options->comp.alg = COMP_ALGV2_LZ4; + options->comp.flags = 0; + } +#endif + else + { + msg (msglevel, "bad comp option: %s", p[1]); + goto err; + } + } + else + { + options->comp.alg = COMP_ALG_STUB; + options->comp.flags = COMP_F_SWAP; + } } -#endif /* ENABLE_LZO */ +#endif /* USE_COMP */ #ifdef ENABLE_CRYPTO - else if (streq (p[0], "show-ciphers")) + else if (streq (p[0], "show-ciphers") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->show_ciphers = true; } - else if (streq (p[0], "show-digests")) + else if (streq (p[0], "show-digests") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->show_digests = true; } - else if (streq (p[0], "show-engines")) + else if (streq (p[0], "show-engines") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->show_engines = true; } - else if (streq (p[0], "key-direction") && p[1]) + else if (streq (p[0], "key-direction") && p[1] && !p[2]) { int key_direction; @@ -6407,7 +6693,7 @@ add_option (struct options *options, else goto err; } - else if (streq (p[0], "secret") && p[1]) + else if (streq (p[0], "secret") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], INLINE_FILE_TAG) && p[2]) @@ -6427,46 +6713,34 @@ add_option (struct options *options, } options->shared_secret_file = p[1]; } - else if (streq (p[0], "genkey")) + else if (streq (p[0], "genkey") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->genkey = true; } - else if (streq (p[0], "auth") && p[1]) + else if (streq (p[0], "auth") && p[1] && !p[2]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); - options->authname_defined = true; + VERIFY_PERMISSION (OPT_P_GENERAL); options->authname = p[1]; - if (streq (options->authname, "none")) - { - options->authname_defined = false; - options->authname = NULL; - } } - else if (streq (p[0], "auth")) + else if (streq (p[0], "cipher") && p[1] && !p[2]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); - options->authname_defined = true; + VERIFY_PERMISSION (OPT_P_NCP); + options->ciphername = p[1]; } - else if (streq (p[0], "cipher") && p[1]) + else if (streq (p[0], "ncp-ciphers") && p[1] && !p[2]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); - options->ciphername_defined = true; - options->ciphername = p[1]; - if (streq (options->ciphername, "none")) - { - options->ciphername_defined = false; - options->ciphername = NULL; - } + VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_INSTANCE); + options->ncp_ciphers = p[1]; } - else if (streq (p[0], "cipher")) + else if (streq (p[0], "ncp-disable") && !p[1]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); - options->ciphername_defined = true; + VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_INSTANCE); + options->ncp_enabled = false; } - else if (streq (p[0], "prng") && p[1]) + else if (streq (p[0], "prng") && p[1] && !p[3]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "none")) options->prng_hash = NULL; else @@ -6486,14 +6760,14 @@ add_option (struct options *options, } } } - else if (streq (p[0], "no-replay")) + else if (streq (p[0], "no-replay") && !p[1]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_GENERAL); options->replay = false; } - else if (streq (p[0], "replay-window")) + else if (streq (p[0], "replay-window") && !p[3]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_GENERAL); if (p[1]) { int replay_window; @@ -6531,28 +6805,28 @@ add_option (struct options *options, goto err; } } - else if (streq (p[0], "mute-replay-warnings")) + else if (streq (p[0], "mute-replay-warnings") && !p[1]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_GENERAL); options->mute_replay_warnings = true; } - else if (streq (p[0], "no-iv")) + else if (streq (p[0], "no-iv") && !p[1]) { - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_GENERAL); options->use_iv = false; } - else if (streq (p[0], "replay-persist") && p[1]) + else if (streq (p[0], "replay-persist") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->packet_id_file = p[1]; } - else if (streq (p[0], "test-crypto")) + else if (streq (p[0], "test-crypto") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->test_crypto = true; } -#ifndef ENABLE_CRYPTO_POLARSSL - else if (streq (p[0], "engine")) +#ifndef ENABLE_CRYPTO_MBEDTLS + else if (streq (p[0], "engine") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (p[1]) @@ -6562,13 +6836,13 @@ add_option (struct options *options, else options->engine = "auto"; } -#endif /* ENABLE_CRYPTO_POLARSSL */ +#endif /* ENABLE_CRYPTO_MBEDTLS */ #ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH - else if (streq (p[0], "keysize") && p[1]) + else if (streq (p[0], "keysize") && p[1] && !p[2]) { int keysize; - VERIFY_PERMISSION (OPT_P_CRYPTO); + VERIFY_PERMISSION (OPT_P_NCP); keysize = atoi (p[1]) / 8; if (keysize < 0 || keysize > MAX_CIPHER_KEY_LENGTH) { @@ -6579,29 +6853,38 @@ add_option (struct options *options, } #endif #ifdef ENABLE_PREDICTION_RESISTANCE - else if (streq (p[0], "use-prediction-resistance")) + else if (streq (p[0], "use-prediction-resistance") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->use_prediction_resistance = true; } #endif -#ifdef ENABLE_SSL - else if (streq (p[0], "show-tls")) + else if (streq (p[0], "show-tls") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->show_tls_ciphers = true; } - else if (streq (p[0], "tls-server")) + else if (streq (p[0], "show-curves") && !p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->show_curves = true; + } + else if (streq (p[0], "ecdh-curve") && p[1] && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->ecdh_curve= p[1]; + } + else if (streq (p[0], "tls-server") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->tls_server = true; } - else if (streq (p[0], "tls-client")) + else if (streq (p[0], "tls-client") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->tls_client = true; } - else if (streq (p[0], "ca") && p[1]) + else if (streq (p[0], "ca") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ca_file = p[1]; @@ -6610,14 +6893,14 @@ add_option (struct options *options, options->ca_file_inline = p[2]; } } -#ifndef ENABLE_CRYPTO_POLARSSL - else if (streq (p[0], "capath") && p[1]) +#ifndef ENABLE_CRYPTO_MBEDTLS + else if (streq (p[0], "capath") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->ca_path = p[1]; } -#endif /* ENABLE_CRYPTO_POLARSSL */ - else if (streq (p[0], "dh") && p[1]) +#endif /* ENABLE_CRYPTO_MBEDTLS */ + else if (streq (p[0], "dh") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->dh_file = p[1]; @@ -6626,7 +6909,7 @@ add_option (struct options *options, options->dh_file_inline = p[2]; } } - else if (streq (p[0], "cert") && p[1]) + else if (streq (p[0], "cert") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->cert_file = p[1]; @@ -6635,7 +6918,7 @@ add_option (struct options *options, options->cert_file_inline = p[2]; } } - else if (streq (p[0], "extra-certs") && p[1]) + else if (streq (p[0], "extra-certs") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->extra_certs_file = p[1]; @@ -6644,19 +6927,19 @@ add_option (struct options *options, options->extra_certs_file_inline = p[2]; } } - else if (streq (p[0], "verify-hash") && p[1]) + else if (streq (p[0], "verify-hash") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc); } #ifdef ENABLE_CRYPTOAPI - else if (streq (p[0], "cryptoapicert") && p[1]) + else if (streq (p[0], "cryptoapicert") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->cryptoapi_cert = p[1]; } #endif - else if (streq (p[0], "key") && p[1]) + else if (streq (p[0], "key") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->priv_key_file = p[1]; @@ -6665,7 +6948,7 @@ add_option (struct options *options, options->priv_key_file_inline = p[2]; } } - else if (streq (p[0], "tls-version-min") && p[1]) + else if (streq (p[0], "tls-version-min") && p[1] && !p[3]) { int ver; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -6679,7 +6962,7 @@ add_option (struct options *options, ~(SSLF_TLS_VERSION_MIN_MASK << SSLF_TLS_VERSION_MIN_SHIFT); options->ssl_flags |= (ver << SSLF_TLS_VERSION_MIN_SHIFT); } - else if (streq (p[0], "tls-version-max") && p[1]) + else if (streq (p[0], "tls-version-max") && p[1] && !p[2]) { int ver; VERIFY_PERMISSION (OPT_P_GENERAL); @@ -6693,8 +6976,8 @@ add_option (struct options *options, ~(SSLF_TLS_VERSION_MAX_MASK << SSLF_TLS_VERSION_MAX_SHIFT); options->ssl_flags |= (ver << SSLF_TLS_VERSION_MAX_SHIFT); } -#ifndef ENABLE_CRYPTO_POLARSSL - else if (streq (p[0], "pkcs12") && p[1]) +#ifndef ENABLE_CRYPTO_MBEDTLS + else if (streq (p[0], "pkcs12") && p[1] && ((streq (p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->pkcs12_file = p[1]; @@ -6703,8 +6986,8 @@ add_option (struct options *options, options->pkcs12_file_inline = p[2]; } } -#endif /* ENABLE_CRYPTO_POLARSSL */ - else if (streq (p[0], "askpass")) +#endif /* ENABLE_CRYPTO_MBEDTLS */ + else if (streq (p[0], "askpass") && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (p[1]) @@ -6714,12 +6997,12 @@ add_option (struct options *options, else options->key_pass_file = "stdin"; } - else if (streq (p[0], "auth-nocache")) + else if (streq (p[0], "auth-nocache") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); ssl_set_auth_nocache (); } - else if (streq (p[0], "auth-token") && p[1]) + else if (streq (p[0], "auth-token") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_ECHO); ssl_set_auth_token(p[1]); @@ -6728,34 +7011,39 @@ add_option (struct options *options, management_auth_token (management, p[1]); #endif } - else if (streq (p[0], "single-session")) + else if (streq (p[0], "single-session") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->single_session = true; } #ifdef ENABLE_PUSH_PEER_INFO - else if (streq (p[0], "push-peer-info")) + else if (streq (p[0], "push-peer-info") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->push_peer_info = true; } #endif - else if (streq (p[0], "tls-exit")) + else if (streq (p[0], "tls-exit") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->tls_exit = true; } - else if (streq (p[0], "tls-cipher") && p[1]) + else if (streq (p[0], "tls-cipher") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->cipher_list = p[1]; } - else if (streq (p[0], "crl-verify") && p[1]) + else if (streq (p[0], "crl-verify") && p[1] && ((p[2] && streq(p[2], "dir")) + || (p[2] && streq (p[1], INLINE_FILE_TAG) ) || !p[2]) && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (p[2] && streq(p[2], "dir")) options->ssl_flags |= SSLF_CRL_VERIFY_DIR; options->crl_file = p[1]; + if (streq (p[1], INLINE_FILE_TAG) && p[2]) + { + options->crl_file_inline = p[2]; + } } else if (streq (p[0], "tls-verify") && p[1]) { @@ -6766,85 +7054,48 @@ add_option (struct options *options, string_substitute (p[1], ',', ' ', &options->gc), "tls-verify", true); } -#ifndef ENABLE_CRYPTO_POLARSSL - else if (streq (p[0], "tls-export-cert") && p[1]) +#ifndef ENABLE_CRYPTO_MBEDTLS + else if (streq (p[0], "tls-export-cert") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->tls_export_cert = p[1]; } #endif - else if (streq (p[0], "compat-names")) +#if P2MP_SERVER + else if (streq (p[0], "compat-names") && ((p[1] && streq (p[1], "no-remapping")) || !p[1]) && !p[2]) +#else + else if (streq (p[0], "compat-names") && !p[1]) +#endif { VERIFY_PERMISSION (OPT_P_GENERAL); - if (options->verify_x509_type != VERIFY_X509_NONE && - options->verify_x509_type != TLS_REMOTE_SUBJECT_DN && - options->verify_x509_type != TLS_REMOTE_SUBJECT_RDN_PREFIX) + if (options->verify_x509_type != VERIFY_X509_NONE) { msg (msglevel, "you cannot use --compat-names with --verify-x509-name"); goto err; } - msg (M_WARN, "DEPRECATED OPTION: --compat-names, please update your configuration"); + msg (M_WARN, "DEPRECATED OPTION: --compat-names, please update your configuration. This will be removed in OpenVPN v2.5."); compat_flag (COMPAT_FLAG_SET | COMPAT_NAMES); #if P2MP_SERVER if (p[1] && streq (p[1], "no-remapping")) compat_flag (COMPAT_FLAG_SET | COMPAT_NO_NAME_REMAPPING); } - else if (streq (p[0], "no-name-remapping")) + else if (streq (p[0], "no-name-remapping") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); - if (options->verify_x509_type != VERIFY_X509_NONE && - options->verify_x509_type != TLS_REMOTE_SUBJECT_DN && - options->verify_x509_type != TLS_REMOTE_SUBJECT_RDN_PREFIX) + if (options->verify_x509_type != VERIFY_X509_NONE) { msg (msglevel, "you cannot use --no-name-remapping with --verify-x509-name"); goto err; } - msg (M_WARN, "DEPRECATED OPTION: --no-name-remapping, please update your configuration"); + msg (M_WARN, "DEPRECATED OPTION: --no-name-remapping, please update your configuration. This will be removed in OpenVPN v2.5."); compat_flag (COMPAT_FLAG_SET | COMPAT_NAMES); compat_flag (COMPAT_FLAG_SET | COMPAT_NO_NAME_REMAPPING); #endif } - else if (streq (p[0], "tls-remote") && p[1]) - { - VERIFY_PERMISSION (OPT_P_GENERAL); - - if (options->verify_x509_type != VERIFY_X509_NONE && - options->verify_x509_type != TLS_REMOTE_SUBJECT_DN && - options->verify_x509_type != TLS_REMOTE_SUBJECT_RDN_PREFIX) - { - msg (msglevel, "you cannot use --tls-remote with --verify-x509-name"); - goto err; - } - msg (M_WARN, "DEPRECATED OPTION: --tls-remote, please update your configuration"); - - if (strlen (p[1])) - { - int is_username = (!strchr (p[1], '=') || !strstr (p[1], ", ")); - int type = TLS_REMOTE_SUBJECT_DN; - if (p[1][0] != '/' && is_username) - type = TLS_REMOTE_SUBJECT_RDN_PREFIX; - - /* - * Enable legacy openvpn format for DNs that have not been converted - * yet and --x509-username-field (not containing an '=' or ', ') - */ - if (p[1][0] == '/' || is_username) - compat_flag (COMPAT_FLAG_SET | COMPAT_NAMES); - - options->verify_x509_type = type; - options->verify_x509_name = p[1]; - } - } - else if (streq (p[0], "verify-x509-name") && p[1] && strlen (p[1])) + else if (streq (p[0], "verify-x509-name") && p[1] && strlen (p[1]) && !p[3]) { int type = VERIFY_X509_SUBJECT_DN; VERIFY_PERMISSION (OPT_P_GENERAL); - if (options->verify_x509_type == TLS_REMOTE_SUBJECT_DN || - options->verify_x509_type == TLS_REMOTE_SUBJECT_RDN_PREFIX) - { - msg (msglevel, "you cannot use --verify-x509-name with --tls-remote"); - goto err; - } if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES)) { msg (msglevel, "you cannot use --verify-x509-name with " @@ -6868,7 +7119,7 @@ add_option (struct options *options, options->verify_x509_type = type; options->verify_x509_name = p[1]; } - else if (streq (p[0], "ns-cert-type") && p[1]) + else if (streq (p[0], "ns-cert-type") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], "server")) @@ -6881,7 +7132,6 @@ add_option (struct options *options, goto err; } } -#if OPENSSL_VERSION_NUMBER >= 0x00907000L || ENABLE_CRYPTO_POLARSSL else if (streq (p[0], "remote-cert-ku")) { int j; @@ -6891,12 +7141,12 @@ add_option (struct options *options, for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j) sscanf (p[j], "%x", &(options->remote_cert_ku[j-1])); } - else if (streq (p[0], "remote-cert-eku") && p[1]) + else if (streq (p[0], "remote-cert-eku") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->remote_cert_eku = p[1]; } - else if (streq (p[0], "remote-cert-tls") && p[1]) + else if (streq (p[0], "remote-cert-tls") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); @@ -6919,38 +7169,37 @@ add_option (struct options *options, goto err; } } -#endif /* OPENSSL_VERSION_NUMBER */ - else if (streq (p[0], "tls-timeout") && p[1]) + else if (streq (p[0], "tls-timeout") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->tls_timeout = positive_atoi (p[1]); } - else if (streq (p[0], "reneg-bytes") && p[1]) + else if (streq (p[0], "reneg-bytes") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->renegotiate_bytes = positive_atoi (p[1]); } - else if (streq (p[0], "reneg-pkts") && p[1]) + else if (streq (p[0], "reneg-pkts") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->renegotiate_packets = positive_atoi (p[1]); } - else if (streq (p[0], "reneg-sec") && p[1]) + else if (streq (p[0], "reneg-sec") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->renegotiate_seconds = positive_atoi (p[1]); } - else if (streq (p[0], "hand-window") && p[1]) + else if (streq (p[0], "hand-window") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->handshake_window = positive_atoi (p[1]); } - else if (streq (p[0], "tran-window") && p[1]) + else if (streq (p[0], "tran-window") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_TLS_PARMS); options->transition_window = positive_atoi (p[1]); } - else if (streq (p[0], "tls-auth") && p[1]) + else if (streq (p[0], "tls-auth") && p[1] && !p[3]) { VERIFY_PERMISSION (OPT_P_GENERAL); if (streq (p[1], INLINE_FILE_TAG) && p[2]) @@ -6970,7 +7219,16 @@ add_option (struct options *options, } options->tls_auth_file = p[1]; } - else if (streq (p[0], "key-method") && p[1]) + else if (streq (p[0], "tls-crypt") && p[1] && !p[3]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + if (streq (p[1], INLINE_FILE_TAG) && p[2]) + { + options->tls_crypt_inline = p[2]; + } + options->tls_crypt_file = p[1]; + } + else if (streq (p[0], "key-method") && p[1] && !p[2]) { int key_method; @@ -6986,8 +7244,13 @@ add_option (struct options *options, } options->key_method = key_method; } + else if (streq (p[0], "x509-track") && p[1] && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + x509_track_add (&options->x509_track, p[1], msglevel, &options->gc); + } #ifdef ENABLE_X509ALTUSERNAME - else if (streq (p[0], "x509-username-field") && p[1]) + else if (streq (p[0], "x509-username-field") && p[1] && !p[2]) { /* This option used to automatically upcase the fieldname passed as the * option argument, e.g., "ou" became "OU". Now, this "helpfulness" is @@ -7014,10 +7277,9 @@ add_option (struct options *options, options->x509_username_field = p[1]; } #endif /* ENABLE_X509ALTUSERNAME */ -#endif /* ENABLE_SSL */ #endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 - else if (streq (p[0], "show-pkcs11-ids")) + else if (streq (p[0], "show-pkcs11-ids") && !p[3]) { char *provider = p[1]; bool cert_private = (p[2] == NULL ? false : ( atoi (p[2]) != 0 )); @@ -7087,40 +7349,68 @@ add_option (struct options *options, for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j) options->pkcs11_cert_private[j-1] = atoi (p[j]) != 0 ? 1 : 0; } - else if (streq (p[0], "pkcs11-pin-cache") && p[1]) + else if (streq (p[0], "pkcs11-pin-cache") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->pkcs11_pin_cache_period = atoi (p[1]); } - else if (streq (p[0], "pkcs11-id") && p[1]) + else if (streq (p[0], "pkcs11-id") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->pkcs11_id = p[1]; } - else if (streq (p[0], "pkcs11-id-management")) + else if (streq (p[0], "pkcs11-id-management") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->pkcs11_id_management = true; } #endif - else if (streq (p[0], "rmtun")) + else if (streq (p[0], "rmtun") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->persist_config = true; options->persist_mode = 0; } - else if (streq (p[0], "mktun")) + else if (streq (p[0], "mktun") && !p[1]) { VERIFY_PERMISSION (OPT_P_GENERAL); options->persist_config = true; options->persist_mode = 1; } - else if (streq (p[0], "peer-id") && p[1]) + else if (streq (p[0], "peer-id") && p[1] && !p[2]) { VERIFY_PERMISSION (OPT_P_PEER_ID); options->use_peer_id = true; options->peer_id = atoi(p[1]); } +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 + else if (streq (p[0], "keying-material-exporter") && p[1] && p[2]) + { + int ekm_length = positive_atoi (p[2]); + + VERIFY_PERMISSION (OPT_P_GENERAL); + + if (strncmp(p[1], "EXPORTER", 8)) + { + msg (msglevel, "Keying material exporter label must begin with " + "\"EXPORTER\""); + goto err; + } + if (ekm_length < 16 || ekm_length > 4095) + { + msg (msglevel, "Invalid keying material exporter length"); + goto err; + } + + options->keying_material_exporter_label = p[1]; + options->keying_material_exporter_length = ekm_length; + } +#endif + else if (streq (p[0], "allow-recursive-routing") && !p[1]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->allow_recursive_routing = true; + } else { int i; @@ -7136,9 +7426,9 @@ add_option (struct options *options, } } if (file) - msg (msglevel, "Unrecognized option or missing parameter(s) in %s:%d: %s (%s)", file, line, p[0], PACKAGE_VERSION); + msg (msglevel, "Unrecognized option or missing or extra parameter(s) in %s:%d: %s (%s)", file, line, p[0], PACKAGE_VERSION); else - msg (msglevel, "Unrecognized option or missing parameter(s): --%s (%s)", p[0], PACKAGE_VERSION); + msg (msglevel, "Unrecognized option or missing or extra parameter(s): --%s (%s)", p[0], PACKAGE_VERSION); } err: gc_free (&gc); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 26b09ea..a028556 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -39,7 +39,7 @@ #include "plugin.h" #include "manage.h" #include "proxy.h" -#include "lzo.h" +#include "comp.h" #include "pushlist.h" #include "clinat.h" @@ -71,44 +71,37 @@ struct options_pre_pull bool routes_ipv6_defined; struct route_ipv6_option_list *routes_ipv6; -#ifdef ENABLE_CLIENT_NAT bool client_nat_defined; struct client_nat_option_list *client_nat; -#endif int foreign_option_index; }; #endif -#if defined(ENABLE_CRYPTO) && !defined(ENABLE_CRYPTO_OPENSSL) && !defined(ENABLE_CRYPTO_POLARSSL) -# error "At least one of OpenSSL or PolarSSL needs to be defined." +#if defined(ENABLE_CRYPTO) && !defined(ENABLE_CRYPTO_OPENSSL) && !defined(ENABLE_CRYPTO_MBEDTLS) +# error "At least one of OpenSSL or mbed TLS needs to be defined." #endif struct connection_entry { int proto; - int local_port; + sa_family_t af; + const char* local_port; bool local_port_defined; - int remote_port; + const char *remote_port; const char *local; const char *remote; bool remote_float; bool bind_defined; + bool bind_ipv6_only; bool bind_local; int connect_retry_seconds; - bool connect_retry_defined; - int connect_retry_max; + int connect_retry_seconds_max; int connect_timeout; - bool connect_timeout_defined; -#ifdef ENABLE_HTTP_PROXY struct http_proxy_options *http_proxy_options; -#endif -#ifdef ENABLE_SOCKS const char *socks_proxy_server; - int socks_proxy_port; + const char *socks_proxy_port; const char *socks_proxy_authfile; - bool socks_proxy_retry; -#endif int tun_mtu; /* MTU of tun device */ bool tun_mtu_defined; /* true if user overriding parm with command line option */ @@ -124,9 +117,7 @@ struct connection_entry int mssfix; /* Upper bound on TCP MSS */ bool mssfix_default; /* true if --mssfix was supplied without a parameter */ -#ifdef ENABLE_OCC - int explicit_exit_notification; /* Explicitly tell peer when we are exiting via OCC_EXIT message */ -#endif + int explicit_exit_notification; /* Explicitly tell peer when we are exiting via OCC_EXIT or [RESTART] message */ # define CE_DISABLED (1<<0) # define CE_MAN_QUERY_PROXY (1<<1) @@ -143,8 +134,9 @@ struct connection_entry struct remote_entry { const char *remote; - int remote_port; + const char *remote_port; int proto; + sa_family_t af; }; #define CONNECTION_LIST_SIZE 64 @@ -153,8 +145,6 @@ struct connection_list { int len; int current; - int n_cycles; - bool no_advance; struct connection_entry *array[CONNECTION_LIST_SIZE]; }; @@ -168,6 +158,8 @@ struct remote_host_store { # define RH_HOST_LEN 80 char host[RH_HOST_LEN]; +#define RH_PORT_LEN 20 + char port[RH_PORT_LEN]; }; /* Command line options */ @@ -198,20 +190,23 @@ struct options bool show_ciphers; bool show_digests; bool show_engines; -#ifdef ENABLE_SSL bool show_tls_ciphers; -#endif + bool show_curves; bool genkey; #endif /* Networking parms */ + int connect_retry_max; struct connection_entry ce; - char *remote_ip_hint; struct connection_list *connection_list; + struct remote_list *remote_list; - bool force_connection_list; + /* Do not advanced the connection or remote addr list*/ + bool no_advance; + /* Counts the number of unsuccessful connection attempts */ + unsigned int unsuccessful_attempts; -#if HTTP_PROXY_OVERRIDE +#if ENABLE_MANAGEMENT struct http_proxy_options *http_proxy_override; #endif @@ -256,7 +251,6 @@ struct options int ping_send_timeout; /* Send a TCP/UDP ping to remote every n seconds */ int ping_rec_timeout; /* Expect a TCP/UDP ping from remote at least once every n seconds */ bool ping_timer_remote; /* Run ping timer only if we have a remote address */ - bool tun_ipv6; /* Build tun dev that supports IPv6 */ # define PING_UNDEF 0 # define PING_EXIT 1 @@ -273,6 +267,8 @@ struct options #endif int resolve_retry_seconds; /* If hostname resolve fails, retry for n seconds */ + bool resolve_in_advance; + const char *ip_remote_hint; struct tuntap_options tuntap_options; @@ -300,6 +296,7 @@ struct options bool log; bool suppress_timestamps; + bool machine_readable_output; int nice; int verbosity; int mute; @@ -315,9 +312,8 @@ struct options /* optimize TUN/TAP/UDP writes */ bool fast_io; -#ifdef ENABLE_LZO - /* LZO_x flags from lzo.h */ - unsigned int lzo; +#ifdef USE_COMP + struct compress_options comp; #endif /* buffer sizes */ @@ -339,16 +335,12 @@ struct options int route_delay; int route_delay_window; bool route_delay_defined; - int max_routes; struct route_option_list *routes; struct route_ipv6_option_list *routes_ipv6; /* IPv6 */ bool route_nopull; 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 */ @@ -357,7 +349,7 @@ struct options #ifdef ENABLE_MANAGEMENT const char *management_addr; - int management_port; + const char *management_port; const char *management_user_pass; int management_log_history_cache; int management_echo_buffer_size; @@ -369,6 +361,7 @@ struct options /* Mask of MF_ values of manage.h */ unsigned int management_flags; + const char *management_certificate; #endif #ifdef ENABLE_PLUGIN @@ -429,9 +422,7 @@ 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; @@ -439,6 +430,7 @@ struct options struct in6_addr push_ifconfig_ipv6_local; /* IPv6 */ int push_ifconfig_ipv6_netbits; /* IPv6 */ struct in6_addr push_ifconfig_ipv6_remote; /* IPv6 */ + bool push_ifconfig_ipv6_blocked; /* IPv6 */ bool enable_c2c; bool duplicate_cn; int cf_max; @@ -450,9 +442,11 @@ struct options const char *auth_user_pass_verify_script; bool auth_user_pass_verify_script_via_file; + bool auth_token_generate; + unsigned int auth_token_lifetime; #if PORT_SHARE char *port_share_host; - int port_share_port; + char *port_share_port; const char *port_share_journal_dir; #endif #endif @@ -464,8 +458,6 @@ struct options const char *auth_user_pass_file; struct options_pre_pull *pre_pull; - int server_poll_timeout; - int scheduled_exit_interval; #ifdef ENABLE_CLIENT_CR @@ -478,9 +470,9 @@ struct options const char *shared_secret_file; const char *shared_secret_file_inline; int key_direction; - bool ciphername_defined; const char *ciphername; - bool authname_defined; + bool ncp_enabled; + const char *ncp_ciphers; const char *authname; int keysize; const char *prng_hash; @@ -497,7 +489,6 @@ struct options bool use_prediction_resistance; #endif -#ifdef ENABLE_SSL /* TLS (control channel) parms */ bool tls_server; bool tls_client; @@ -509,6 +500,7 @@ struct options const char *priv_key_file; const char *pkcs12_file; const char *cipher_list; + const char *ecdh_curve; const char *tls_verify; int verify_x509_type; const char *verify_x509_name; @@ -518,6 +510,7 @@ struct options const char *ca_file_inline; const char *cert_file_inline; const char *extra_certs_file_inline; + const char *crl_file_inline; char *priv_key_file_inline; const char *dh_file_inline; const char *pkcs12_file_inline; /* contains the base64 encoding of pkcs12 file */ @@ -565,10 +558,14 @@ struct options /* Old key allowed to live n seconds after new key goes active */ int transition_window; - /* Special authentication MAC for TLS control channel */ - const char *tls_auth_file; /* shared secret */ + /* Shared secret used for TLS control channel authentication */ + const char *tls_auth_file; const char *tls_auth_file_inline; + /* Shared secret used for TLS control channel authenticated encryption */ + const char *tls_crypt_file; + const char *tls_crypt_inline; + /* Allow only one session */ bool single_session; @@ -578,17 +575,15 @@ struct options bool tls_exit; -#endif /* ENABLE_SSL */ #endif /* ENABLE_CRYPTO */ -#ifdef ENABLE_X509_TRACK const struct x509_track *x509_track; -#endif /* special state parms */ int foreign_option_index; -#ifdef WIN32 +#ifdef _WIN32 + HANDLE msg_channel; const char *exit_event_name; bool exit_event_initial_state; bool show_net_up; @@ -598,6 +593,18 @@ struct options bool use_peer_id; uint32_t peer_id; + +#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 + /* Keying Material Exporters [RFC 5705] */ + const char *keying_material_exporter_label; + int keying_material_exporter_length; +#endif + + struct pull_filter_list *pull_filter_list; + + /* Useful when packets sent by openvpn itself are not subject + to the routing tables that would move packets into the tunnel. */ + bool allow_recursive_routing; }; #define streq(x, y) (!strcmp((x), (y))) @@ -617,7 +624,7 @@ struct options #define OPT_P_PERSIST_IP (1<<9) #define OPT_P_COMP (1<<10) /* TODO */ #define OPT_P_MESSAGES (1<<11) -#define OPT_P_CRYPTO (1<<12) /* TODO */ +#define OPT_P_NCP (1<<12) /**< Negotiable crypto parameters */ #define OPT_P_TLS_PARMS (1<<13) /* TODO */ #define OPT_P_MTU (1<<14) /* TODO */ #define OPT_P_NICE (1<<15) @@ -652,7 +659,7 @@ struct options #define PUSH_DEFINED(opt) (false) #endif -#ifdef WIN32 +#ifdef _WIN32 #define ROUTE_OPTION_FLAGS(o) ((o)->route_method & ROUTE_METHOD_MASK) #else #define ROUTE_OPTION_FLAGS(o) (0) @@ -690,7 +697,7 @@ void usage_small (void); void show_library_versions(const unsigned int flags); -#ifdef WIN32 +#ifdef _WIN32 void show_windows_version(const unsigned int flags); #endif @@ -722,7 +729,7 @@ void options_warning (char *actual, const char *expected); void options_postprocess (struct options *options); void pre_pull_save (struct options *o); -void pre_pull_restore (struct options *o); +void pre_pull_restore (struct options *o, struct gc_arena *gc); bool apply_push_options (struct options *options, struct buffer *buf, @@ -784,20 +791,5 @@ void options_string_import (struct options *options, bool get_ipv6_addr( const char * prefix_str, struct in6_addr *network, unsigned int * netbits, int msglevel ); -/* - * inline functions - */ -static inline bool -connection_list_defined (const struct options *o) -{ - return o->connection_list != NULL; -} - -static inline void -connection_list_set_no_advance (struct options *o) -{ - if (o->connection_list) - o->connection_list->no_advance = true; -} #endif diff --git a/src/openvpn/otime.h b/src/openvpn/otime.h index 4ca1032..c0b0f38 100644 --- a/src/openvpn/otime.h +++ b/src/openvpn/otime.h @@ -76,8 +76,8 @@ openvpn_gettimeofday (struct timeval *tv, void *tz) static inline void update_time (void) { -#ifdef WIN32 - /* on WIN32, gettimeofday is faster than time(NULL) */ +#ifdef _WIN32 + /* on _WIN32, gettimeofday is faster than time(NULL) */ struct timeval tv; openvpn_gettimeofday (&tv, NULL); #else @@ -90,8 +90,8 @@ update_time (void) static inline void update_time (void) { -#if defined(WIN32) - /* on WIN32, gettimeofday is faster than time(NULL) */ +#if defined(_WIN32) + /* on _WIN32, gettimeofday is faster than time(NULL) */ struct timeval tv; if (!gettimeofday (&tv, NULL)) { diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c index baa4966..9874519 100644 --- a/src/openvpn/packet_id.c +++ b/src/openvpn/packet_id.c @@ -76,10 +76,9 @@ packet_id_debug (int msglevel, } void -packet_id_init (struct packet_id *p, bool tcp_mode, int seq_backtrack, int time_backtrack, const char *name, int unit) +packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack, const char *name, int unit) { - dmsg (D_PID_DEBUG, "PID packet_id_init tcp_mode=%d seq_backtrack=%d time_backtrack=%d", - tcp_mode, + dmsg (D_PID_DEBUG, "PID packet_id_init seq_backtrack=%d time_backtrack=%d", seq_backtrack, time_backtrack); @@ -88,7 +87,7 @@ packet_id_init (struct packet_id *p, bool tcp_mode, int seq_backtrack, int time_ p->rec.name = name; p->rec.unit = unit; - if (seq_backtrack && !tcp_mode) + if (seq_backtrack) { ASSERT (MIN_SEQ_BACKTRACK <= seq_backtrack && seq_backtrack <= MAX_SEQ_BACKTRACK); ASSERT (MIN_TIME_BACKTRACK <= time_backtrack && time_backtrack <= MAX_TIME_BACKTRACK); diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h index 3ddaab6..fb059b7 100644 --- a/src/openvpn/packet_id.h +++ b/src/openvpn/packet_id.h @@ -210,7 +210,7 @@ struct packet_id struct packet_id_rec rec; }; -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_init (struct packet_id *p, 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 ? */ @@ -258,6 +258,12 @@ bool packet_id_write (const struct packet_id_net *pin, struct buffer *buf, bool * Inline functions. */ +/** Is this struct packet_id initialized? */ +static inline bool packet_id_initialized (const struct packet_id *pid) +{ + return pid->rec.initialized; +} + /* are we in enabled state? */ static inline bool packet_id_persist_enabled (const struct packet_id_persist *p) diff --git a/src/openvpn/pf.c b/src/openvpn/pf.c index 461beed..a3208db 100644 --- a/src/openvpn/pf.c +++ b/src/openvpn/pf.c @@ -35,8 +35,8 @@ #if defined(ENABLE_PF) #include "init.h" - #include "memdbg.h" +#include "ssl_verify.h" #include "pf-inline.h" diff --git a/src/openvpn/pkcs11.c b/src/openvpn/pkcs11.c index a1f13c5..2621058 100644 --- a/src/openvpn/pkcs11.c +++ b/src/openvpn/pkcs11.c @@ -744,9 +744,10 @@ _pkcs11_openvpn_show_pkcs11_ids_pin_prompt ( ASSERT (token!=NULL); buf_printf (&pass_prompt, "Please enter '%s' token PIN or 'cancel': ", token->display); - - if (!get_console_input (BSTR (&pass_prompt), false, pin, pin_max)) { - msg (M_FATAL, "Cannot read password from stdin"); + if (!query_user_SINGLE(BSTR(&pass_prompt), BLEN(&pass_prompt), + pin, pin_max, false)) + { + msg (M_FATAL, "Could not retrieve the PIN"); } gc_free (&gc); diff --git a/src/openvpn/pkcs11_mbedtls.c b/src/openvpn/pkcs11_mbedtls.c new file mode 100644 index 0000000..e208b61 --- /dev/null +++ b/src/openvpn/pkcs11_mbedtls.c @@ -0,0 +1,129 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file PKCS #11 mbed TLS backend + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_PKCS11) && defined(ENABLE_CRYPTO_MBEDTLS) + +#include "errlevel.h" +#include "pkcs11_backend.h" +#include +#include + +int +pkcs11_init_tls_session(pkcs11h_certificate_t certificate, + struct tls_root_ctx * const ssl_ctx) +{ + int ret = 1; + + ASSERT (NULL != ssl_ctx); + + ALLOC_OBJ_CLEAR (ssl_ctx->crt_chain, mbedtls_x509_crt); + if (mbedtls_pkcs11_x509_cert_bind(ssl_ctx->crt_chain, certificate)) { + msg (M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + goto cleanup; + } + + ALLOC_OBJ_CLEAR (ssl_ctx->priv_key_pkcs11, mbedtls_pkcs11_context); + if (mbedtls_pkcs11_priv_key_bind(ssl_ctx->priv_key_pkcs11, certificate)) { + msg (M_FATAL, "PKCS#11: Cannot initialize mbed TLS private key object"); + goto cleanup; + } + + ALLOC_OBJ_CLEAR (ssl_ctx->priv_key, mbedtls_pk_context); + if (!mbed_ok(mbedtls_pk_setup_rsa_alt(ssl_ctx->priv_key, + ssl_ctx->priv_key_pkcs11, mbedtls_ssl_pkcs11_decrypt, + mbedtls_ssl_pkcs11_sign, mbedtls_ssl_pkcs11_key_len))) { + goto cleanup; + } + + ret = 0; + +cleanup: + return ret; +} + +char * +pkcs11_certificate_dn (pkcs11h_certificate_t cert, struct gc_arena *gc) +{ + char *ret = NULL; + char dn[1024] = {0}; + + mbedtls_x509_crt mbed_crt = {0}; + + if (mbedtls_pkcs11_x509_cert_bind(&mbed_crt, cert)) { + msg (M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + goto cleanup; + } + + if (-1 == mbedtls_x509_dn_gets (dn, sizeof(dn), &mbed_crt.subject)) { + msg (M_FATAL, "PKCS#11: mbed TLS cannot parse subject"); + goto cleanup; + } + + ret = string_alloc(dn, gc); + +cleanup: + mbedtls_x509_crt_free(&mbed_crt); + + return ret; +} + +int +pkcs11_certificate_serial (pkcs11h_certificate_t cert, char *serial, + size_t serial_len) +{ + int ret = 1; + + mbedtls_x509_crt mbed_crt = {0}; + + if (mbedtls_pkcs11_x509_cert_bind(&mbed_crt, cert)) { + msg (M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + goto cleanup; + } + + if (-1 == mbedtls_x509_serial_gets (serial, serial_len, &mbed_crt.serial)) { + msg (M_FATAL, "PKCS#11: mbed TLS cannot parse serial"); + goto cleanup; + } + + ret = 0; + +cleanup: + mbedtls_x509_crt_free(&mbed_crt); + + return ret; +} +#endif /* defined(ENABLE_PKCS11) && defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/pkcs11_polarssl.c b/src/openvpn/pkcs11_polarssl.c deleted file mode 100644 index a58beef..0000000 --- a/src/openvpn/pkcs11_polarssl.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file PKCS #11 PolarSSL backend - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_PKCS11) && defined(ENABLE_CRYPTO_POLARSSL) - -#include "errlevel.h" -#include "pkcs11_backend.h" -#include -#include - -int -pkcs11_init_tls_session(pkcs11h_certificate_t certificate, - struct tls_root_ctx * const ssl_ctx) -{ - int ret = 1; - - ASSERT (NULL != ssl_ctx); - - ALLOC_OBJ_CLEAR (ssl_ctx->crt_chain, x509_crt); - if (pkcs11_x509_cert_init(ssl_ctx->crt_chain, certificate)) { - msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); - goto cleanup; - } - - ALLOC_OBJ_CLEAR (ssl_ctx->priv_key_pkcs11, pkcs11_context); - if (pkcs11_priv_key_init(ssl_ctx->priv_key_pkcs11, certificate)) { - msg (M_FATAL, "PKCS#11: Cannot initialize PolarSSL private key object"); - goto cleanup; - } - - ALLOC_OBJ_CLEAR (ssl_ctx->priv_key, pk_context); - if (0 != pk_init_ctx_rsa_alt(ssl_ctx->priv_key, ssl_ctx->priv_key_pkcs11, - ssl_pkcs11_decrypt, ssl_pkcs11_sign, ssl_pkcs11_key_len)) { - goto cleanup; - } - - ret = 0; - -cleanup: - return ret; -} - -char * -pkcs11_certificate_dn (pkcs11h_certificate_t cert, struct gc_arena *gc) -{ - char *ret = NULL; - char dn[1024] = {0}; - - x509_crt polar_cert = {0}; - - if (pkcs11_x509_cert_init(&polar_cert, cert)) { - msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); - goto cleanup; - } - - if (-1 == x509_dn_gets (dn, sizeof(dn), &polar_cert.subject)) { - msg (M_FATAL, "PKCS#11: PolarSSL cannot parse subject"); - goto cleanup; - } - - ret = string_alloc(dn, gc); - -cleanup: - x509_crt_free(&polar_cert); - - return ret; -} - -int -pkcs11_certificate_serial (pkcs11h_certificate_t cert, char *serial, - size_t serial_len) -{ - int ret = 1; - - x509_crt polar_cert = {0}; - - if (pkcs11_x509_cert_init(&polar_cert, cert)) { - msg (M_FATAL, "PKCS#11: Cannot retrieve PolarSSL certificate object"); - goto cleanup; - } - - if (-1 == x509_serial_gets (serial, serial_len, &polar_cert.serial)) { - msg (M_FATAL, "PKCS#11: PolarSSL cannot parse serial"); - goto cleanup; - } - - ret = 0; - -cleanup: - x509_crt_free(&polar_cert); - - return ret; -} -#endif /* defined(ENABLE_PKCS11) && defined(ENABLE_CRYPTO_POLARSSL) */ diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c index 16d4dac..6343647 100644 --- a/src/openvpn/platform.c +++ b/src/openvpn/platform.c @@ -158,7 +158,7 @@ platform_nice (int niceval) unsigned int platform_getpid () { -#ifdef WIN32 +#ifdef _WIN32 return (unsigned int) GetCurrentProcessId (); #else #ifdef HAVE_GETPID @@ -190,7 +190,7 @@ int platform_chdir (const char* dir) { #ifdef HAVE_CHDIR -#ifdef WIN32 +#ifdef _WIN32 int res; struct gc_arena gc = gc_new (); res = _wchdir (wide_string (dir, &gc)); @@ -210,7 +210,7 @@ platform_chdir (const char* dir) bool platform_system_ok (int stat) { -#ifdef WIN32 +#ifdef _WIN32 return stat == 0; #else return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0; @@ -220,7 +220,7 @@ platform_system_ok (int stat) int platform_access (const char *path, int mode) { -#ifdef WIN32 +#ifdef _WIN32 struct gc_arena gc = gc_new (); int ret = _waccess (wide_string (path, &gc), mode & ~X_OK); gc_free (&gc); @@ -236,7 +236,7 @@ platform_access (const char *path, int mode) void platform_sleep_milliseconds (unsigned int n) { -#ifdef WIN32 +#ifdef _WIN32 Sleep (n); #else struct timeval tv; @@ -252,7 +252,7 @@ platform_sleep_milliseconds (unsigned int n) void platform_sleep_until_signal (void) { -#ifdef WIN32 +#ifdef _WIN32 ASSERT (0); #else select (0, NULL, NULL, NULL, NULL); @@ -263,7 +263,7 @@ platform_sleep_until_signal (void) bool platform_unlink (const char *filename) { -#if defined(WIN32) +#if defined(_WIN32) struct gc_arena gc = gc_new (); BOOL ret = DeleteFileW (wide_string (filename, &gc)); gc_free (&gc); @@ -278,7 +278,7 @@ platform_unlink (const char *filename) FILE * platform_fopen (const char *path, const char *mode) { -#ifdef WIN32 +#ifdef _WIN32 struct gc_arena gc = gc_new (); FILE *f = _wfopen (wide_string (path, &gc), wide_string (mode, &gc)); gc_free (&gc); @@ -291,7 +291,7 @@ platform_fopen (const char *path, const char *mode) int platform_open (const char *path, int flags, int mode) { -#ifdef WIN32 +#ifdef _WIN32 struct gc_arena gc = gc_new (); int fd = _wopen (wide_string (path, &gc), flags, mode); gc_free (&gc); @@ -304,7 +304,7 @@ platform_open (const char *path, int flags, int mode) int platform_stat (const char *path, platform_stat_t *buf) { -#ifdef WIN32 +#ifdef _WIN32 struct gc_arena gc = gc_new (); int res = _wstat (wide_string (path, &gc), buf); gc_free (&gc); diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h index 7c0a4d7..fe2685a 100644 --- a/src/openvpn/platform.h +++ b/src/openvpn/platform.h @@ -130,7 +130,7 @@ int platform_putenv (char *string); FILE *platform_fopen (const char *path, const char *mode); int platform_open (const char *path, int flags, int mode); -#ifdef WIN32 +#ifdef _WIN32 typedef struct _stat platform_stat_t; #else typedef struct stat platform_stat_t; diff --git a/src/openvpn/plugin.c b/src/openvpn/plugin.c index 4e5e6ce..2443438 100644 --- a/src/openvpn/plugin.c +++ b/src/openvpn/plugin.c @@ -27,6 +27,9 @@ #elif defined(_MSC_VER) #include "config-msvc.h" #endif +#ifdef HAVE_CONFIG_VERSION_H +#include "config-version.h" +#endif #include "syshead.h" @@ -172,7 +175,7 @@ plugin_option_list_print (const struct plugin_option_list *list, int msglevel) } #endif -#ifndef WIN32 +#ifndef _WIN32 static void libdl_resolve_symbol (void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags) @@ -203,7 +206,7 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o) p->so_pathname = o->so_pathname; p->plugin_type_mask = plugin_supported_types (); -#ifndef WIN32 +#ifndef _WIN32 p->handle = NULL; #if defined(PLUGIN_LIBDIR) @@ -347,6 +350,17 @@ static struct openvpn_plugin_callbacks callbacks = { plugin_vlog }; + +/* Provide a wrapper macro for a version patch level string to plug-ins. + * This is located here purely to not make the code too messy with #ifndef + * inside a struct declaration + */ +#ifndef CONFIGURE_GIT_REVISION +# define _OPENVPN_PATCH_LEVEL OPENVPN_VERSION_PATCH +#else +# define _OPENVPN_PATCH_LEVEL "git:" CONFIGURE_GIT_REVISION CONFIGURE_GIT_FLAGS +#endif + static void plugin_open_item (struct plugin *p, const struct plugin_option *o, @@ -375,7 +389,12 @@ plugin_open_item (struct plugin *p, (const char ** const) o->argv, (const char ** const) envp, &callbacks, - SSLAPI }; + SSLAPI, + PACKAGE_VERSION, + OPENVPN_VERSION_MAJOR, + OPENVPN_VERSION_MINOR, + _OPENVPN_PATCH_LEVEL + }; struct openvpn_plugin_args_open_return retargs; CLEAR(retargs); @@ -420,7 +439,7 @@ plugin_call_item (const struct plugin *p, const struct argv *av, struct openvpn_plugin_string_list **retlist, const char **envp -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO , int certdepth, openvpn_x509_cert_t *current_cert #endif @@ -449,7 +468,7 @@ plugin_call_item (const struct plugin *p, (const char ** const) envp, p->plugin_handle, per_client_context, -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO (current_cert ? certdepth : -1), current_cert #else @@ -500,10 +519,10 @@ plugin_close_item (struct plugin *p) if (p->plugin_handle) (*p->close)(p->plugin_handle); -#ifndef WIN32 +#ifndef _WIN32 if (dlclose (p->handle)) msg (M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname); -#elif defined(WIN32) +#elif defined(_WIN32) if (!FreeLibrary (p->module)) msg (M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname); #endif @@ -659,7 +678,7 @@ plugin_call_ssl (const struct plugin_list *pl, const struct argv *av, struct plugin_return *pr, struct env_set *es -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO , int certdepth, openvpn_x509_cert_t *current_cert #endif @@ -689,7 +708,7 @@ plugin_call_ssl (const struct plugin_list *pl, av, pr ? &pr->list[i] : NULL, envp -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO ,certdepth, current_cert #endif diff --git a/src/openvpn/plugin.h b/src/openvpn/plugin.h index 2f8416b..b1e0458 100644 --- a/src/openvpn/plugin.h +++ b/src/openvpn/plugin.h @@ -32,8 +32,8 @@ #ifdef ENABLE_CRYPTO_OPENSSL #include "ssl_verify_openssl.h" #endif -#ifdef ENABLE_CRYPTO_POLARSSL -#include "ssl_verify_polarssl.h" +#ifdef ENABLE_CRYPTO_MBEDTLS +#include "ssl_verify_mbedtls.h" #endif #include "openvpn-plugin.h" @@ -59,7 +59,7 @@ struct plugin { unsigned int plugin_type_mask; int requested_initialization_point; -#ifndef WIN32 +#ifndef _WIN32 void *handle; #else HMODULE module; @@ -127,7 +127,7 @@ int plugin_call_ssl (const struct plugin_list *pl, const struct argv *av, struct plugin_return *pr, struct env_set *es -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO , int current_cert_depth, openvpn_x509_cert_t *current_cert #endif @@ -183,7 +183,7 @@ plugin_call_ssl (const struct plugin_list *pl, const struct argv *av, struct plugin_return *pr, struct env_set *es -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO , int current_cert_depth, openvpn_x509_cert_t *current_cert #endif @@ -202,7 +202,7 @@ plugin_call(const struct plugin_list *pl, struct env_set *es) { return plugin_call_ssl(pl, type, av, pr, es -#ifdef ENABLE_SSL +#ifdef ENABLE_CRYPTO , -1, NULL #endif ); diff --git a/src/openvpn/proto.h b/src/openvpn/proto.h index f91e787..07612c8 100644 --- a/src/openvpn/proto.h +++ b/src/openvpn/proto.h @@ -218,6 +218,45 @@ struct ip_tcp_udp_hdr { #define MTU_TO_MSS(mtu) (mtu - sizeof(struct openvpn_iphdr) \ - sizeof(struct openvpn_tcphdr)) +/* + * This returns an ip protocol version of packet inside tun + * and offset of IP header (via parameter). + */ +inline static int get_tun_ip_ver(int tunnel_type, struct buffer *buf, int *ip_hdr_offset) +{ + int ip_ver = -1; + + /* for tun get ip version from ip header */ + if (tunnel_type == DEV_TYPE_TUN) + { + *ip_hdr_offset = 0; + if (likely(BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))) + { + ip_ver = OPENVPN_IPH_GET_VER (*BPTR(buf)); + } + } + else if (tunnel_type == DEV_TYPE_TAP) + { + *ip_hdr_offset = (int)(sizeof (struct openvpn_ethhdr)); + /* for tap get ip version from eth header */ + if (likely(BLEN (buf) >= *ip_hdr_offset)) + { + const struct openvpn_ethhdr *eh = (const struct openvpn_ethhdr *) BPTR (buf); + uint16_t proto = ntohs (eh->proto); + if (proto == OPENVPN_ETH_P_IPV6) + { + ip_ver = 6; + } + else if (proto == OPENVPN_ETH_P_IPV4) + { + ip_ver = 4; + } + } + } + + return ip_ver; +} + /* * If raw tunnel packet is IPv4 or IPv6, return true and increment * buffer offset to start of IP header. diff --git a/src/openvpn/proxy.c b/src/openvpn/proxy.c index 89989d1..0f78020 100644 --- a/src/openvpn/proxy.c +++ b/src/openvpn/proxy.c @@ -41,8 +41,7 @@ #include "httpdigest.h" #include "ntlm.h" #include "memdbg.h" - -#ifdef ENABLE_HTTP_PROXY +#include "forward.h" #define UP_TYPE_PROXY "HTTP Proxy" @@ -54,7 +53,6 @@ init_http_proxy_options_once (struct http_proxy_options **hpo, { ALLOC_OBJ_CLEAR_GC (*hpo, struct http_proxy_options, gc); /* http proxy defaults */ - (*hpo)->timeout = 5; (*hpo)->http_version = "1.0"; } return *hpo; @@ -243,6 +241,8 @@ get_user_pass_http (struct http_proxy_info *p, const bool force) unsigned int flags = GET_USER_PASS_MANAGEMENT; if (p->queried_creds) flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED; + if (p->options.inline_creds) + flags |= GET_USER_PASS_INLINE_CREDS; get_user_pass (&static_proxy_user_pass, p->options.auth_file, UP_TYPE_PROXY, @@ -257,6 +257,8 @@ clear_user_pass_http (void) purge_user_pass (&static_proxy_user_pass, true); } +#if 0 +/* function only used in #if 0 debug statement */ static void dump_residual (socket_descriptor_t sd, int timeout, @@ -271,6 +273,7 @@ dump_residual (socket_descriptor_t sd, msg (D_PROXY, "PROXY HEADER: '%s'", buf); } } +#endif /* * Extract the Proxy-Authenticate header from the stream. @@ -439,12 +442,11 @@ struct http_proxy_info * http_proxy_new (const struct http_proxy_options *o) { struct http_proxy_info *p; - struct http_proxy_options opt; if (!o || !o->server) msg (M_FATAL, "HTTP_PROXY: server not specified"); - ASSERT (legal_ipv4_port (o->port)); + ASSERT ( o->port); ALLOC_OBJ_CLEAR (p, struct http_proxy_info); p->options = *o; @@ -489,11 +491,73 @@ http_proxy_close (struct http_proxy_info *hp) free (hp); } +bool +add_proxy_headers (struct http_proxy_info *p, + socket_descriptor_t sd, /* already open to proxy */ + const char *host, /* openvpn server remote */ + const char* port /* openvpn server port */ + ) +{ + char buf[512]; + int i; + bool host_header_sent=false; + + /* + * Send custom headers if provided + * If content is NULL the whole header is in name + * Also remember if we already sent a Host: header + */ + for (i=0; i < MAX_CUSTOM_HTTP_HEADER && p->options.custom_headers[i].name;i++) + { + if (p->options.custom_headers[i].content) + { + openvpn_snprintf (buf, sizeof(buf), "%s: %s", + p->options.custom_headers[i].name, + p->options.custom_headers[i].content); + if (!strcasecmp(p->options.custom_headers[i].name, "Host")) + host_header_sent=true; + } + else + { + openvpn_snprintf (buf, sizeof(buf), "%s", + p->options.custom_headers[i].name); + if (!strncasecmp(p->options.custom_headers[i].name, "Host:", 5)) + host_header_sent=true; + } + + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + return false; + } + + if (!host_header_sent) + { + openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf(sd, buf)) + return false; + } + + /* send User-Agent string if provided */ + if (p->options.user_agent) + { + openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", + p->options.user_agent); + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + return false; + } + + return true; +} + + bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ - const int port, /* openvpn server port */ + const char *port, /* openvpn server port */ + struct event_timeout* server_poll_timeout, struct buffer *lookahead, volatile int *signal_received) { @@ -521,7 +585,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, else { /* format HTTP CONNECT message */ - openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", + openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%s HTTP/%s", host, port, p->options.http_version); @@ -532,18 +596,8 @@ establish_http_proxy_passthru (struct http_proxy_info *p, if (!send_line_crlf (sd, buf)) goto error; - openvpn_snprintf(buf, sizeof(buf), "Host: %s", host); - if (!send_line_crlf(sd, buf)) - goto error; - - /* send User-Agent string if provided */ - if (p->options.user_agent) - { - openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", - p->options.user_agent); - if (!send_line_crlf (sd, buf)) - goto error; - } + if (!add_proxy_headers (p, sd, host, port)) + goto error; /* auth specified? */ switch (p->auth_method) @@ -586,7 +640,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; /* receive reply from proxy */ - if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + if (!recv_line (sd, buf, sizeof(buf), get_server_poll_remaining_time (server_poll_timeout), true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ @@ -615,7 +669,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, while (true) { - if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + if (!recv_line (sd, buf, sizeof(buf), get_server_poll_remaining_time (server_poll_timeout), true, NULL, signal_received)) goto error; chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); @@ -642,7 +696,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* now send the phase 3 reply */ /* format HTTP CONNECT message */ - openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", + openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%s HTTP/%s", host, port, p->options.http_version); @@ -658,14 +712,11 @@ establish_http_proxy_passthru (struct http_proxy_info *p, if (!send_line_crlf (sd, buf)) goto error; - /* send HOST etc, */ - openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); - msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); - if (!send_line_crlf (sd, buf)) - goto error; + if (!add_proxy_headers (p, sd, host, port)) + goto error; - msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3"); + msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3"); { const char *np3 = ntlm_phase_3 (p, buf2, &gc); if (!np3) @@ -685,7 +736,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; /* receive reply from proxy */ - if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + if (!recv_line (sd, buf, sizeof(buf), get_server_poll_remaining_time (server_poll_timeout), true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ @@ -730,7 +781,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* build the digest response */ - openvpn_snprintf (uri, sizeof(uri), "%s:%d", + openvpn_snprintf (uri, sizeof(uri), "%s:%s", host, port); @@ -771,9 +822,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; /* send HOST etc, */ - openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); - msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); - if (!send_line_crlf (sd, buf)) + if (!add_proxy_headers (p, sd, host, port)) goto error; /* send digest response */ @@ -795,7 +844,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; /* receive reply from proxy */ - if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + if (!recv_line (sd, buf, sizeof(buf), get_server_poll_remaining_time (server_poll_timeout), true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ @@ -819,7 +868,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* figure out what kind of authentication the proxy needs */ char *pa = NULL; const int method = get_proxy_authenticate(sd, - p->options.timeout, + get_server_poll_remaining_time (server_poll_timeout), &pa, NULL, signal_received); @@ -863,7 +912,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, msg (D_LINK_ERRORS, "HTTP proxy returned bad status"); #if 0 /* DEBUGGING -- show a multi-line HTTP error response */ - dump_residual(sd, p->options.timeout, signal_received); + dump_residual(sd, get_server_poll_remaining_time (server_poll_timeout), signal_received); #endif goto error; } @@ -871,7 +920,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* SUCCESS */ /* receive line from proxy and discard */ - if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) + if (!recv_line (sd, NULL, 0, get_server_poll_remaining_time (server_poll_timeout), true, NULL, signal_received)) goto error; /* @@ -894,14 +943,8 @@ establish_http_proxy_passthru (struct http_proxy_info *p, return ret; error: - /* on error, should we exit or restart? */ if (!*signal_received) - *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */ + *signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- HTTP proxy error */ gc_free (&gc); return ret; } - -#else -static void dummy(void) {} -#endif /* ENABLE_HTTP_PROXY */ - diff --git a/src/openvpn/proxy.h b/src/openvpn/proxy.h index 5e476f1..7d2581c 100644 --- a/src/openvpn/proxy.h +++ b/src/openvpn/proxy.h @@ -28,8 +28,6 @@ #include "buffer.h" #include "misc.h" -#ifdef ENABLE_HTTP_PROXY - /* HTTP CONNECT authentication methods */ #define HTTP_AUTH_NONE 0 #define HTTP_AUTH_BASIC 1 @@ -38,11 +36,15 @@ #define HTTP_AUTH_NTLM2 4 #define HTTP_AUTH_N 5 /* number of HTTP_AUTH methods */ +struct http_custom_header { + const char *name; + const char *content; +}; + +#define MAX_CUSTOM_HTTP_HEADER 10 struct http_proxy_options { const char *server; - int port; - bool retry; - int timeout; + const char *port; # define PAR_NO 0 /* don't support any auth retries */ # define PAR_ALL 1 /* allow all proxy auth protocols */ @@ -53,11 +55,13 @@ struct http_proxy_options { const char *auth_file; const char *http_version; const char *user_agent; + struct http_custom_header custom_headers[MAX_CUSTOM_HTTP_HEADER]; + bool inline_creds; }; struct http_proxy_options_simple { const char *server; - int port; + const char *port; int auth_retry; }; @@ -80,13 +84,12 @@ void http_proxy_close (struct http_proxy_info *hp); bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ - const int port, /* openvpn server port */ + const char *port, /* openvpn server port */ + struct event_timeout* server_poll_timeout, struct buffer *lookahead, volatile int *signal_received); uint8_t *make_base64_string2 (const uint8_t *str, int str_len, struct gc_arena *gc); uint8_t *make_base64_string (const uint8_t *str, struct gc_arena *gc); -#endif /* ENABLE_HTTP_PROXY */ - #endif /* PROXY_H */ diff --git a/src/openvpn/ps.c b/src/openvpn/ps.c index 6495dc7..2cb68f1 100644 --- a/src/openvpn/ps.c +++ b/src/openvpn/ps.c @@ -223,12 +223,12 @@ port_share_sendmsg (const socket_descriptor_t sd, if (socket_defined (sd_send)) { - *((socket_descriptor_t*)CMSG_DATA(h)) = sd_send; + memcpy (CMSG_DATA(h), &sd_send, sizeof (sd_send)); } else { socketpair (PF_UNIX, SOCK_DGRAM, 0, sd_null); - *((socket_descriptor_t*)CMSG_DATA(h)) = sd_null[0]; + memcpy (CMSG_DATA(h), &sd_null[0], sizeof (sd_null[0])); } status = sendmsg (sd, &mesg, MSG_NOSIGNAL); @@ -330,8 +330,8 @@ journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_ 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); + const char *f = print_openvpn_sockaddr (&from, &gc); + const char *t = print_openvpn_sockaddr (&to, &gc); fnlen = strlen(journal_dir) + strlen(t) + 2; jfn = (char *) malloc(fnlen); check_malloc_return (jfn); @@ -340,7 +340,8 @@ journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_ fd = platform_open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd != -1) { - write(fd, f, strlen(f)); + if (write(fd, f, strlen(f)) != strlen(f)) + msg(M_WARN, "PORT SHARE: writing to journal file (%s) failed", jfn); close (fd); cp->jfn = jfn; } @@ -371,17 +372,6 @@ proxy_list_close (struct proxy_connection **list) } } -static void -sock_addr_set (struct openvpn_sockaddr *osaddr, - const in_addr_t addr, - const int port) -{ - CLEAR (*osaddr); - 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 proxy_connection_io_requeue (struct proxy_connection *pc, const int rwflags_new, struct event_set *es) { @@ -403,26 +393,23 @@ proxy_connection_io_requeue (struct proxy_connection *pc, const int rwflags_new, static bool proxy_entry_new (struct proxy_connection **list, struct event_set *es, - const in_addr_t server_addr, - const int server_port, + const struct sockaddr_in server_addr, const socket_descriptor_t sd_client, struct buffer *initial_data, const char *journal_dir) { - struct openvpn_sockaddr osaddr; socket_descriptor_t sd_server; int status; struct proxy_connection *pc; struct proxy_connection *cp; /* connect to port share server */ - sock_addr_set (&osaddr, server_addr, server_port); if ((sd_server = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { msg (M_WARN|M_ERRNO, "PORT SHARE PROXY: cannot create socket"); return false; } - status = openvpn_connect (sd_server, &osaddr, 5, NULL); + status = openvpn_connect (sd_server,(const struct sockaddr*) &server_addr, 5, NULL); if (status) { msg (M_WARN, "PORT SHARE PROXY: connect to port-share server failed"); @@ -482,8 +469,7 @@ static bool 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 struct sockaddr_in server_addr, const int max_initial_buf, const char *journal_dir) { @@ -516,7 +502,8 @@ control_message_from_parent (const socket_descriptor_t sd_control, h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t)); h->cmsg_level = SOL_SOCKET; h->cmsg_type = SCM_RIGHTS; - *((socket_descriptor_t*)CMSG_DATA(h)) = SOCKET_UNDEFINED; + static const socket_descriptor_t socket_undefined = SOCKET_UNDEFINED; + memcpy (CMSG_DATA(h), &socket_undefined, sizeof(socket_undefined)); status = recvmsg (sd_control, &mesg, MSG_NOSIGNAL); if (status != -1) @@ -530,7 +517,8 @@ control_message_from_parent (const socket_descriptor_t sd_control, } else { - const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h)); + socket_descriptor_t received_fd; + memcpy (&received_fd, CMSG_DATA(h), sizeof(received_fd)); dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", (int)received_fd); if (status >= 2 && command == COMMAND_REDIRECT) @@ -539,7 +527,6 @@ control_message_from_parent (const socket_descriptor_t sd_control, if (proxy_entry_new (list, es, server_addr, - server_port, received_fd, &buf, journal_dir)) @@ -716,8 +703,7 @@ 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, +port_share_proxy (const struct sockaddr_in hostaddr, const socket_descriptor_t sd_control, const int max_initial_buf, const char *journal_dir) @@ -754,7 +740,7 @@ port_share_proxy (const in_addr_t hostaddr, 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, max_initial_buf, journal_dir)) + if (!control_message_from_parent (sd_control, &list, es, hostaddr, max_initial_buf, journal_dir)) goto done; } else @@ -789,14 +775,16 @@ port_share_proxy (const in_addr_t hostaddr, */ struct port_share * port_share_open (const char *host, - const int port, + const char *port, const int max_initial_buf, const char *journal_dir) { pid_t pid; socket_descriptor_t fd[2]; - in_addr_t hostaddr; + struct sockaddr_in hostaddr; struct port_share *ps; + int status; + struct addrinfo* ai; ALLOC_OBJ_CLEAR (ps, struct port_share); ps->foreground_fd = -1; @@ -805,7 +793,12 @@ port_share_open (const char *host, /* * Get host's IP address */ - hostaddr = getaddr (GETADDR_RESOLVE|GETADDR_HOST_ORDER|GETADDR_FATAL, host, 0, NULL, NULL); + + status = openvpn_getaddrinfo (GETADDR_RESOLVE|GETADDR_FATAL, + host, port, 0, NULL, AF_INET, &ai); + ASSERT (status==0); + hostaddr = *((struct sockaddr_in*) ai->ai_addr); + freeaddrinfo(ai); /* * Make a socket for foreground and background processes @@ -881,7 +874,7 @@ port_share_open (const char *host, prng_init (NULL, 0); /* execute the event loop */ - port_share_proxy (hostaddr, port, fd[1], max_initial_buf, journal_dir); + port_share_proxy (hostaddr, fd[1], max_initial_buf, journal_dir); openvpn_close_socket (fd[1]); diff --git a/src/openvpn/ps.h b/src/openvpn/ps.h index 4280635..e8919d4 100644 --- a/src/openvpn/ps.h +++ b/src/openvpn/ps.h @@ -44,7 +44,7 @@ struct port_share { extern struct port_share *port_share; struct port_share *port_share_open (const char *host, - const int port, + const char *port, const int max_initial_buf, const char *journal_dir); diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 71f39c1..f86bdd3 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -33,12 +33,40 @@ #include "push.h" #include "options.h" #include "ssl.h" +#include "ssl_verify.h" #include "manage.h" #include "memdbg.h" #if P2MP +static char push_reply_cmd[] = "PUSH_REPLY"; + +/** + * Add an option to the given push list by providing a format string. + * + * The string added to the push options is allocated in o->gc, so the caller + * does not have to preserve anything. + * + * @param gc GC arena where options are allocated + * @param push_list Push list containing options + * @param msglevel The message level to use when printing errors + * @param fmt Format string for the option + * @param ... Format string arguments + * + * @return true on success, false on failure. + */ +static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, + int msglevel, const char *fmt, ...) +#ifdef __GNUC__ +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format (gnu_printf, 4, 5))) +#else + __attribute__ ((format (__printf__, 4, 5))) +#endif +#endif + ; + /* * Auth username/password * @@ -49,7 +77,8 @@ void receive_auth_failed (struct context *c, const struct buffer *buffer) { msg (M_VERB0, "AUTH: Received control message: %s", BSTR(buffer)); - connection_list_set_no_advance(&c->options); + c->options.no_advance=true; + if (c->options.pull) { switch (auth_retry_get ()) @@ -120,8 +149,7 @@ server_pushed_signal (struct context *c, const struct buffer *buffer, const bool else if (m[i] == 'N') { /* next server? */ - if (c->options.connection_list) - c->options.connection_list->no_advance = false; + c->options.no_advance = false; } } } @@ -214,11 +242,37 @@ incoming_push_message (struct context *c, const struct buffer *buffer) { c->options.push_option_types_found |= option_types_found; + /* delay bringing tun/tap up until --push parms received from remote */ if (status == PUSH_MSG_REPLY) - do_up (c, true, c->options.push_option_types_found ); /* delay bringing tun/tap up until --push parms received from remote */ + { + if (!do_up (c, true, c->options.push_option_types_found)) + { + msg (D_PUSH_ERRORS, "Failed to open tun/tap interface"); + goto error; + } + } event_timeout_clear (&c->c2.push_request_interval); } + else if (status == PUSH_MSG_REQUEST) + { + if (c->options.mode == MODE_SERVER) + { + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + /* Do not regenerate keys if client send a second push request */ + if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized && + !tls_session_update_crypto_params (session, &c->options, + &c->c2.frame)) + { + msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed"); + goto error; + } + } + } + goto cleanup; +error: + register_signal (c, SIGUSR1, "process-push-msg-failed"); +cleanup: gc_free (&gc); } @@ -241,79 +295,158 @@ send_push_request (struct context *c) #if P2MP_SERVER -bool -send_push_reply (struct context *c) +/** + * Prepare push options, based on local options and available peer info. + * + * @param context context structure storing data for VPN tunnel + * @param gc gc arena for allocating push options + * @param push_list push list to where options are added + * + * @return true on success, false on failure. + */ +static bool +prepare_push_reply (struct context *c, struct gc_arena *gc, + struct push_list *push_list) { - struct gc_arena gc = gc_new (); - struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); - struct push_entry *e = c->options.push_list.head; - bool multi_push = false; - static char cmd[] = "PUSH_REPLY"; - const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ - const int safe_cap = BCAP (&buf) - extra; - bool push_sent = false; + const char *optstr = NULL; + struct tls_multi *tls_multi = c->c2.tls_multi; + const char * const peer_info = tls_multi->peer_info; + struct options *o = &c->options; - msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap ); + /* ipv6 */ + if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked) + { + push_option_fmt (gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s", + print_in6_addr (c->c2.push_ifconfig_ipv6_local, 0, gc), + c->c2.push_ifconfig_ipv6_netbits, + print_in6_addr (c->c2.push_ifconfig_ipv6_remote, + 0, gc)); + } - buf_printf (&buf, "%s", cmd); + /* ipv4 */ + if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && + c->c2.push_ifconfig_remote_netmask) + { + in_addr_t ifconfig_local = c->c2.push_ifconfig_local; + if (c->c2.push_ifconfig_local_alias) + ifconfig_local = c->c2.push_ifconfig_local_alias; + push_option_fmt (gc, push_list, M_USAGE, "ifconfig %s %s", + print_in_addr_t (ifconfig_local, 0, gc), + print_in_addr_t (c->c2.push_ifconfig_remote_netmask, + 0, gc)); + } + + /* Send peer-id if client supports it */ + optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; + if (optstr) + { + int proto = 0; + int r = sscanf(optstr, "IV_PROTO=%d", &proto); + if ((r == 1) && (proto >= 2)) + { + push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", + tls_multi->peer_id); + } + } - if ( c->c2.push_ifconfig_ipv6_defined ) + /* Push cipher if client supports Negotiable Crypto Parameters */ + if (tls_peer_info_ncp_ver (peer_info) >= 2 && o->ncp_enabled) { - /* IPv6 is put into buffer first, could be lengthy */ - buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s", - print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc), - c->c2.push_ifconfig_ipv6_netbits, - print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) ); - if (BLEN (&buf) >= safe_cap) + /* if we have already created our key, we cannot change our own + * cipher, so disable NCP and warn = explain why + */ + const struct tls_session *session = &tls_multi->session[TM_ACTIVE]; + if ( session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized ) + { + msg( M_INFO, "PUSH: client wants to negotiate cipher (NCP), but " + "server has already generated data channel keys, " + "ignoring client request" ); + } + else { - msg (M_WARN, "--push ifconfig-ipv6 option is too long"); - goto fail; + /* Push the first cipher from --ncp-ciphers to the client. + * TODO: actual negotiation, instead of server dictatorship. */ + char *push_cipher = string_alloc(o->ncp_ciphers, &o->gc); + o->ciphername = strtok (push_cipher, ":"); + push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername); } } + /* If server uses --auth-gen-token and we have an auth token + * to send to the client + */ + if (false == tls_multi->auth_token_sent && NULL != tls_multi->auth_token) + { + push_option_fmt(gc, push_list, M_USAGE, + "auth-token %s", tls_multi->auth_token); + tls_multi->auth_token_sent = true; + } + return true; +} + +static bool +send_push_options (struct context *c, struct buffer *buf, + struct push_list *push_list, int safe_cap, + bool *push_sent, bool *multi_push) +{ + struct push_entry *e = push_list->head; + while (e) { if (e->enable) { const int l = strlen (e->option); - if (BLEN (&buf) + l >= safe_cap) + if (BLEN (buf) + l >= safe_cap) { - buf_printf (&buf, ",push-continuation 2"); - { - const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); - if (!status) - goto fail; - push_sent = true; - multi_push = true; - buf_reset_len (&buf); - buf_printf (&buf, "%s", cmd); - } + buf_printf (buf, ",push-continuation 2"); + { + const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH); + if (!status) + return false; + *push_sent = true; + *multi_push = true; + buf_reset_len (buf); + buf_printf (buf, "%s", push_reply_cmd); + } } - if (BLEN (&buf) + l >= safe_cap) + if (BLEN (buf) + l >= safe_cap) { msg (M_WARN, "--push option is too long"); - goto fail; + return false; } - buf_printf (&buf, ",%s", e->option); + buf_printf (buf, ",%s", e->option); } e = e->next; } + return true; +} + +static bool +send_push_reply (struct context *c, struct push_list *per_client_push_list) +{ + struct gc_arena gc = gc_new (); + struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); + bool multi_push = false; + const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ + const int safe_cap = BCAP (&buf) - extra; + bool push_sent = false; + + buf_printf (&buf, "%s", push_reply_cmd); + + /* send options which are common to all clients */ + if (!send_push_options (c, &buf, &c->options.push_list, safe_cap, + &push_sent, &multi_push)) + goto fail; + + /* send client-specific options */ + if (!send_push_options (c, &buf, per_client_push_list, safe_cap, + &push_sent, &multi_push)) + goto fail; - if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask) - { - 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"); - if (BLEN (&buf) > sizeof(cmd)-1) + if (BLEN (&buf) > sizeof(push_reply_cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) @@ -329,7 +462,7 @@ send_push_reply (struct context *c) bool status = false; buf_reset_len (&buf); - buf_printf (&buf, "%s", cmd); + buf_printf (&buf, "%s", push_reply_cmd); status = send_control_channel_string (c, BSTR(&buf), D_PUSH); if (!status) goto fail; @@ -344,7 +477,8 @@ send_push_reply (struct context *c) } static void -push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) +push_option_ex (struct gc_arena *gc, struct push_list *push_list, + const char *opt, bool enable, int msglevel) { if (!string_class (opt, CC_ANY, CC_COMMA)) { @@ -353,20 +487,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) else { struct push_entry *e; - ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc); + ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc); e->enable = true; e->option = opt; - if (o->push_list.head) + if (push_list->head) { - ASSERT(o->push_list.tail); - o->push_list.tail->next = e; - o->push_list.tail = e; + ASSERT(push_list->tail); + push_list->tail->next = e; + push_list->tail = e; } else { - ASSERT(!o->push_list.tail); - o->push_list.head = e; - o->push_list.tail = e; + ASSERT(!push_list->tail); + push_list->head = e; + push_list->tail = e; } } } @@ -374,7 +508,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) void push_option (struct options *o, const char *opt, int msglevel) { - push_option_ex (o, opt, true, msglevel); + push_option_ex (&o->gc, &o->push_list, opt, true, msglevel); } void @@ -386,7 +520,8 @@ clone_push_list (struct options *o) push_reset (o); while (e) { - push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL); + push_option_ex (&o->gc, &o->push_list, + string_alloc (e->option, &o->gc), true, M_FATAL); e = e->next; } } @@ -400,55 +535,137 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc) push_option (o, opt, msglevel); } +static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, + int msglevel, const char *format, ...) +{ + va_list arglist; + char tmp[256] = {0}; + int len; + va_start (arglist, format); + len = vsnprintf (tmp, sizeof(tmp), format, arglist); + va_end (arglist); + if (len > sizeof(tmp)-1) + return false; + push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel); + return true; +} + void push_reset (struct options *o) { CLEAR (o->push_list); } + +void +push_remove_option (struct options *o, const char *p) +{ + msg (D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p); + + /* ifconfig-ipv6 is special, as not part of the push list */ + if ( streq( p, "ifconfig-ipv6" )) + { + o->push_ifconfig_ipv6_blocked = true; + return; + } + + if (o && o->push_list.head ) + { + struct push_entry *e = o->push_list.head; + + /* cycle through the push list */ + while (e) + { + if ( e->enable && + strncmp( e->option, p, strlen(p) ) == 0 ) + { + msg (D_PUSH_DEBUG, "PUSH_REMOVE removing: '%s'", e->option); + e->enable = false; + } + + e = e->next; + } + } +} #endif +#if P2MP_SERVER int -process_incoming_push_msg (struct context *c, - const struct buffer *buffer, - bool honor_received_options, - unsigned int permission_mask, - unsigned int *option_types_found) +process_incoming_push_request (struct context *c) { int ret = PUSH_MSG_ERROR; - struct buffer buf = *buffer; -#if P2MP_SERVER - if (buf_string_compare_advance (&buf, "PUSH_REQUEST")) +#ifdef ENABLE_ASYNC_PUSH + c->c2.push_request_received = true; +#endif + if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED) { - if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED) + const char *client_reason = tls_client_reason (c->c2.tls_multi); + send_auth_failed (c, client_reason); + ret = PUSH_MSG_AUTH_FAILURE; + } + else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED) + { + time_t now; + + openvpn_time (&now); + if (c->c2.sent_push_reply_expiry > now) { - const char *client_reason = tls_client_reason (c->c2.tls_multi); - send_auth_failed (c, client_reason); - ret = PUSH_MSG_AUTH_FAILURE; + ret = PUSH_MSG_ALREADY_REPLIED; } - else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED) + else { - time_t now; + /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */ + struct push_list push_list; + struct gc_arena gc = gc_new (); - openvpn_time(&now); - if (c->c2.sent_push_reply_expiry > now) + CLEAR (push_list); + if (prepare_push_reply (c, &gc, &push_list) && + send_push_reply (c, &push_list)) { - ret = PUSH_MSG_ALREADY_REPLIED; - } - else - { - if (send_push_reply (c)) - { - ret = PUSH_MSG_REQUEST; - c->c2.sent_push_reply_expiry = now + 30; - } + ret = PUSH_MSG_REQUEST; + c->c2.sent_push_reply_expiry = now + 30; } + gc_free(&gc); } - else + } + else + { + ret = PUSH_MSG_REQUEST_DEFERRED; + } + + return ret; +} +#endif + +static void +push_update_digest(md_ctx_t *ctx, struct buffer *buf) +{ + char line[OPTION_PARM_SIZE]; + while (buf_parse (buf, ',', line, sizeof (line))) + { + /* peer-id might change on restart and this should not trigger reopening tun */ + if (strstr (line, "peer-id ") != line) { - ret = PUSH_MSG_REQUEST_DEFERRED; + md_ctx_update (ctx, (const uint8_t *) line, strlen(line)); } } +} + +int +process_incoming_push_msg (struct context *c, + const struct buffer *buffer, + bool honor_received_options, + unsigned int permission_mask, + unsigned int *option_types_found) +{ + int ret = PUSH_MSG_ERROR; + struct buffer buf = *buffer; + +#if P2MP_SERVER + if (buf_string_compare_advance (&buf, "PUSH_REQUEST")) + { + ret = process_incoming_push_request(c); + } else #endif @@ -460,12 +677,12 @@ process_incoming_push_msg (struct context *c, struct buffer buf_orig = buf; if (!c->c2.pulled_options_md5_init_done) { - md5_state_init (&c->c2.pulled_options_state); + md_ctx_init(&c->c2.pulled_options_state, md_kt_get("MD5")); c->c2.pulled_options_md5_init_done = true; } if (!c->c2.did_pre_pull_restore) { - pre_pull_restore (&c->options); + pre_pull_restore (&c->options, &c->c2.gc); c->c2.did_pre_pull_restore = true; } if (apply_push_options (&c->options, @@ -473,20 +690,22 @@ process_incoming_push_msg (struct context *c, permission_mask, option_types_found, c->c2.es)) - switch (c->options.push_continuation) - { - case 0: - case 1: - md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig)); - md5_state_final (&c->c2.pulled_options_state, &c->c2.pulled_options_digest); - c->c2.pulled_options_md5_init_done = false; - ret = PUSH_MSG_REPLY; - break; - case 2: - md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig)); - ret = PUSH_MSG_CONTINUATION; - break; - } + { + push_update_digest (&c->c2.pulled_options_state, &buf_orig); + switch (c->options.push_continuation) + { + case 0: + case 1: + md_ctx_final (&c->c2.pulled_options_state, c->c2.pulled_options_digest.digest); + md_ctx_cleanup (&c->c2.pulled_options_state); + c->c2.pulled_options_md5_init_done = false; + ret = PUSH_MSG_REPLY; + break; + case 2: + ret = PUSH_MSG_CONTINUATION; + break; + } + } } else if (ch == '\0') { @@ -518,7 +737,8 @@ remove_iroutes_from_push_route_list (struct options *o) /* parse the push item */ CLEAR (p); - if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) + if ( e->enable && + parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) { /* is the push item a route directive? */ if (p[0] && !strcmp (p[0], "route") && !p[3]) @@ -544,12 +764,12 @@ remove_iroutes_from_push_route_list (struct options *o) } } } - } - /* should we copy the push item? */ - e->enable = enable; - if (!enable) - msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option); + /* should we copy the push item? */ + e->enable = enable; + if (!enable) + msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option); + } e = e->next; } diff --git a/src/openvpn/push.h b/src/openvpn/push.h index 8c3f157..d6cb4b1 100644 --- a/src/openvpn/push.h +++ b/src/openvpn/push.h @@ -37,8 +37,7 @@ #define PUSH_MSG_CONTINUATION 5 #define PUSH_MSG_ALREADY_REPLIED 6 -void incoming_push_message (struct context *c, - const struct buffer *buffer); +int process_incoming_push_request (struct context *c); int process_incoming_push_msg (struct context *c, const struct buffer *buffer, @@ -54,14 +53,15 @@ void server_pushed_signal (struct context *c, const struct buffer *buffer, const #if P2MP_SERVER +void incoming_push_message (struct context *c, const struct buffer *buffer); + void clone_push_list (struct options *o); void push_option (struct options *o, const char *opt, int msglevel); void push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc); void push_reset (struct options *o); - -bool send_push_reply (struct context *c); +void push_remove_option (struct options *o, const char *p); void remove_iroutes_from_push_route_list (struct options *o); diff --git a/src/openvpn/reliable.c b/src/openvpn/reliable.c index 763169e..22883a7 100644 --- a/src/openvpn/reliable.c +++ b/src/openvpn/reliable.c @@ -35,7 +35,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #include "buffer.h" #include "error.h" @@ -754,4 +754,4 @@ reliable_debug_print (const struct reliable *rel, char *desc) #else static void dummy(void) {} -#endif /* ENABLE_CRYPTO && ENABLE_SSL*/ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/reliable.h b/src/openvpn/reliable.h index 594ab82..828dcd3 100644 --- a/src/openvpn/reliable.h +++ b/src/openvpn/reliable.h @@ -29,7 +29,7 @@ */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #ifndef RELIABLE_H #define RELIABLE_H @@ -477,4 +477,4 @@ void reliable_ack_debug_print (const struct reliable_ack *ack, char *desc); #endif /* RELIABLE_H */ -#endif /* ENABLE_CRYPTO && ENABLE_SSL */ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/route.c b/src/openvpn/route.c index 827bd79..fec12c1 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -42,12 +42,21 @@ #include "manage.h" #include "win32.h" #include "options.h" -#include "win32.h" #include "memdbg.h" -#ifdef WIN32 +#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) +#include /* RTM_GETROUTE etc. */ +#endif + +#ifdef _WIN32 +#include "openvpn-msg.h" + #define METRIC_NOT_USED ((DWORD)-1) +static bool add_route_service (const struct route_ipv4 *, const struct tuntap *); +static bool del_route_service (const struct route_ipv4 *, const struct tuntap *); +static bool add_route_ipv6_service (const struct route_ipv6 *, const struct tuntap *); +static bool del_route_ipv6_service (const struct route_ipv6 *, const struct tuntap *); #endif static void delete_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es); @@ -93,76 +102,62 @@ add_bypass_address (struct route_bypass *rb, const in_addr_t a) } struct route_option_list * -new_route_option_list (const int max_routes, struct gc_arena *a) +new_route_option_list (struct gc_arena *a) { struct route_option_list *ret; - ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_option_list, struct route_option, max_routes, a); - ret->capacity = max_routes; + ALLOC_OBJ_CLEAR_GC (ret, struct route_option_list, a); + ret->gc = a; return ret; } struct route_ipv6_option_list * -new_route_ipv6_option_list (const int max_routes, struct gc_arena *a) +new_route_ipv6_option_list (struct gc_arena *a) { struct route_ipv6_option_list *ret; - ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_option_list, struct route_ipv6_option, max_routes, a); - ret->capacity = max_routes; + ALLOC_OBJ_CLEAR_GC (ret, struct route_ipv6_option_list, a); + ret->gc = a; return ret; } +/* + * NOTE: structs are cloned/copied shallow by design. + * The routes list from src will stay intact since it is allocated using + * the options->gc. The cloned/copied lists will share this common tail + * to avoid copying the data around between pulls. Pulled routes use + * the c2->gc so they get freed immediately after a reconnect. + */ struct route_option_list * clone_route_option_list (const struct route_option_list *src, struct gc_arena *a) { - const size_t rl_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list)); - struct route_option_list *ret = gc_malloc (rl_size, false, a); - memcpy (ret, src, rl_size); + struct route_option_list *ret; + ALLOC_OBJ_GC (ret, struct route_option_list, a); + *ret = *src; return ret; } struct route_ipv6_option_list * clone_route_ipv6_option_list (const struct route_ipv6_option_list *src, struct gc_arena *a) { - const size_t rl_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list)); - struct route_ipv6_option_list *ret = gc_malloc (rl_size, false, a); - memcpy (ret, src, rl_size); + struct route_ipv6_option_list *ret; + ALLOC_OBJ_GC (ret, struct route_ipv6_option_list, a); + *ret = *src; return ret; } void -copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src) +copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src, struct gc_arena *a) { - const size_t src_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list)); - if (src->capacity > dest->capacity) - msg (M_FATAL, PACKAGE_NAME " ROUTE: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->capacity, dest->capacity); - memcpy (dest, src, src_size); + *dest = *src; + dest->gc = a; } void copy_route_ipv6_option_list (struct route_ipv6_option_list *dest, - const struct route_ipv6_option_list *src) + const struct route_ipv6_option_list *src, + struct gc_arena *a) { - const size_t src_size = array_mult_safe (sizeof(struct route_ipv6_option), src->capacity, sizeof(struct route_ipv6_option_list)); - if (src->capacity > dest->capacity) - msg (M_FATAL, PACKAGE_NAME " ROUTE: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->capacity, dest->capacity); - memcpy (dest, src, src_size); -} - -struct route_list * -new_route_list (const int max_routes, struct gc_arena *a) -{ - struct route_list *ret; - ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_list, struct route_ipv4, max_routes, a); - ret->capacity = max_routes; - return ret; -} - -struct route_ipv6_list * -new_route_ipv6_list (const int max_routes, struct gc_arena *a) -{ - struct route_ipv6_list *ret; - ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_ipv6_list, struct route_ipv6, max_routes, a); - ret->capacity = max_routes; - return ret; + *dest = *src; + dest->gc = a; } static const char * @@ -292,15 +287,15 @@ init_route (struct route_ipv4 *r, /* get_special_addr replaces specialaddr with a special ip addr like gw. getaddrinfo is called to convert a a addrinfo struct */ - if(get_special_addr (rl, ro->network, &special.s_addr, &status)) + if(get_special_addr (rl, ro->network, (in_addr_t *) &special.s_addr, &status)) { special.s_addr = htonl(special.s_addr); - ret = openvpn_getaddrinfo(0, inet_ntoa(special), 0, NULL, + ret = openvpn_getaddrinfo(0, inet_ntoa(special), NULL, 0, NULL, AF_INET, network_list); } else ret = openvpn_getaddrinfo(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL, - ro->network, 0, NULL, AF_INET, network_list); + ro->network, NULL, 0, NULL, AF_INET, network_list); status = (ret == 0); @@ -389,7 +384,7 @@ init_route_ipv6 (struct route_ipv6 *r6, const struct route_ipv6_option *r6o, const struct route_ipv6_list *rl6 ) { - r6->defined = false; + CLEAR (*r6); if ( !get_ipv6_addr( r6o->prefix, &r6->network, &r6->netbits, M_WARN )) goto fail; @@ -402,7 +397,7 @@ init_route_ipv6 (struct route_ipv6 *r6, msg( M_WARN, PACKAGE_NAME "ROUTE6: cannot parse gateway spec '%s'", r6o->gateway ); } } - else if (rl6->remote_endpoint_defined) + else if (rl6->spec_flags & RTSA_REMOTE_ENDPOINT) { r6->gateway = rl6->remote_endpoint_ipv6; } @@ -414,7 +409,6 @@ init_route_ipv6 (struct route_ipv6 *r6, /* metric */ - r6->metric_defined = false; r6->metric = -1; if (is_route_parm_defined (r6o->metric)) { @@ -426,22 +420,21 @@ init_route_ipv6 (struct route_ipv6 *r6, r6o->metric); goto fail; } - r6->metric_defined = true; + r6->flags |= RT_METRIC_DEFINED; } - else if (rl6->default_metric_defined) + else if (rl6->spec_flags & RTSA_DEFAULT_METRIC) { r6->metric = rl6->default_metric; - r6->metric_defined = true; + r6->flags |= RT_METRIC_DEFINED; } - r6->defined = true; + r6->flags |= RT_DEFINED; return true; fail: msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s", r6o->prefix); - r6->defined = false; return false; } @@ -453,15 +446,14 @@ add_route_to_option_list (struct route_option_list *l, const char *metric) { struct route_option *ro; - if (l->n >= l->capacity) - msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d routes -- please increase the max-routes option in the client configuration file", - l->capacity); - ro = &l->routes[l->n]; + ALLOC_OBJ_GC (ro, struct route_option, l->gc); ro->network = network; ro->netmask = netmask; ro->gateway = gateway; ro->metric = metric; - ++l->n; + ro->next = l->routes; + l->routes = ro; + } void @@ -471,32 +463,26 @@ add_route_ipv6_to_option_list (struct route_ipv6_option_list *l, const char *metric) { struct route_ipv6_option *ro; - if (l->n >= l->capacity) - msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d IPv6 routes -- please increase the max-routes option in the client configuration file", - l->capacity); - ro = &l->routes_ipv6[l->n]; + ALLOC_OBJ_GC (ro, struct route_ipv6_option, l->gc); ro->prefix = prefix; ro->gateway = gateway; ro->metric = metric; - ++l->n; + ro->next = l->routes_ipv6; + l->routes_ipv6 = ro; } void clear_route_list (struct route_list *rl) { - const int capacity = rl->capacity; - const size_t rl_size = array_mult_safe (sizeof(struct route_ipv4), capacity, sizeof(struct route_list)); - memset(rl, 0, rl_size); - rl->capacity = capacity; + gc_free (&rl->gc); + CLEAR (*rl); } void clear_route_ipv6_list (struct route_ipv6_list *rl6) { - const int capacity = rl6->capacity; - const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list)); - memset(rl6, 0, rl6_size); - rl6->capacity = capacity; + gc_free (&rl6->gc); + CLEAR (*rl6); } void @@ -517,22 +503,27 @@ add_block_local_item (struct route_list *rl, { const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED); if ((rl->rgi.flags & rgi_needed) == rgi_needed - && rl->rgi.gateway.netmask < 0xFFFFFFFF - && (rl->n)+2 <= rl->capacity) + && rl->rgi.gateway.netmask < 0xFFFFFFFF) { - struct route_ipv4 r; + struct route_ipv4 *r1, *r2; unsigned int l2; + ALLOC_OBJ_GC (r1, struct route_ipv4, &rl->gc); + ALLOC_OBJ_GC (r2, struct route_ipv4, &rl->gc); + /* split a route into two smaller blocking routes, and direct them to target */ - CLEAR(r); - r.flags = RT_DEFINED; - r.gateway = target; - r.network = gateway->addr & gateway->netmask; l2 = ((~gateway->netmask)+1)>>1; - r.netmask = ~(l2-1); - rl->routes[rl->n++] = r; - r.network += l2; - rl->routes[rl->n++] = r; + r1->flags = RT_DEFINED; + r1->gateway = target; + r1->network = gateway->addr & gateway->netmask; + r1->netmask = ~(l2-1); + r1->next = rl->routes; + rl->routes = r1; + + *r2 = *r1; + r2->network += l2; + r2->next = rl->routes; + rl->routes = r2; } } @@ -547,8 +538,10 @@ add_block_local (struct route_list *rl) { size_t i; +#ifndef TARGET_ANDROID /* add bypass for gateway addr */ add_bypass_address (&rl->spec.bypass, rl->rgi.gateway.addr); +#endif /* block access to local subnet */ add_block_local_item (rl, &rl->rgi.gateway, rl->spec.remote_endpoint); @@ -597,7 +590,7 @@ init_route_list (struct route_list *rl, { setenv_route_addr (es, "net_gateway", rl->rgi.gateway.addr, -1); #if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL) - print_default_gateway (D_ROUTE, &rl->rgi); + print_default_gateway (D_ROUTE, &rl->rgi, NULL); #endif } else @@ -644,72 +637,108 @@ init_route_list (struct route_list *rl, /* parse the routes from opt to rl */ { - int i = 0; - int j = rl->n; - bool warned = false; - for (i = 0; i < opt->n; ++i) + struct route_option *ro; + for (ro = opt->routes; ro; ro = ro->next) { struct addrinfo* netlist = NULL; struct route_ipv4 r; - if (!init_route (&r, - &netlist, - &opt->routes[i], - rl)) + if (!init_route (&r, &netlist, ro, rl)) ret = false; else { struct addrinfo* curele; for (curele = netlist; curele; curele = curele->ai_next) { - if (j < rl->capacity) - { - r.network = ntohl(((struct sockaddr_in*)(curele)->ai_addr)->sin_addr.s_addr); - rl->routes[j++] = r; - } - else - { - if (!warned) - { - msg (M_WARN, PACKAGE_NAME " ROUTE: routes dropped because number of expanded routes is greater than route list capacity (%d)", rl->capacity); - warned = true; - } - } + struct route_ipv4 *new; + ALLOC_OBJ_GC (new, struct route_ipv4, &rl->gc); + *new = r; + new->network = ntohl (((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr); + new->next = rl->routes; + rl->routes = new; } } if (netlist) - freeaddrinfo(netlist); + gc_addspecial(netlist, &gc_freeaddrinfo_callback, &gc); } - rl->n = j; } gc_free (&gc); return ret; } +/* check whether an IPv6 host address is covered by a given route_ipv6 + * (not the most beautiful implementation in the world, but portable and + * "good enough") + */ +static bool +route_ipv6_match_host( const struct route_ipv6 *r6, + const struct in6_addr *host ) +{ + unsigned int bits = r6->netbits; + int i; + unsigned int mask; + + if ( bits>128 ) + return false; + + for( i=0; bits >= 8; i++, bits -= 8 ) + { + if ( r6->network.s6_addr[i] != host->s6_addr[i] ) + return false; + } + + if ( bits == 0 ) + return true; + + mask = 0xff << (8-bits); + + if ( (r6->network.s6_addr[i] & mask) == (host->s6_addr[i] & mask )) + return true; + + return false; +} + bool init_route_ipv6_list (struct route_ipv6_list *rl6, const struct route_ipv6_option_list *opt6, const char *remote_endpoint, int default_metric, + const struct in6_addr *remote_host_ipv6, struct env_set *es) { struct gc_arena gc = gc_new (); bool ret = true; + bool need_remote_ipv6_route; clear_route_ipv6_list (rl6); rl6->flags = opt6->flags; + if (remote_host_ipv6) + { + rl6->remote_host_ipv6 = *remote_host_ipv6; + rl6->spec_flags |= RTSA_REMOTE_HOST; + } + if (default_metric >= 0 ) { rl6->default_metric = default_metric; - rl6->default_metric_defined = true; + rl6->spec_flags |= RTSA_DEFAULT_METRIC; } - /* "default_gateway" is stuff for "redirect-gateway", which we don't - * do for IPv6 yet -> TODO - */ + msg (D_ROUTE, "GDG6: remote_host_ipv6=%s", + remote_host_ipv6? print_in6_addr (*remote_host_ipv6, 0, &gc): "n/a" ); + + get_default_gateway_ipv6 (&rl6->rgi6, remote_host_ipv6); + if (rl6->rgi6.flags & RGI_ADDR_DEFINED) + { + setenv_str (es, "net_gateway_ipv6", print_in6_addr (rl6->rgi6.gateway.addr_ipv6, 0, &gc)); +#if defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL) + print_default_gateway (D_ROUTE, NULL, &rl6->rgi6); +#endif + } + else { dmsg (D_ROUTE, "ROUTE6: default_gateway=UNDEF"); } @@ -719,36 +748,81 @@ init_route_ipv6_list (struct route_ipv6_list *rl6, if ( inet_pton( AF_INET6, remote_endpoint, &rl6->remote_endpoint_ipv6) == 1 ) { - rl6->remote_endpoint_defined = true; + rl6->spec_flags |= RTSA_REMOTE_ENDPOINT; } else { - msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint); + msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve VPN endpoint: %s", remote_endpoint); ret = false; } } - else - rl6->remote_endpoint_defined = false; + /* parse the routes from opt6 to rl6 + * discovering potential overlaps with remote_host_ipv6 in the process + */ + need_remote_ipv6_route = false; - if (!(opt6->n >= 0 && opt6->n <= rl6->capacity)) - msg (M_FATAL, PACKAGE_NAME " ROUTE6: (init) number of route options (%d) is greater than route list capacity (%d)", opt6->n, rl6->capacity); - - /* parse the routes from opt to rl6 */ { - int i, j = 0; - for (i = 0; i < opt6->n; ++i) + struct route_ipv6_option *ro6; + for (ro6 = opt6->routes_ipv6; ro6; ro6 = ro6->next) { - if (!init_route_ipv6 (&rl6->routes_ipv6[j], - &opt6->routes_ipv6[i], - rl6 )) + struct route_ipv6 *r6; + ALLOC_OBJ_GC (r6, struct route_ipv6, &rl6->gc); + if (!init_route_ipv6 (r6, ro6, rl6)) ret = false; else - ++j; + { + r6->next = rl6->routes_ipv6; + rl6->routes_ipv6 = r6; + +#ifndef TARGET_ANDROID + /* On Android the VPNService protect function call will take of + * avoiding routing loops, so ignore this part and let + * need_remote_ipv6_route always evaluate to false + */ + if ( remote_host_ipv6 && + route_ipv6_match_host( r6, remote_host_ipv6 ) ) + { + need_remote_ipv6_route = true; + msg (D_ROUTE, "ROUTE6: %s/%d overlaps IPv6 remote %s, adding host route to VPN endpoint", + print_in6_addr (r6->network, 0, &gc), r6->netbits, + print_in6_addr (*remote_host_ipv6, 0, &gc)); + } +#endif + } } - rl6->n = j; } + /* add VPN server host route if needed */ + if ( need_remote_ipv6_route ) + { + if ( (rl6->rgi6.flags & (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) ) == + (RGI_ADDR_DEFINED|RGI_IFACE_DEFINED) ) + { + struct route_ipv6 *r6; + ALLOC_OBJ_CLEAR_GC (r6, struct route_ipv6, &rl6->gc); + + r6->network = *remote_host_ipv6; + r6->netbits = 128; + if ( !(rl6->rgi6.flags & RGI_ON_LINK) ) + { r6->gateway = rl6->rgi6.gateway.addr_ipv6; } + r6->metric = 1; +#ifdef _WIN32 + r6->adapter_index = rl6->rgi6.adapter_index; +#else + r6->iface = rl6->rgi6.iface; +#endif + r6->flags = RT_DEFINED | RT_METRIC_DEFINED; + + r6->next = rl6->routes_ipv6; + rl6->routes_ipv6 = r6; + } + else + { + msg (M_WARN, "ROUTE6: IPv6 route overlaps with IPv6 remote address, but could not determine IPv6 gateway address + interface, expect failure\n" ); + } + } + gc_free (&gc); return ret; } @@ -854,6 +928,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u } else { +#ifndef TARGET_ANDROID bool local = BOOL_CAST(rl->flags & RG_LOCAL); if (rl->flags & RG_AUTO_LOCAL) { const int tla = rl->spec.remote_host_local; @@ -886,6 +961,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled"); } } +#endif /* route DHCP/DNS server traffic through original default gateway */ add_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es); @@ -1015,22 +1091,23 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tun redirect_default_route_to_vpn (rl, tt, flags, es); if ( rl && !(rl->iflags & RL_ROUTES_ADDED) ) { - int i; + struct route_ipv4 *r; #ifdef ENABLE_MANAGEMENT - if (management && rl->n) + if (management && rl->routes) { management_set_state (management, OPENVPN_STATE_ADD_ROUTES, NULL, - 0, - 0); + NULL, + NULL, + NULL, + NULL); } #endif - - for (i = 0; i < rl->n; ++i) + + for (r = rl->routes; r; r = r->next) { - struct route_ipv4 *r = &rl->routes[i]; check_subnet_conflict (r->network, r->netmask, "route"); if (flags & ROUTE_DELETE_FIRST) delete_route (r, tt, flags, &rl->rgi, es); @@ -1038,18 +1115,16 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tun } rl->iflags |= RL_ROUTES_ADDED; } - if (rl6 && !rl6->routes_added) + if (rl6 && !(rl6->iflags & RL_ROUTES_ADDED) ) { - int i; - - for (i = 0; i < rl6->n; ++i) + struct route_ipv6 *r; + for (r = rl6->routes_ipv6; r; r = r->next) { - struct route_ipv6 *r = &rl6->routes_ipv6[i]; if (flags & ROUTE_DELETE_FIRST) delete_route_ipv6 (r, tt, flags, es); add_route_ipv6 (r, tt, flags, es); } - rl6->routes_added = true; + rl6->iflags |= RL_ROUTES_ADDED; } } @@ -1059,10 +1134,9 @@ delete_routes (struct route_list *rl, struct route_ipv6_list *rl6, { if ( rl && rl->iflags & RL_ROUTES_ADDED ) { - int i; - for (i = rl->n - 1; i >= 0; --i) + struct route_ipv4 *r; + for (r = rl->routes; r; r = r->next) { - struct route_ipv4 * r = &rl->routes[i]; delete_route (r, tt, flags, &rl->rgi, es); } rl->iflags &= ~RL_ROUTES_ADDED; @@ -1075,15 +1149,14 @@ delete_routes (struct route_list *rl, struct route_ipv6_list *rl6, clear_route_list (rl); } - if ( rl6 && rl6->routes_added ) + if ( rl6 && (rl6->iflags & RL_ROUTES_ADDED) ) { - int i; - for (i = rl6->n - 1; i >= 0; --i) + struct route_ipv6 *r6; + for (r6 = rl6->routes_ipv6; r6; r6 = r6->next) { - const struct route_ipv6 *r6 = &rl6->routes_ipv6[i]; delete_route_ipv6 (r6, tt, flags, es); } - rl6->routes_added = false; + rl6->iflags &= ~RL_ROUTES_ADDED; } if ( rl6 ) @@ -1098,7 +1171,7 @@ static const char * show_opt (const char *option) { if (!option) - return "nil"; + return "default (not set)"; else return option; } @@ -1117,19 +1190,21 @@ void print_route_options (const struct route_option_list *rol, int level) { - int i; + struct route_option *ro; if (rol->flags & RG_ENABLE) msg (level, " [redirect_default_gateway local=%d]", (rol->flags & RG_LOCAL) != 0); - for (i = 0; i < rol->n; ++i) - print_route_option (&rol->routes[i], level); + for (ro = rol->routes; ro; ro = ro->next) + print_route_option (ro, level); } void -print_default_gateway(const int msglevel, const struct route_gateway_info *rgi) +print_default_gateway(const int msglevel, + const struct route_gateway_info *rgi, + const struct route_ipv6_gateway_info *rgi6) { struct gc_arena gc = gc_new (); - if (rgi->flags & RGI_ADDR_DEFINED) + if (rgi && (rgi->flags & RGI_ADDR_DEFINED)) { struct buffer out = alloc_buf_gc (256, &gc); buf_printf (&out, "ROUTE_GATEWAY"); @@ -1139,7 +1214,7 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi) buf_printf (&out, " %s", print_in_addr_t (rgi->gateway.addr, 0, &gc)); if (rgi->flags & RGI_NETMASK_DEFINED) buf_printf (&out, "/%s", print_in_addr_t (rgi->gateway.netmask, 0, &gc)); -#ifdef WIN32 +#ifdef _WIN32 if (rgi->flags & RGI_IFACE_DEFINED) buf_printf (&out, " I=%u", (unsigned int)rgi->adapter_index); #else @@ -1150,6 +1225,27 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi) buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi->hwaddr, 6, 0, 1, ":", &gc)); msg (msglevel, "%s", BSTR (&out)); } + + if (rgi6 && (rgi6->flags & RGI_ADDR_DEFINED)) + { + struct buffer out = alloc_buf_gc (256, &gc); + buf_printf (&out, "ROUTE6_GATEWAY"); + buf_printf (&out, " %s", print_in6_addr (rgi6->gateway.addr_ipv6, 0, &gc)); + if (rgi6->flags & RGI_ON_LINK) + buf_printf (&out, " ON_LINK"); + if (rgi6->flags & RGI_NETMASK_DEFINED) + buf_printf (&out, "/%d", rgi6->gateway.netbits_ipv6); +#ifdef _WIN32 + if (rgi6->flags & RGI_IFACE_DEFINED) + buf_printf (&out, " I=%u", (unsigned int)rgi6->adapter_index); +#else + if (rgi6->flags & RGI_IFACE_DEFINED) + buf_printf (&out, " IFACE=%s", rgi6->iface); +#endif + if (rgi6->flags & RGI_HWADDR_DEFINED) + buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi6->hwaddr, 6, 0, 1, ":", &gc)); + msg (msglevel, "%s", BSTR (&out)); + } gc_free (&gc); } @@ -1167,9 +1263,9 @@ print_route (const struct route_ipv4 *r, int level) void print_routes (const struct route_list *rl, int level) { - int i; - for (i = 0; i < rl->n; ++i) - print_route (&rl->routes[i], level); + struct route_ipv4 *r; + for (r = rl->routes; r; r = r->next) + print_route (r, level); } static void @@ -1195,16 +1291,17 @@ setenv_route (struct env_set *es, const struct route_ipv4 *r, int i) void setenv_routes (struct env_set *es, const struct route_list *rl) { - int i; - for (i = 0; i < rl->n; ++i) - setenv_route (es, &rl->routes[i], i + 1); + int i = 1; + struct route_ipv4 *r; + for (r = rl->routes; r; r = r->next) + setenv_route (es, r, i++); } static void setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i) { struct gc_arena gc = gc_new (); - if (r6->defined) + if (r6->flags & RT_DEFINED) { struct buffer name1 = alloc_buf_gc( 256, &gc ); struct buffer val = alloc_buf_gc( 256, &gc ); @@ -1223,9 +1320,10 @@ setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i) void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6) { - int i; - for (i = 0; i < rl6->n; ++i) - setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1); + int i = 1; + struct route_ipv6 *r6; + for (r6 = rl6->routes_ipv6; r6; r6 = r6->next) + setenv_route_ipv6 (es, r6, i++); } /* @@ -1297,7 +1395,7 @@ add_route (struct route_ipv4 *r, const struct env_set *es) { struct gc_arena gc; - struct argv argv; + struct argv argv = argv_new (); const char *network; const char *netmask; const char *gateway; @@ -1308,7 +1406,6 @@ add_route (struct route_ipv4 *r, return; gc_init (&gc); - argv_init (&argv); network = print_in_addr_t (r->network, 0, &gc); netmask = print_in_addr_t (r->netmask, 0, &gc); @@ -1323,7 +1420,7 @@ add_route (struct route_ipv4 *r, argv_printf (&argv, "%s route add %s/%d", iproute_path, network, - count_netmask_bits(netmask)); + netmask_to_netbits2(r->netmask)); if (r->flags & RT_METRIC_DEFINED) argv_printf_cat (&argv, "metric %d", r->metric); @@ -1348,7 +1445,16 @@ add_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed"); -#elif defined (WIN32) +#elif defined (TARGET_ANDROID) + struct buffer out = alloc_buf_gc (128, &gc); + + if (rgi) + buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); + else + buf_printf (&out, "%s %s %s", network, netmask, gateway); + management_android_control (management, "ROUTE", buf_bptr(&out)); + +#elif defined (_WIN32) { DWORD ai = TUN_ADAPTER_INDEX_INVALID; argv_printf (&argv, "%s%sc ADD %s MASK %s %s", @@ -1367,7 +1473,12 @@ add_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); - if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI) + if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_SERVICE) + { + status = add_route_service (r, tt); + msg (D_ROUTE, "Route addition via service %s", status ? "succeeded" : "failed"); + } + else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI) { status = add_route_ipapi (r, tt, ai); msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed"); @@ -1511,6 +1622,17 @@ add_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed"); +#elif defined(TARGET_AIX) + + { + int netbits = netmask_to_netbits2(r->netmask); + argv_printf (&argv, "%s add -net %s/%d %s", + ROUTE_PATH, + network, netbits, gateway); + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed"); + } + #else msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script"); #endif @@ -1525,33 +1647,30 @@ add_route (struct route_ipv4 *r, } -static const char * -print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits, - struct gc_arena * gc) +static void +route_ipv6_clear_host_bits( struct route_ipv6 *r6 ) { /* clear host bit parts of route * (needed if routes are specified improperly, or if we need to * explicitely setup/clear the "connected" network routes on some OSes) */ int byte = 15; - int bits_to_clear = 128 - netbits; + int bits_to_clear = 128 - r6->netbits; while( byte >= 0 && bits_to_clear > 0 ) { if ( bits_to_clear >= 8 ) - { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; } + { r6->network.s6_addr[byte--] = 0; bits_to_clear -= 8; } else - { network_copy.s6_addr[byte--] &= (0xff << bits_to_clear); bits_to_clear = 0; } + { r6->network.s6_addr[byte--] &= (0xff << bits_to_clear); bits_to_clear = 0; } } - - return print_in6_addr( network_copy, 0, gc); } void add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) { struct gc_arena gc; - struct argv argv; + struct argv argv = argv_new (); const char *network; const char *gateway; @@ -1560,19 +1679,48 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla bool gateway_needed = false; - if (!r6->defined) + if (! (r6->flags & RT_DEFINED) ) return; +#ifndef _WIN32 + if ( r6->iface != NULL ) /* vpn server special route */ + { + device = r6->iface; + if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) ) + gateway_needed = true; + } +#endif + gc_init (&gc); - argv_init (&argv); - network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc); + route_ipv6_clear_host_bits (r6); + + network = print_in6_addr( r6->network, 0, &gc); gateway = print_in6_addr( r6->gateway, 0, &gc); - if ( !tt->ipv6 ) +#if defined(TARGET_DARWIN) || \ + defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \ + defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) + + /* the BSD platforms cannot specify gateway and interface independently, + * but for link-local destinations, we MUST specify the interface, so + * we build a combined "$gateway%$interface" gateway string + */ + if ( r6->iface != NULL && gateway_needed && + IN6_IS_ADDR_LINKLOCAL(&r6->gateway) ) /* fe80::...%intf */ { - msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s", - network, r6->netbits, device ); + int len = strlen(gateway) + 1 + strlen(r6->iface)+1; + char * tmp = gc_malloc( len, true, &gc ); + snprintf( tmp, len, "%s%%%s", gateway, r6->iface ); + gateway = tmp; + } +#endif + + if (!tt->did_ifconfig_ipv6_setup) + { + msg( M_INFO, "add_route_ipv6(): not adding %s/%d: " + "no IPv6 address been configured on interface %s", + network, r6->netbits, device); return; } @@ -1591,7 +1739,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla * gateway unless the route is to be an on-link network */ if ( tt->type == DEV_TYPE_TAP && - !(r6->metric_defined && r6->metric == 0 ) ) + !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) ) { gateway_needed = true; } @@ -1605,7 +1753,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla device); if (gateway_needed) argv_printf_cat (&argv, "via %s", gateway); - if (r6->metric_defined && r6->metric > 0 ) + if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 ) argv_printf_cat (&argv, " metric %d", r6->metric); #else @@ -1616,70 +1764,95 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla device); if (gateway_needed) argv_printf_cat (&argv, "gw %s", gateway); - if (r6->metric_defined && r6->metric > 0 ) + if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 ) argv_printf_cat (&argv, " metric %d", r6->metric); #endif /*ENABLE_IPROUTE*/ argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed"); -#elif defined (WIN32) +#elif defined (TARGET_ANDROID) + struct buffer out = alloc_buf_gc (64, &gc); + + buf_printf (&out, "%s/%d %s", network, r6->netbits, device); + + management_android_control (management, "ROUTE6", buf_bptr(&out)); + +#elif defined (_WIN32) - if (win32_version_info() != WIN_XP) + if (tt->options.msg_channel) + status = add_route_ipv6_service (r6, tt); + else { struct buffer out = alloc_buf_gc (64, &gc); - buf_printf (&out, "interface=%d", tt->adapter_index ); + if ( r6->adapter_index ) /* vpn server special route */ + { + buf_printf (&out, "interface=%d", r6->adapter_index ); + gateway_needed = true; + } + else + { + buf_printf (&out, "interface=%d", tt->adapter_index ); + } device = buf_bptr(&out); - } - /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */ - argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - network, - r6->netbits, - device); - - /* next-hop depends on TUN or TAP mode: - * - in TAP mode, we use the "real" next-hop - * - in TUN mode we use a special-case link-local address that the tapdrvr - * knows about and will answer ND (neighbor discovery) packets for - */ - if ( tt->type == DEV_TYPE_TUN ) - argv_printf_cat( &argv, " %s", "fe80::8" ); - else - argv_printf_cat( &argv, " %s", gateway ); + /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */ + argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + network, + r6->netbits, + device); + + /* next-hop depends on TUN or TAP mode: + * - in TAP mode, we use the "real" next-hop + * - in TUN mode we use a special-case link-local address that the tapdrvr + * knows about and will answer ND (neighbor discovery) packets for + */ + if ( tt->type == DEV_TYPE_TUN && !gateway_needed ) + argv_printf_cat( &argv, " %s", "fe80::8" ); + else if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) ) + argv_printf_cat( &argv, " %s", gateway ); #if 0 - if (r->metric_defined) - argv_printf_cat (&argv, " METRIC %d", r->metric); + if (r6->flags & RT_METRIC_DEFINED) + argv_printf_cat (&argv, " METRIC %d", r->metric); #endif - /* in some versions of Windows, routes are persistent across reboots by - * default, unless "store=active" is set (pointed out by Tony Lim, thanks) - */ - argv_printf_cat( &argv, " store=active" ); + /* in some versions of Windows, routes are persistent across reboots by + * default, unless "store=active" is set (pointed out by Tony Lim, thanks) + */ + argv_printf_cat( &argv, " store=active" ); - argv_msg (D_ROUTE, &argv); + argv_msg (D_ROUTE, &argv); - netcmd_semaphore_lock (); - status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed"); - netcmd_semaphore_release (); + netcmd_semaphore_lock (); + status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed"); + netcmd_semaphore_release (); + } #elif defined (TARGET_SOLARIS) /* example: route add -inet6 2001:db8::/32 somegateway 0 */ - /* for some weird reason, this does not work for me unless I set + /* for some reason, routes to tun/tap do not work for me unless I set * "metric 0" - otherwise, the routes will be nicely installed, but - * packets will just disappear somewhere. So we use "0" now... + * packets will just disappear somewhere. So we always use "0" now, + * unless the route points to "gateway on other interface"... + * + * (Note: OpenSolaris can not specify host%interface gateways, so we just + * use the GW addresses - it seems to still work for fe80:: addresses, + * however this is done internally. NUD maybe?) */ - - argv_printf (&argv, "%s add -inet6 %s/%d %s 0", + argv_printf (&argv, "%s add -inet6 %s/%d %s", ROUTE_PATH, network, r6->netbits, gateway ); + /* on tun/tap, not "elsewhere"? -> metric 0 */ + if ( !r6->iface ) + argv_printf_cat (&argv, "0"); + argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed"); @@ -1730,11 +1903,22 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla argv_msg (D_ROUTE, &argv); status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed"); +#elif defined(TARGET_AIX) + + argv_printf (&argv, "%s add -inet6 %s/%d %s", + ROUTE_PATH, + network, r6->netbits, gateway); + argv_msg (D_ROUTE, &argv); + status = openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed"); + #else msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script"); #endif - r6->defined = status; + if (status) + r6->flags |= RT_ADDED; + else + r6->flags &= ~RT_ADDED; argv_reset (&argv); gc_free (&gc); } @@ -1747,7 +1931,7 @@ delete_route (struct route_ipv4 *r, const struct env_set *es) { struct gc_arena gc; - struct argv argv; + struct argv argv = argv_new (); const char *network; const char *netmask; const char *gateway; @@ -1757,7 +1941,6 @@ delete_route (struct route_ipv4 *r, return; gc_init (&gc); - argv_init (&argv); network = print_in_addr_t (r->network, 0, &gc); netmask = print_in_addr_t (r->netmask, 0, &gc); @@ -1772,7 +1955,7 @@ delete_route (struct route_ipv4 *r, argv_printf (&argv, "%s route del %s/%d", iproute_path, network, - count_netmask_bits(netmask)); + netmask_to_netbits2(r->netmask)); #else argv_printf (&argv, "%s del -net %s netmask %s", ROUTE_PATH, @@ -1784,7 +1967,7 @@ delete_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed"); -#elif defined (WIN32) +#elif defined (_WIN32) argv_printf (&argv, "%s%sc DELETE %s MASK %s %s", get_win_sys_path(), @@ -1795,7 +1978,12 @@ delete_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); - if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI) + if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_SERVICE) + { + const bool status = del_route_service (r, tt); + msg (D_ROUTE, "Route deletion via service %s", status ? "succeeded" : "failed"); + } + else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI) { const bool status = del_route_ipapi (r, tt); msg (D_ROUTE, "Route deletion via IPAPI %s", status ? "succeeded" : "failed"); @@ -1889,6 +2077,20 @@ delete_route (struct route_ipv4 *r, argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed"); +#elif defined(TARGET_ANDROID) + msg (M_NONFATAL, "Sorry, deleting routes on Android is not possible. The VpnService API allows routes to be set on connect only."); + +#elif defined(TARGET_AIX) + + { + int netbits = netmask_to_netbits2(r->netmask); + argv_printf (&argv, "%s delete -net %s/%d %s", + ROUTE_PATH, + network, netbits, gateway); + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: AIX route delete command failed"); + } + #else msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script"); #endif @@ -1903,27 +2105,45 @@ void delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) { struct gc_arena gc; - struct argv argv; + struct argv argv = argv_new (); const char *network; const char *gateway; const char *device = tt->actual_name; bool gateway_needed = false; - if (!r6->defined) + if ((r6->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED)) return; +#ifndef _WIN32 + if ( r6->iface != NULL ) /* vpn server special route */ + { + device = r6->iface; + gateway_needed = true; + } +#endif + gc_init (&gc); - argv_init (&argv); - network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc); + network = print_in6_addr( r6->network, 0, &gc); gateway = print_in6_addr( r6->gateway, 0, &gc); - if ( !tt->ipv6 ) +#if defined(TARGET_DARWIN) || \ + defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \ + defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) + + /* the BSD platforms cannot specify gateway and interface independently, + * but for link-local destinations, we MUST specify the interface, so + * we build a combined "$gateway%$interface" gateway string + */ + if ( r6->iface != NULL && gateway_needed && + IN6_IS_ADDR_LINKLOCAL(&r6->gateway) ) /* fe80::...%intf */ { - msg( M_INFO, "delete_route_ipv6(): not deleting %s/%d, no IPv6 on if %s", - network, r6->netbits, device ); - return; + int len = strlen(gateway) + 1 + strlen(r6->iface)+1; + char * tmp = gc_malloc( len, true, &gc ); + snprintf( tmp, len, "%s%%%s", gateway, r6->iface ); + gateway = tmp; } +#endif msg( M_INFO, "delete_route_ipv6(%s/%d)", network, r6->netbits ); @@ -1931,7 +2151,7 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne * delete, otherwise some OSes will refuse to delete the route */ if ( tt->type == DEV_TYPE_TAP && - !(r6->metric_defined && r6->metric == 0 ) ) + !( (r6->flags & RT_METRIC_DEFINED) && r6->metric == 0 ) ) { gateway_needed = true; } @@ -1954,61 +2174,70 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne device); if (gateway_needed) argv_printf_cat (&argv, "gw %s", gateway); - if (r6->metric_defined && r6->metric > 0 ) + if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0 ) argv_printf_cat (&argv, " metric %d", r6->metric); #endif /*ENABLE_IPROUTE*/ argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed"); -#elif defined (WIN32) +#elif defined (_WIN32) - if (win32_version_info() != WIN_XP) + if (tt->options.msg_channel) + del_route_ipv6_service (r6, tt); + else { struct buffer out = alloc_buf_gc (64, &gc); - buf_printf (&out, "interface=%d", tt->adapter_index ); + if ( r6->adapter_index ) /* vpn server special route */ + { + buf_printf (&out, "interface=%d", r6->adapter_index ); + gateway_needed = true; + } + else + { + buf_printf (&out, "interface=%d", tt->adapter_index ); + } device = buf_bptr(&out); - } - /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */ - argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - network, - r6->netbits, - device); - - /* next-hop depends on TUN or TAP mode: - * - in TAP mode, we use the "real" next-hop - * - in TUN mode we use a special-case link-local address that the tapdrvr - * knows about and will answer ND (neighbor discovery) packets for - * (and "route deletion without specifying next-hop" does not work...) - */ - if ( tt->type == DEV_TYPE_TUN ) - argv_printf_cat( &argv, " %s", "fe80::8" ); - else - argv_printf_cat( &argv, " %s", gateway ); + /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */ + argv_printf (&argv, "%s%sc interface ipv6 delete route %s/%d %s", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + network, + r6->netbits, + device); + + /* next-hop depends on TUN or TAP mode: + * - in TAP mode, we use the "real" next-hop + * - in TUN mode we use a special-case link-local address that the tapdrvr + * knows about and will answer ND (neighbor discovery) packets for + * (and "route deletion without specifying next-hop" does not work...) + */ + if ( tt->type == DEV_TYPE_TUN && !gateway_needed ) + argv_printf_cat( &argv, " %s", "fe80::8" ); + else if ( !IN6_IS_ADDR_UNSPECIFIED(&r6->gateway) ) + argv_printf_cat( &argv, " %s", gateway ); #if 0 - if (r->metric_defined) - argv_printf_cat (&argv, "METRIC %d", r->metric); + if (r6->flags & RT_METRIC_DEFINED) + argv_printf_cat (&argv, "METRIC %d", r->metric); #endif - /* Windows XP to 7 "just delete" routes, wherever they came from, but - * in Windows 8(.1?), if you create them with "store=active", this is - * how you should delete them as well (pointed out by Cedric Tabary) - */ - argv_printf_cat( &argv, " store=active" ); + /* Windows XP to 7 "just delete" routes, wherever they came from, but + * in Windows 8(.1?), if you create them with "store=active", this is + * how you should delete them as well (pointed out by Cedric Tabary) + */ + argv_printf_cat( &argv, " store=active" ); - argv_msg (D_ROUTE, &argv); + argv_msg (D_ROUTE, &argv); - netcmd_semaphore_lock (); - openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete ipv6 command failed"); - netcmd_semaphore_release (); + netcmd_semaphore_lock (); + openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete ipv6 command failed"); + netcmd_semaphore_release (); + } #elif defined (TARGET_SOLARIS) /* example: route delete -inet6 2001:db8::/32 somegateway */ - /* GERT-TODO: this is untested, but should work */ argv_printf (&argv, "%s delete -inet6 %s/%d %s", ROUTE_PATH, @@ -2066,6 +2295,14 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne argv_msg (D_ROUTE, &argv); openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route delete -inet6 command failed"); +#elif defined(TARGET_AIX) + + argv_printf (&argv, "%s delete -inet6 %s/%d %s", + ROUTE_PATH, + network, r6->netbits, gateway); + argv_msg (D_ROUTE, &argv); + openvpn_execve_check (&argv, es, 0, "ERROR: AIX route add command failed"); + #else msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script"); #endif @@ -2079,7 +2316,7 @@ delete_route_ipv6 (const struct route_ipv6 *r6, const struct tuntap *tt, unsigne * to get the current default gateway. */ -#if defined(WIN32) +#if defined(_WIN32) static const MIB_IPFORWARDTABLE * get_windows_routing_table (struct gc_arena *gc) @@ -2148,6 +2385,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt) int count = 0; int good = 0; int ambig = 0; + int len = -1; bool adapter_up = false; if (is_adapter_up (tt, adapters)) @@ -2157,9 +2395,9 @@ test_routes (const struct route_list *rl, const struct tuntap *tt) if (rl) { - int i; - for (i = 0; i < rl->n; ++i) - test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway); + struct route_ipv4 *r; + for (r = rl->routes, len = 0; r; r = r->next, ++len) + test_route_helper (&ret, &count, &good, &ambig, adapters, r->gateway); if ((rl->flags & RG_ENABLE) && (rl->spec.flags & RTSA_REMOTE_ENDPOINT)) test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint); @@ -2169,7 +2407,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt) msg (D_ROUTE, "TEST ROUTES: %d/%d succeeded len=%d ret=%d a=%d u/d=%s", good, count, - rl ? rl->n : -1, + len, (int)ret, ambig, adapter_up ? "up" : "down"); @@ -2301,6 +2539,81 @@ windows_route_find_if_index (const struct route_ipv4 *r, const struct tuntap *tt return ret; } +/* IPv6 implementation using GetBestRoute2() + * (TBD: dynamic linking so the binary can still run on XP?) + * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365922(v=vs.85).aspx + * https://msdn.microsoft.com/en-us/library/windows/desktop/aa814411(v=vs.85).aspx + */ +void +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest) +{ + struct gc_arena gc = gc_new (); + MIB_IPFORWARD_ROW2 BestRoute; + SOCKADDR_INET DestinationAddress, BestSourceAddress; + DWORD BestIfIndex; + DWORD status; + NET_LUID InterfaceLuid; + + CLEAR(*rgi6); + CLEAR(InterfaceLuid); // cleared = not used for lookup + CLEAR(DestinationAddress); + + DestinationAddress.si_family = AF_INET6; + if ( dest ) + { + DestinationAddress.Ipv6.sin6_addr = *dest; + } + + status = GetBestInterfaceEx( &DestinationAddress, &BestIfIndex ); + + if (status != NO_ERROR) + { + msg (D_ROUTE, "NOTE: GetBestInterfaceEx returned error: %s (code=%u)", + strerror_win32 (status, &gc), + (unsigned int)status); + goto done; + } + + msg( D_ROUTE, "GetBestInterfaceEx() returned if=%d", (int) BestIfIndex ); + + status = GetBestRoute2( &InterfaceLuid, BestIfIndex, NULL, + &DestinationAddress, 0, + &BestRoute, &BestSourceAddress ); + + if (status != NO_ERROR) + { + msg (D_ROUTE, "NOTE: GetIpForwardEntry2 returned error: %s (code=%u)", + strerror_win32 (status, &gc), + (unsigned int)status); + goto done; + } + + msg( D_ROUTE, "GDG6: II=%d DP=%s/%d NH=%s", + BestRoute.InterfaceIndex, + print_in6_addr( BestRoute.DestinationPrefix.Prefix.Ipv6.sin6_addr, 0, &gc), + BestRoute.DestinationPrefix.PrefixLength, + print_in6_addr( BestRoute.NextHop.Ipv6.sin6_addr, 0, &gc) ); + msg( D_ROUTE, "GDG6: Metric=%d, Loopback=%d, AA=%d, I=%d", + (int) BestRoute.Metric, + (int) BestRoute.Loopback, + (int) BestRoute.AutoconfigureAddress, + (int) BestRoute.Immortal ); + + rgi6->gateway.addr_ipv6 = BestRoute.NextHop.Ipv6.sin6_addr; + rgi6->adapter_index = BestRoute.InterfaceIndex; + rgi6->flags |= RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + + /* on-link is signalled by receiving an empty (::) NextHop */ + if ( IN6_IS_ADDR_UNSPECIFIED(&BestRoute.NextHop.Ipv6.sin6_addr) ) + { + rgi6->flags |= RGI_ON_LINK; + } + + done: + gc_free (&gc); +} + bool add_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt, DWORD adapter_index) { @@ -2407,6 +2720,126 @@ del_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt) return ret; } +static bool +do_route_service (const bool add, const route_message_t *rt, const size_t size, HANDLE pipe) +{ + DWORD len; + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new (); + + if (!WriteFile (pipe, rt, size, &len, NULL) || + !ReadFile (pipe, &ack, sizeof (ack), &len, NULL)) + { + msg (M_WARN, "ROUTE: could not talk to service: %s [%lu]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + goto out; + } + + if (ack.error_number != NO_ERROR) + { + msg (M_WARN, "ROUTE: route %s failed using service: %s [status=%u if_index=%lu]", + (add ? "addition" : "deletion"), strerror_win32 (ack.error_number, &gc), + ack.error_number, rt->iface.index); + goto out; + } + + ret = true; + +out: + gc_free (&gc); + return ret; +} + +static bool +do_route_ipv4_service (const bool add, const struct route_ipv4 *r, const struct tuntap *tt) +{ + DWORD if_index = windows_route_find_if_index (r, tt); + if (if_index == ~0) + return false; + + route_message_t msg = { + .header = { + (add ? msg_add_route : msg_del_route), + sizeof (route_message_t), + 0 }, + .family = AF_INET, + .prefix.ipv4.s_addr = htonl(r->network), + .gateway.ipv4.s_addr = htonl(r->gateway), + .iface = { .index = if_index, .name = "" }, + .metric = (r->flags & RT_METRIC_DEFINED ? r->metric : -1) + }; + + netmask_to_netbits (r->network, r->netmask, &msg.prefix_len); + if (msg.prefix_len == -1) + msg.prefix_len = 32; + + return do_route_service (add, &msg, sizeof (msg), tt->options.msg_channel); +} + +static bool +do_route_ipv6_service (const bool add, const struct route_ipv6 *r, const struct tuntap *tt) +{ + bool status; + route_message_t msg = { + .header = { + (add ? msg_add_route : msg_del_route), + sizeof (route_message_t), + 0 }, + .family = AF_INET6, + .prefix.ipv6 = r->network, + .prefix_len = r->netbits, + .gateway.ipv6 = r->gateway, + .iface = { .index = tt->adapter_index, .name = "" }, + .metric = ( (r->flags & RT_METRIC_DEFINED) ? r->metric : -1) + }; + + if ( r->adapter_index ) /* vpn server special route */ + msg.iface.index = r->adapter_index; + + /* In TUN mode we use a special link-local address as the next hop. + * The tapdrvr knows about it and will answer neighbor discovery packets. + */ + if (tt->type == DEV_TYPE_TUN) + inet_pton (AF_INET6, "fe80::8", &msg.gateway.ipv6); + + if (msg.iface.index == TUN_ADAPTER_INDEX_INVALID) + { + strncpy (msg.iface.name, tt->actual_name, sizeof (msg.iface.name)); + msg.iface.name[sizeof (msg.iface.name) - 1] = '\0'; + } + + status = do_route_service (add, &msg, sizeof (msg), tt->options.msg_channel); + msg (D_ROUTE, "IPv6 route %s via service %s", + add ? "addition" : "deletion", + status ? "succeeded" : "failed"); + return status; +} + +static bool +add_route_service (const struct route_ipv4 *r, const struct tuntap *tt) +{ + return do_route_ipv4_service (true, r, tt); +} + +static bool +del_route_service (const struct route_ipv4 *r, const struct tuntap *tt) +{ + return do_route_ipv4_service (false, r, tt); +} + +static bool +add_route_ipv6_service (const struct route_ipv6 *r, const struct tuntap *tt) +{ + return do_route_ipv6_service (true, r, tt); +} + +static bool +del_route_ipv6_service (const struct route_ipv6 *r, const struct tuntap *tt) +{ + return do_route_ipv6_service (false, r, tt); +} + static const char * format_route_entry (const MIB_IPFORWARDROW *r, struct gc_arena *gc) { @@ -2451,7 +2884,7 @@ show_routes (int msglev) gc_free (&gc); } -#elif defined(TARGET_LINUX) +#elif defined(TARGET_LINUX) || defined(TARGET_ANDROID) void get_default_gateway (struct route_gateway_info *rgi) @@ -2463,6 +2896,7 @@ get_default_gateway (struct route_gateway_info *rgi) CLEAR(*rgi); +#ifndef TARGET_ANDROID /* get default gateway IP addr */ { FILE *fp = fopen ("/proc/net/route", "r"); @@ -2519,6 +2953,19 @@ get_default_gateway (struct route_gateway_info *rgi) } } } +#else + /* Android, set some pseudo GW, addr is in host byte order, + * Determining the default GW on Android 5.0+ is non trivial + * and serves almost no purpose since OpenVPN only uses the + * default GW address to add routes for networks that should + * NOT be routed over the VPN. Using a well known address + * (127.'d'.'g'.'w') for the default GW make detecting + * these routes easier from the controlling app. + */ + rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w'; + rgi->flags |= RGI_ADDR_DEFINED; + strcpy(best_name, "android-gw"); +#endif /* scan adapter list */ if (rgi->flags & RGI_ADDR_DEFINED) @@ -2617,136 +3064,149 @@ get_default_gateway (struct route_gateway_info *rgi) gc_free (&gc); } -#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||defined(TARGET_SOLARIS) - -#include -#include -#include -#include - -struct { - struct rt_msghdr m_rtm; - char m_space[512]; -} m_rtmsg; - -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) - -/* - * FIXME -- add support for netmask, hwaddr, and iface +/* IPv6 implementation using netlink + * http://www.linuxjournal.com/article/7356 + * netlink(3), netlink(7), rtnetlink(7) + * http://www.virtualbox.org/svn/vbox/trunk/src/VBox/NetworkServices/NAT/rtmon_linux.c */ -void -get_default_gateway (struct route_gateway_info *rgi) -{ - struct gc_arena gc = gc_new (); - int s, seq, l, pid, rtm_addrs; - unsigned int i; - struct sockaddr so_dst, so_mask; - char *cp = m_rtmsg.m_space; - struct sockaddr *gate = NULL, *sa; - struct rt_msghdr *rtm_aux; - -#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) - -#define NEXTADDR(w, u) \ - if (rtm_addrs & (w)) {\ - l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\ - } - -#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) - -#else /* TARGET_SOLARIS */ - -#define NEXTADDR(w, u) \ - if (rtm_addrs & (w)) {\ - l = ROUNDUP(sizeof(struct sockaddr_in)); memmove(cp, &(u), l); cp += l;\ - } - -#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in))) +struct rtreq { + struct nlmsghdr nh; + struct rtmsg rtm; + char attrbuf[512]; +}; -#endif +void +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest) +{ + int nls = -1; + struct rtreq rtreq; + struct rtattr *rta; + + char rtbuf[2000]; + ssize_t ssize; + + CLEAR(*rgi6); + + nls = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); + if ( nls < 0 ) + { msg(M_WARN|M_ERRNO, "GDG6: socket() failed" ); goto done; } + + /* bind() is not needed, no unsolicited msgs coming in */ + + /* request best matching route, see netlink(7) for explanations + */ + CLEAR(rtreq); + rtreq.nh.nlmsg_type = RTM_GETROUTE; + rtreq.nh.nlmsg_flags = NLM_F_REQUEST; /* best match only */ + rtreq.rtm.rtm_family = AF_INET6; + rtreq.rtm.rtm_src_len = 0; /* not source dependent */ + rtreq.rtm.rtm_dst_len = 128; /* exact dst */ + rtreq.rtm.rtm_table = RT_TABLE_MAIN; + rtreq.rtm.rtm_protocol = RTPROT_UNSPEC; + rtreq.nh.nlmsg_len = NLMSG_SPACE(sizeof(rtreq.rtm)); + + /* set RTA_DST for target IPv6 address we want */ + rta = (struct rtattr *)(((char *) &rtreq)+NLMSG_ALIGN(rtreq.nh.nlmsg_len)); + rta->rta_type = RTA_DST; + rta->rta_len = RTA_LENGTH(16); + rtreq.nh.nlmsg_len = NLMSG_ALIGN(rtreq.nh.nlmsg_len) + + RTA_LENGTH(16); + + if ( dest == NULL ) /* ::, unspecified */ + memset( RTA_DATA(rta), 0, 16 ); /* :: = all-zero */ + else + memcpy( RTA_DATA(rta), (void *)dest, 16 ); + /* send and receive reply */ + if ( send( nls, &rtreq, rtreq.nh.nlmsg_len, 0 ) < 0 ) + { msg(M_WARN|M_ERRNO, "GDG6: send() failed" ); goto done; } -#define rtm m_rtmsg.m_rtm + ssize = recv(nls, rtbuf, sizeof(rtbuf), MSG_TRUNC); - CLEAR(*rgi); + if (ssize < 0) + { msg(M_WARN|M_ERRNO, "GDG6: recv() failed" ); goto done; } - pid = getpid(); - seq = 0; - rtm_addrs = RTA_DST | RTA_NETMASK; + if (ssize > sizeof(rtbuf)) + { + msg(M_WARN, "get_default_gateway_ipv6: returned message too big for buffer (%d>%d)", (int)ssize, (int)sizeof(rtbuf) ); + goto done; + } - bzero(&so_dst, sizeof(so_dst)); - bzero(&so_mask, sizeof(so_mask)); - bzero(&rtm, sizeof(struct rt_msghdr)); + struct nlmsghdr *nh; - rtm.rtm_type = RTM_GET; - rtm.rtm_flags = RTF_UP | RTF_GATEWAY; - rtm.rtm_version = RTM_VERSION; - rtm.rtm_seq = ++seq; - rtm.rtm_addrs = rtm_addrs; + for (nh = (struct nlmsghdr *)rtbuf; + NLMSG_OK(nh, ssize); + nh = NLMSG_NEXT(nh, ssize)) + { + struct rtmsg *rtm; + int attrlen; - so_dst.sa_family = AF_INET; - so_mask.sa_family = AF_INET; + if (nh->nlmsg_type == NLMSG_DONE) { break; } -#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) - so_dst.sa_len = sizeof(struct sockaddr_in); - so_mask.sa_len = sizeof(struct sockaddr_in); -#endif + if (nh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh); + msg(M_WARN, "GDG6: NLSMG_ERROR: error %d\n", ne->error); + break; + } - NEXTADDR(RTA_DST, so_dst); - NEXTADDR(RTA_NETMASK, so_mask); + if (nh->nlmsg_type != RTM_NEWROUTE) { + /* shouldn't happen */ + msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type ); + continue; + } - rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + rtm = (struct rtmsg *)NLMSG_DATA(nh); + attrlen = RTM_PAYLOAD(nh); - s = socket(PF_ROUTE, SOCK_RAW, 0); + /* we're only looking for routes in the main table, as "we have + * no IPv6" will lead to a lookup result in "Local" (::/0 reject) + */ + if (rtm->rtm_family != AF_INET6 || + rtm->rtm_table != RT_TABLE_MAIN) + { continue; } /* we're not interested */ - if (write(s, (char *)&m_rtmsg, l) < 0) - { - msg(M_WARN|M_ERRNO, "Could not retrieve default gateway from route socket:"); - gc_free (&gc); - close(s); - return; + for (rta = RTM_RTA(rtm); + RTA_OK(rta, attrlen); + rta = RTA_NEXT(rta, attrlen)) + { + if (rta->rta_type == RTA_GATEWAY) { + if ( RTA_PAYLOAD(rta) != sizeof(struct in6_addr) ) + { msg(M_WARN, "GDG6: RTA_GW size mismatch"); continue; } + rgi6->gateway.addr_ipv6 = *(struct in6_addr*) RTA_DATA(rta); + rgi6->flags |= RGI_ADDR_DEFINED; + } + else if (rta->rta_type == RTA_OIF) { + char ifname[IF_NAMESIZE+1]; + int oif; + if ( RTA_PAYLOAD(rta) != sizeof(oif) ) + { msg(M_WARN, "GDG6: oif size mismatch"); continue; } + + memcpy(&oif, RTA_DATA(rta), sizeof(oif)); + if_indextoname(oif,ifname); + strncpy( rgi6->iface, ifname, sizeof(rgi6->iface)-1 ); + rgi6->flags |= RGI_IFACE_DEFINED; + } + } } - do { - l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); - } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); - - close(s); - - rtm_aux = &rtm; - - 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); - } - } - else + /* if we have an interface but no gateway, the destination is on-link */ + if ( ( rgi6->flags & (RGI_IFACE_DEFINED|RGI_ADDR_DEFINED) ) == + RGI_IFACE_DEFINED ) { - gc_free (&gc); - return; + rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK); + if ( dest ) + rgi6->gateway.addr_ipv6 = *dest; } - - if (gate != NULL ) - { - rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr); - rgi->flags |= RGI_ADDR_DEFINED; - - gc_free (&gc); - } - else - { - gc_free (&gc); - } + done: + if (nls >= 0) + close (nls); } -#elif defined(TARGET_DARWIN) +#elif defined(TARGET_DARWIN) || defined(TARGET_SOLARIS) || \ + defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) || \ + defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) #include #include @@ -2759,15 +3219,41 @@ struct rtmsg { char m_space[512]; }; -#define ROUNDUP(a) \ +/* the route socket code is identical for all 4 supported BSDs and for + * MacOS X (Darwin), with one crucial difference: when going from + * 32 bit to 64 bit, the BSDs increased the structure size but kept + * source code compatibility by keeping the use of "long", while + * MacOS X decided to keep binary compatibility by *changing* the API + * to use "uint32_t", thus 32 bit on all OS X variants + * + * We used to have a large amount of duplicate code here which really + * differed only in this (long) vs. (uint32_t) - IMHO, worse than + * having a combined block for all BSDs with this single #ifdef inside + */ + +#if defined(TARGET_DARWIN) +# define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) +#else +# define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#endif + +#if defined(TARGET_SOLARIS) +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(sizeof(u)); memmove(cp, &(u), l); cp += l;\ + } +#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in))) +#else #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) {\ - l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\ + l = ROUNDUP( ((struct sockaddr *)&(u))->sa_len); memmove(cp, &(u), l); cp += l;\ } #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) +#endif #define max(a,b) ((a) > (b) ? (a) : (b)) @@ -2805,9 +3291,12 @@ get_default_gateway (struct route_gateway_info *rgi) rtm.rtm_addrs = rtm_addrs; so_dst.sa_family = AF_INET; - so_dst.sa_len = sizeof(struct sockaddr_in); so_mask.sa_family = AF_INET; + +#ifndef TARGET_SOLARIS + so_dst.sa_len = sizeof(struct sockaddr_in); so_mask.sa_len = sizeof(struct sockaddr_in); +#endif NEXTADDR(RTA_DST, so_dst); NEXTADDR(RTA_NETMASK, so_mask); @@ -2865,7 +3354,6 @@ get_default_gateway (struct route_gateway_info *rgi) { /* get interface name */ const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp; - int len = adl->sdl_nlen; if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi->iface)) { memcpy (rgi->iface, adl->sdl_data, adl->sdl_nlen); @@ -2932,7 +3420,12 @@ get_default_gateway (struct route_gateway_info *rgi) for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); ) { ifr = (struct ifreq *)cp; +#if defined(TARGET_SOLARIS) + const size_t len = sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr); +#else const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len); +#endif + if (!ifr->ifr_addr.sa_family) break; if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ)) @@ -2954,121 +3447,162 @@ get_default_gateway (struct route_gateway_info *rgi) gc_free (&gc); } -#undef max - -#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) - -#include -#include -#include -#include - -struct { - struct rt_msghdr m_rtm; - char m_space[512]; -} m_rtmsg; - -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +/* BSD implementation using routing socket (as does IPv4) + * (the code duplication is somewhat unavoidable if we want this to + * work on OpenSolaris as well. *sigh*) + */ -/* - * FIXME -- add support for netmask, hwaddr, and iface +/* Solaris has no length field - this is ugly, but less #ifdef in total */ +#if defined(TARGET_SOLARIS) +# undef ADVANCE +# define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in6))) +#endif + void -get_default_gateway (struct route_gateway_info *rgi) +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest) { - struct gc_arena gc = gc_new (); - int s, seq, l, rtm_addrs; - unsigned int i; - pid_t pid; - struct sockaddr so_dst, so_mask; - char *cp = m_rtmsg.m_space; - struct sockaddr *gate = NULL, *sa; - struct rt_msghdr *rtm_aux; -#define NEXTADDR(w, u) \ - if (rtm_addrs & (w)) {\ - l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\ - } + struct rtmsg m_rtmsg; + int sockfd = -1; + int seq, l, pid, rtm_addrs; + unsigned int i; + struct sockaddr_in6 so_dst, so_mask; + char *cp = m_rtmsg.m_space; + struct sockaddr *gate = NULL, *ifp = NULL, *sa; + struct rt_msghdr *rtm_aux; -#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + CLEAR(*rgi6); -#define rtm m_rtmsg.m_rtm + /* setup data to send to routing socket */ + pid = getpid(); + seq = 0; + rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP; - CLEAR(*rgi); + bzero(&m_rtmsg, sizeof(m_rtmsg)); + bzero(&so_dst, sizeof(so_dst)); + bzero(&so_mask, sizeof(so_mask)); + bzero(&rtm, sizeof(struct rt_msghdr)); - pid = getpid(); - seq = 0; - rtm_addrs = RTA_DST | RTA_NETMASK; + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = RTF_UP; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; - bzero(&so_dst, sizeof(so_dst)); - bzero(&so_mask, sizeof(so_mask)); - bzero(&rtm, sizeof(struct rt_msghdr)); + so_dst.sin6_family = AF_INET6; + so_mask.sin6_family = AF_INET6; - rtm.rtm_type = RTM_GET; - rtm.rtm_flags = RTF_UP | RTF_GATEWAY; - rtm.rtm_version = RTM_VERSION; - rtm.rtm_seq = ++seq; - rtm.rtm_addrs = rtm_addrs; + if ( dest != NULL && /* specific host? */ + !IN6_IS_ADDR_UNSPECIFIED(dest) ) + { + so_dst.sin6_addr = *dest; + /* :: needs /0 "netmask", host route wants "no netmask */ + rtm_addrs &= ~RTA_NETMASK; + } - so_dst.sa_family = AF_INET; - so_dst.sa_len = sizeof(struct sockaddr_in); - so_mask.sa_family = AF_INET; - so_mask.sa_len = sizeof(struct sockaddr_in); + rtm.rtm_addrs = rtm_addrs; - NEXTADDR(RTA_DST, so_dst); - NEXTADDR(RTA_NETMASK, so_mask); +#ifndef TARGET_SOLARIS + so_dst.sin6_len = sizeof(struct sockaddr_in6); + so_mask.sin6_len = sizeof(struct sockaddr_in6); +#endif - rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_NETMASK, so_mask); - s = socket(PF_ROUTE, SOCK_RAW, 0); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; - if (write(s, (char *)&m_rtmsg, l) < 0) + /* transact with routing socket */ + sockfd = socket(PF_ROUTE, SOCK_RAW, 0); + if (sockfd < 0) { - msg(M_WARN|M_ERRNO, "Could not retrieve default gateway from route socket:"); - gc_free (&gc); - close(s); - return; + msg (M_WARN, "GDG6: socket #1 failed"); + goto done; + } + if (write(sockfd, (char *)&m_rtmsg, l) < 0) + { + msg (M_WARN, "GDG6: problem writing to routing socket"); + goto done; } - do { - l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); - } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); - - close(s); - - rtm_aux = &rtm; - - 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); - } - } - else + do { - gc_free (&gc); - return; + l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg)); } + while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + close(sockfd); + sockfd = -1; - if (gate != NULL ) + /* extract return data from routing socket */ + rtm_aux = &rtm; + cp = ((char *)(rtm_aux + 1)); + if (rtm_aux->rtm_addrs) { - rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr); - rgi->flags |= RGI_ADDR_DEFINED; - - gc_free (&gc); + for (i = 1; i; i <<= 1) + { + 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 + else + goto done; + + /* get gateway addr and interface name */ + if (gate != NULL ) { - gc_free (&gc); + struct sockaddr_in6 * s6 = (struct sockaddr_in6 *)gate; + struct in6_addr gw = s6->sin6_addr; + +#ifndef TARGET_SOLARIS + /* You do not really want to know... from FreeBSD's route.c + * (KAME encodes the 16 bit scope_id in s6_addr[2] + [3], + * but for a correct link-local address these must be :0000: ) + */ + if ( gate->sa_len == sizeof(struct sockaddr_in6) && + IN6_IS_ADDR_LINKLOCAL(&gw) ) + { + gw.s6_addr[2] = gw.s6_addr[3] = 0; + } + + if ( gate->sa_len != sizeof(struct sockaddr_in6) || + IN6_IS_ADDR_UNSPECIFIED(&gw) ) + { + rgi6->flags |= RGI_ON_LINK; + } + else +#endif + + rgi6->gateway.addr_ipv6 = gw; + rgi6->flags |= RGI_ADDR_DEFINED; + + if (ifp) + { + /* get interface name */ + const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp; + if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi6->iface)) + { + memcpy (rgi6->iface, adl->sdl_data, adl->sdl_nlen); + rgi6->flags |= RGI_IFACE_DEFINED; + } + } } + + done: + if (sockfd >= 0) + close(sockfd); } +#undef max + #else /* @@ -3100,6 +3634,13 @@ get_default_gateway (struct route_gateway_info *rgi) { CLEAR(*rgi); } +void +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest) +{ + msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system"); + CLEAR(*rgi6); +} #endif @@ -3127,13 +3668,33 @@ netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbi return false; } +/* similar to netmask_to_netbits(), but don't mess with base address + * etc., just convert to netbits - non-mappable masks are returned as "-1" + */ +int netmask_to_netbits2 (in_addr_t netmask) +{ + int i; + const int addrlen = sizeof (in_addr_t) * 8; + + for (i = 0; i <= addrlen; ++i) + { + in_addr_t mask = netbits_to_netmask (i); + if (mask == netmask) + { + return i; + } + } + return -1; +} + + /* * get_bypass_addresses() is used by the redirect-gateway bypass-x * functions to build a route bypass to selected DHCP/DNS servers, * so that outgoing packets to these servers don't end up in the tunnel. */ -#if defined(WIN32) +#if defined(_WIN32) static void add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr) @@ -3207,7 +3768,7 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA * Used by redirect-gateway autolocal feature */ -#if defined(WIN32) +#if defined(_WIN32) int test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi) diff --git a/src/openvpn/route.h b/src/openvpn/route.h index fe9b461..c358681 100644 --- a/src/openvpn/route.h +++ b/src/openvpn/route.h @@ -33,15 +33,14 @@ #include "tun.h" #include "misc.h" -#define MAX_ROUTES_DEFAULT 100 - -#ifdef WIN32 +#ifdef _WIN32 /* * Windows route methods */ #define ROUTE_METHOD_ADAPTIVE 0 /* try IP helper first then route.exe */ #define ROUTE_METHOD_IPAPI 1 /* use IP helper API */ #define ROUTE_METHOD_EXE 2 /* use route.exe */ +#define ROUTE_METHOD_SERVICE 3 /* use the privileged Windows service */ #define ROUTE_METHOD_MASK 3 #endif @@ -74,6 +73,7 @@ struct route_special_addr }; struct route_option { + struct route_option *next; const char *network; const char *netmask; const char *gateway; @@ -92,28 +92,28 @@ struct route_option { struct route_option_list { unsigned int flags; /* RG_x flags */ - int capacity; - int n; - struct route_option routes[EMPTY_ARRAY_SIZE]; + struct route_option *routes; + struct gc_arena *gc; }; struct route_ipv6_option { + struct route_ipv6_option *next; const char *prefix; /* e.g. "2001:db8:1::/64" */ const char *gateway; /* e.g. "2001:db8:0::2" */ const char *metric; /* e.g. "5" */ }; struct route_ipv6_option_list { - unsigned int flags; - int capacity; - int n; - struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE]; + unsigned int flags; /* RG_x flags, see route_option-list */ + struct route_ipv6_option *routes_ipv6; + struct gc_arena *gc; }; struct route_ipv4 { # define RT_DEFINED (1<<0) # define RT_ADDED (1<<1) # define RT_METRIC_DEFINED (1<<2) + struct route_ipv4 *next; unsigned int flags; const struct route_option *option; in_addr_t network; @@ -123,26 +123,18 @@ struct route_ipv4 { }; struct route_ipv6 { - bool defined; + struct route_ipv6 *next; + unsigned int flags; /* RT_ flags, see route_ipv4 */ struct in6_addr network; unsigned int netbits; struct in6_addr gateway; - bool metric_defined; int metric; -}; - -struct route_ipv6_list { - bool routes_added; - unsigned int flags; - int default_metric; - bool default_metric_defined; - struct in6_addr remote_endpoint_ipv6; - bool remote_endpoint_defined; - bool did_redirect_default_gateway; /* TODO (?) */ - bool did_local; /* TODO (?) */ - int capacity; - int n; - struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE]; + /* gateway interface */ +# ifdef _WIN32 + DWORD adapter_index; /* interface or ~0 if undefined */ +#else + char * iface; /* interface name (null terminated) */ +#endif }; @@ -161,7 +153,7 @@ struct route_gateway_info { unsigned int flags; /* gateway interface */ -# ifdef WIN32 +# ifdef _WIN32 DWORD adapter_index; /* interface or ~0 if undefined */ #else char iface[16]; /* interface name (null terminated), may be empty */ @@ -179,6 +171,34 @@ struct route_gateway_info { struct route_gateway_address addrs[RGI_N_ADDRESSES]; /* local addresses attached to iface */ }; +struct route_ipv6_gateway_address { + struct in6_addr addr_ipv6; + int netbits_ipv6; +}; + +struct route_ipv6_gateway_info { +/* RGI_ flags used as in route_gateway_info */ + unsigned int flags; + + /* gateway interface */ +# ifdef _WIN32 + DWORD adapter_index; /* interface or ~0 if undefined */ +#else + char iface[16]; /* interface name (null terminated), may be empty */ +#endif + + /* gateway interface hardware address */ + uint8_t hwaddr[6]; + + /* gateway/router address */ + struct route_ipv6_gateway_address gateway; + + /* address/netmask pairs bound to interface */ +# define RGI_N_ADDRESSES 8 + int n_addrs; /* len of addrs, may be 0 */ + struct route_ipv6_gateway_address addrs[RGI_N_ADDRESSES]; /* local addresses attached to iface */ +}; + struct route_list { # define RL_DID_REDIRECT_DEFAULT_GATEWAY (1<<0) # define RL_DID_LOCAL (1<<1) @@ -188,9 +208,22 @@ struct route_list { struct route_special_addr spec; struct route_gateway_info rgi; unsigned int flags; /* RG_x flags */ - int capacity; - int n; - struct route_ipv4 routes[EMPTY_ARRAY_SIZE]; + struct route_ipv4 *routes; + struct gc_arena gc; +}; + +struct route_ipv6_list { + unsigned int iflags; /* RL_ flags, see route_list */ + + unsigned int spec_flags; /* RTSA_ flags, route_special_addr */ + struct in6_addr remote_endpoint_ipv6; /* inside tun */ + struct in6_addr remote_host_ipv6; /* --remote address */ + int default_metric; + + struct route_ipv6_gateway_info rgi6; + unsigned int flags; /* RG_x flags, see route_option_list */ + struct route_ipv6 *routes_ipv6; + struct gc_arena gc; }; #if P2MP @@ -208,17 +241,15 @@ struct iroute_ipv6 { }; #endif -struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a); -struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a); +struct route_option_list *new_route_option_list (struct gc_arena *a); +struct route_ipv6_option_list *new_route_ipv6_option_list (struct gc_arena *a); struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a); struct route_ipv6_option_list *clone_route_ipv6_option_list (const struct route_ipv6_option_list *src, struct gc_arena *a); -void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src); +void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src, struct gc_arena *a); void copy_route_ipv6_option_list (struct route_ipv6_option_list *dest, - const struct route_ipv6_option_list *src); - -struct route_list *new_route_list (const int max_routes, struct gc_arena *a); -struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a); + const struct route_ipv6_option_list *src, + struct gc_arena *a); void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); @@ -251,6 +282,7 @@ bool init_route_ipv6_list (struct route_ipv6_list *rl6, const struct route_ipv6_option_list *opt6, const char *remote_endpoint, int default_metric, + const struct in6_addr *remote_host, struct env_set *es); void route_list_add_vpn_gateway (struct route_list *rl, @@ -277,7 +309,11 @@ void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6); bool is_special_addr (const char *addr_str); void get_default_gateway (struct route_gateway_info *rgi); -void print_default_gateway(const int msglevel, const struct route_gateway_info *rgi); +void get_default_gateway_ipv6 (struct route_ipv6_gateway_info *rgi, + const struct in6_addr *dest); +void print_default_gateway(const int msglevel, + const struct route_gateway_info *rgi, + const struct route_ipv6_gateway_info *rgi6); /* * Test if addr is reachable via a local interface (return ILA_LOCAL), @@ -297,7 +333,7 @@ void print_route_options (const struct route_option_list *rol, void print_routes (const struct route_list *rl, int level); -#ifdef WIN32 +#ifdef _WIN32 void show_routes (int msglev); bool test_routes (const struct route_list *rl, const struct tuntap *tt); @@ -309,6 +345,7 @@ static inline bool test_routes (const struct route_list *rl, const struct tuntap #endif bool netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbits); +int netmask_to_netbits2 (in_addr_t netmask); static inline in_addr_t netbits_to_netmask (const int netbits) diff --git a/src/openvpn/session_id.c b/src/openvpn/session_id.c index 2e07b54..0ebff65 100644 --- a/src/openvpn/session_id.c +++ b/src/openvpn/session_id.c @@ -39,7 +39,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #include "error.h" #include "common.h" @@ -64,4 +64,4 @@ session_id_print (const struct session_id *sid, struct gc_arena *gc) #else static void dummy(void) {} -#endif /* ENABLE_CRYPTO && ENABLE_SSL*/ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/session_id.h b/src/openvpn/session_id.h index 33909dd..2a1f41f 100644 --- a/src/openvpn/session_id.h +++ b/src/openvpn/session_id.h @@ -30,7 +30,7 @@ * negotiated). */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #ifndef SESSION_ID_H #define SESSION_ID_H @@ -83,4 +83,4 @@ void session_id_random (struct session_id *sid); const char *session_id_print (const struct session_id *sid, struct gc_arena *gc); #endif /* SESSION_ID_H */ -#endif /* ENABLE_CRYPTO && ENABLE_SSL */ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/shaper.h b/src/openvpn/shaper.h index afeb9c3..fa132c4 100644 --- a/src/openvpn/shaper.h +++ b/src/openvpn/shaper.h @@ -75,7 +75,7 @@ bool shaper_soonest_event (struct timeval *tv, int delay); static inline void shaper_reset (struct shaper *s, int bytes_per_second) { - s->bytes_per_second = bytes_per_second ? constrain_int (bytes_per_second, SHAPER_MIN, SHAPER_MAX) : 0; + s->bytes_per_second = constrain_int (bytes_per_second, SHAPER_MIN, SHAPER_MAX); #ifdef SHAPER_USE_FP s->factor = 1000000.0 / (double)s->bytes_per_second; diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c index 0ebde24..0ff1437 100644 --- a/src/openvpn/sig.c +++ b/src/openvpn/sig.c @@ -97,14 +97,14 @@ void throw_signal (const int signum) { siginfo_static.signal_received = signum; - siginfo_static.hard = true; + siginfo_static.source = SIG_SOURCE_HARD; } void throw_signal_soft (const int signum, const char *signal_text) { siginfo_static.signal_received = signum; - siginfo_static.hard = false; + siginfo_static.source = SIG_SOURCE_SOFT; siginfo_static.signal_text = signal_text; } @@ -115,7 +115,7 @@ signal_reset (struct signal_info *si) { si->signal_received = 0; si->signal_text = NULL; - si->hard = false; + si->source = SIG_SOURCE_SOFT; } } @@ -124,9 +124,23 @@ print_signal (const struct signal_info *si, const char *title, int msglevel) { if (si) { - const char *hs = (si->hard ? "hard" : "soft"); const char *type = (si->signal_text ? si->signal_text : ""); const char *t = (title ? title : "process"); + const char *hs = NULL; + switch (si->source) + { + case SIG_SOURCE_SOFT: + hs= "soft"; + break; + case SIG_SOURCE_HARD: + hs = "hard"; + break; + case SIG_SOURCE_CONNECTION_FAILED: + hs = "connection failed(soft)"; + break; + default: + ASSERT(0); + } switch (si->signal_received) { @@ -175,8 +189,10 @@ signal_restart_status (const struct signal_info *si) management_set_state (management, state, si->signal_text ? si->signal_text : signal_name (si->signal_received, true), - (in_addr_t)0, - (in_addr_t)0); + NULL, + NULL, + NULL, + NULL); } #endif } @@ -205,7 +221,7 @@ static int signal_mode; /* GLOBAL */ void pre_init_signal_catch (void) { -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_SIGNAL_H signal_mode = SM_PRE_INIT; signal (SIGINT, signal_handler); @@ -215,13 +231,13 @@ pre_init_signal_catch (void) signal (SIGUSR2, SIG_IGN); signal (SIGPIPE, SIG_IGN); #endif /* HAVE_SIGNAL_H */ -#endif /* WIN32 */ +#endif /* _WIN32 */ } void post_init_signal_catch (void) { -#ifndef WIN32 +#ifndef _WIN32 #ifdef HAVE_SIGNAL_H signal_mode = SM_POST_INIT; signal (SIGINT, signal_handler); @@ -265,9 +281,9 @@ print_status (const struct context *c, struct status_output *so) status_printf (so, "TCP/UDP read bytes," counter_format, c->c2.link_read_bytes); status_printf (so, "TCP/UDP write bytes," counter_format, c->c2.link_write_bytes); status_printf (so, "Auth read bytes," counter_format, c->c2.link_read_bytes_auth); -#ifdef ENABLE_LZO - if (lzo_defined (&c->c2.lzo_compwork)) - lzo_print_stats (&c->c2.lzo_compwork, so); +#ifdef USE_COMP + if (c->c2.comp_context) + comp_print_stats (c->c2.comp_context, so); #endif #ifdef PACKET_TRUNCATION_CHECK status_printf (so, "TUN read truncations," counter_format, c->c2.n_trunc_tun_read); @@ -275,7 +291,7 @@ print_status (const struct context *c, struct status_output *so) status_printf (so, "Pre-encrypt truncations," counter_format, c->c2.n_trunc_pre_encrypt); status_printf (so, "Post-decrypt truncations," counter_format, c->c2.n_trunc_post_decrypt); #endif -#ifdef WIN32 +#ifdef _WIN32 if (tuntap_defined (c->c1.tuntap)) status_printf (so, "TAP-WIN32 driver status,\"%s\"", tap_win_getinfo (c->c1.tuntap, &gc)); @@ -360,12 +376,35 @@ process_sigterm (struct context *c) return ret; } +/** + * If a restart signal is received during exit-notification, reset the + * signal and return true. + */ +static bool +ignore_restart_signals (struct context *c) +{ + bool ret = false; +#ifdef ENABLE_OCC + if ( (c->sig->signal_received == SIGUSR1 || c->sig->signal_received == SIGHUP) && + event_timeout_defined(&c->c2.explicit_exit_notification_interval) ) + { + msg (M_INFO, "Ignoring %s received during exit notification", + signal_name(c->sig->signal_received, true)); + signal_reset (c->sig); + ret = true; + } +#endif + return ret; +} + bool process_signal (struct context *c) { bool ret = true; - if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT) + if (ignore_restart_signals (c)) + ret = false; + else if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT) { ret = process_sigterm (c); } diff --git a/src/openvpn/sig.h b/src/openvpn/sig.h index 987efef..2875a4c 100644 --- a/src/openvpn/sig.h +++ b/src/openvpn/sig.h @@ -28,6 +28,15 @@ #include "status.h" #include "win32.h" + + +#define SIG_SOURCE_SOFT 0 +#define SIG_SOURCE_HARD 1 +/* CONNECTION_FAILED is also a "soft" status, + * It is thrown if a connection attempt fails + */ +#define SIG_SOURCE_CONNECTION_FAILED 2 + /* * Signal information, including signal code * and descriptive text. @@ -35,7 +44,7 @@ struct signal_info { volatile int signal_received; - volatile bool hard; + volatile int source; const char *signal_text; }; @@ -70,7 +79,7 @@ void register_signal (struct context *c, int sig, const char *text); void process_explicit_exit_notification_timer_wakeup (struct context *c); #endif -#ifdef WIN32 +#ifdef _WIN32 static inline void get_signal (volatile int *sig) diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index b7ac339..c233f2b 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -38,6 +38,9 @@ #include "ps.h" #include "manage.h" #include "misc.h" +#include "manage.h" +#include "openvpn.h" +#include "forward.h" #include "memdbg.h" @@ -69,23 +72,6 @@ sf2gaf(const unsigned int getaddr_flags, * Functions related to the translation of DNS names to IP addresses. */ -static const char* -h_errno_msg(int h_errno_err) -{ - switch (h_errno_err) - { - case HOST_NOT_FOUND: - return "[HOST_NOT_FOUND] The specified host is unknown."; - case NO_DATA: - return "[NO_DATA] The requested name is valid but does not have an IP address."; - case NO_RECOVERY: - return "[NO_RECOVERY] A non-recoverable name server error occurred."; - case TRY_AGAIN: - return "[TRY_AGAIN] A temporary error occurred on an authoritative name server."; - } - return "[unknown h_errno value]"; -} - /* * Translate IP addr or hostname to in_addr_t. * If resolve error, try again for @@ -100,8 +86,8 @@ getaddr (unsigned int flags, { struct addrinfo *ai; int status; - status = openvpn_getaddrinfo(flags, hostname, resolve_retry_seconds, - signal_received, AF_INET, &ai); + status = openvpn_getaddrinfo (flags & ~GETADDR_HOST_ORDER, hostname, NULL, + resolve_retry_seconds, signal_received, AF_INET, &ai); if(status==0) { struct in_addr ia; if(succeeded) @@ -116,6 +102,178 @@ getaddr (unsigned int flags, } } +static inline bool +streqnull (const char* a, const char* b) +{ + if (a == NULL && b == NULL) + return true; + else if (a == NULL || b == NULL) + return false; + else + return streq (a, b); +} + +/* + get_cached_dns_entry return 0 on success and -1 + otherwise. (like getaddrinfo) + */ +static int +get_cached_dns_entry (struct cached_dns_entry* dns_cache, + const char* hostname, + const char* servname, + int ai_family, + int resolve_flags, + struct addrinfo **ai) +{ + struct cached_dns_entry *ph; + int flags; + + /* Only use flags that are relevant for the structure */ + flags = resolve_flags & GETADDR_CACHE_MASK; + + for (ph = dns_cache; ph ; ph = ph->next) + { + if (streqnull (ph->hostname, hostname) && + streqnull (ph->servname, servname) && + ph->ai_family == ai_family && + ph->flags == flags) + { + *ai = ph->ai; + return 0; + } + } + return -1; +} + + +static int +do_preresolve_host (struct context *c, + const char *hostname, + const char *servname, + const int af, + const int flags) +{ + struct addrinfo *ai; + int status; + + if (get_cached_dns_entry(c->c1.dns_cache, + hostname, + servname, + af, + flags, + &ai) == 0 ) + { + /* entry already cached, return success */ + return 0; + } + + status = openvpn_getaddrinfo (flags, hostname, servname, + c->options.resolve_retry_seconds, NULL, + af, &ai); + if (status == 0) + { + struct cached_dns_entry *ph; + + ALLOC_OBJ_CLEAR_GC (ph, struct cached_dns_entry, &c->gc); + ph->ai = ai; + ph->hostname = hostname; + ph->servname = servname; + ph->flags = flags & GETADDR_CACHE_MASK; + + if (!c->c1.dns_cache) + c->c1.dns_cache = ph; + else + { + struct cached_dns_entry *prev = c->c1.dns_cache; + while (prev->next) + prev = prev->next; + prev->next = ph; + } + + gc_addspecial (ai, &gc_freeaddrinfo_callback, &c->gc); + + } + return status; +} + +void +do_preresolve (struct context *c) +{ + int i; + struct connection_list *l = c->options.connection_list; + const unsigned int preresolve_flags = GETADDR_RESOLVE| + GETADDR_UPDATE_MANAGEMENT_STATE| + GETADDR_MENTION_RESOLVE_RETRY| + GETADDR_FATAL; + + + for (i = 0; i < l->len; ++i) + { + int status; + const char *remote; + int flags = preresolve_flags; + + struct connection_entry* ce = c->options.connection_list->array[i]; + + if (proto_is_dgram(ce->proto)) + flags |= GETADDR_DATAGRAM; + + if (c->options.sockflags & SF_HOST_RANDOMIZE) + flags |= GETADDR_RANDOMIZE; + + if (c->options.ip_remote_hint) + remote = c->options.ip_remote_hint; + else + remote = ce->remote; + + /* HTTP remote hostname does not need to be resolved */ + if (! ce->http_proxy_options) + { + status = do_preresolve_host (c, remote, ce->remote_port, ce->af, flags); + if (status != 0) + goto err; + } + + /* Preresolve proxy */ + if (ce->http_proxy_options) + { + status = do_preresolve_host (c, + ce->http_proxy_options->server, + ce->http_proxy_options->port, + ce->af, + preresolve_flags); + + if (status != 0) + goto err; + } + + if (ce->socks_proxy_server) + { + status = do_preresolve_host (c, + ce->socks_proxy_server, + ce->socks_proxy_port, + ce->af, + flags); + if (status != 0) + goto err; + } + + if (ce->bind_local) + { + flags |= GETADDR_PASSIVE; + flags &= ~GETADDR_RANDOMIZE; + status = do_preresolve_host (c, ce->local, ce->local_port, ce->af, flags); + if (status != 0) + goto err; + + } + + } + return; + + err: + throw_signal_soft (SIGHUP, "Preresolving failed"); +} /* * Translate IPv4/IPv6 addr or hostname into struct addrinfo @@ -124,6 +282,7 @@ getaddr (unsigned int flags, int openvpn_getaddrinfo (unsigned int flags, const char *hostname, + const char *servname, int resolve_retry_seconds, volatile int *signal_received, int ai_family, @@ -134,15 +293,27 @@ openvpn_getaddrinfo (unsigned int flags, int sigrec = 0; int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; struct gc_arena gc = gc_new (); + const char *print_hostname; + const char *print_servname; ASSERT(res); - if (!hostname) - hostname = "::"; + ASSERT (hostname || servname); + ASSERT (!(flags & GETADDR_HOST_ORDER)); - if (flags & GETADDR_RANDOMIZE) + if (hostname && (flags & GETADDR_RANDOMIZE)) hostname = hostname_randomize(hostname, &gc); + if(hostname) + print_hostname = hostname; + else + print_hostname = "undefined"; + + if(servname) + print_servname = servname; + else + print_servname = ""; + if (flags & GETADDR_MSG_VIRT_OUT) msglevel |= M_MSG_VIRT_OUT; @@ -154,25 +325,35 @@ openvpn_getaddrinfo (unsigned int flags, CLEAR(hints); hints.ai_family = ai_family; hints.ai_flags = AI_NUMERICHOST; - hints.ai_socktype = SOCK_STREAM; - status = getaddrinfo(hostname, NULL, &hints, res); + if(flags & GETADDR_PASSIVE) + hints.ai_flags |= AI_PASSIVE; + + if(flags & GETADDR_DATAGRAM) + hints.ai_socktype = SOCK_DGRAM; + else + hints.ai_socktype = SOCK_STREAM; + + status = getaddrinfo(hostname, servname, &hints, res); if (status != 0) /* parse as numeric address failed? */ { const int fail_wait_interval = 5; /* seconds */ - int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval); + /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 */ + int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : + ((resolve_retry_seconds + 4)/ fail_wait_interval); const char *fmt; int level = 0; - fmt = "RESOLVE: Cannot resolve host address: %s: %s"; + fmt = "RESOLVE: Cannot resolve host address: %s:%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.)"; + fmt = "RESOLVE: Cannot resolve host address: %s:%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 IP address: %s", hostname); + msg (msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)", + print_hostname,print_servname, gai_strerror(status)); goto done; } @@ -183,8 +364,10 @@ openvpn_getaddrinfo (unsigned int flags, management_set_state (management, OPENVPN_STATE_RESOLVE, NULL, - (in_addr_t)0, - (in_addr_t)0); + NULL, + NULL, + NULL, + NULL); } #endif @@ -193,14 +376,14 @@ openvpn_getaddrinfo (unsigned int flags, */ while (true) { -#ifndef WIN32 +#ifndef _WIN32 res_init (); #endif /* try hostname lookup */ - hints.ai_flags = 0; + hints.ai_flags &= ~AI_NUMERICHOST; dmsg (D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d", flags, hints.ai_family, hints.ai_socktype); - status = getaddrinfo(hostname, NULL, &hints, res); + status = getaddrinfo(hostname, servname, &hints, res); if (signal_received) { @@ -239,7 +422,8 @@ openvpn_getaddrinfo (unsigned int flags, msg (level, fmt, - hostname, + print_hostname, + print_servname, gai_strerror(status)); if (--resolve_retries <= 0) @@ -252,7 +436,8 @@ openvpn_getaddrinfo (unsigned int flags, /* hostname resolve succeeded */ - /* Do not chose an IP Addresse by random or change the order * + /* + * Do not choose an IP Addresse by random or change the order * * of IP addresses, doing so will break RFC 3484 address selection * */ } @@ -422,59 +607,6 @@ mac_addr_safe (const char *mac_addr) return true; } -static void -update_remote (const char* host, - struct openvpn_sockaddr *addr, - bool *changed, - const unsigned int sockflags) -{ - switch(addr->addr.sa.sa_family) - { - case AF_INET: - if (host && addr) - { - 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; - case AF_INET6: - if (host && addr) - { - int status; - struct addrinfo* ai; - - status = openvpn_getaddrinfo(sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags), host, 1, NULL, AF_INET6, &ai); - - if ( status ==0 ) - { - struct sockaddr_in6 sin6; - CLEAR(sin6); - sin6 = *((struct sockaddr_in6*)ai->ai_addr); - 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; - } - freeaddrinfo(ai); - } - } - break; - default: - ASSERT(0); - } -} - static int socket_get_sndbuf (int sd) { @@ -558,7 +690,7 @@ socket_set_buffers (int fd, const struct socket_buffer_size *sbs) static bool socket_set_tcp_nodelay (int sd, int state) { -#if defined(WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) +#if defined(_WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) if (setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof (state)) != 0) { msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state); @@ -579,7 +711,7 @@ static inline void socket_set_mark (int sd, int mark) { #if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK - if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, &mark, sizeof (mark)) != 0) + if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof (mark)) != 0) msg (M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark); #endif } @@ -619,14 +751,17 @@ link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf) */ socket_descriptor_t -create_socket_tcp (int af) +create_socket_tcp (struct addrinfo* addrinfo) { socket_descriptor_t sd; - if ((sd = socket (af, SOCK_STREAM, IPPROTO_TCP)) < 0) + ASSERT (addrinfo); + ASSERT (addrinfo->ai_socktype == SOCK_STREAM); + + if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) msg (M_ERR, "Cannot create TCP socket"); -#ifndef WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */ +#ifndef _WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */ /* set SO_REUSEADDR on socket */ { int on = 1; @@ -640,106 +775,134 @@ create_socket_tcp (int af) } static socket_descriptor_t -create_socket_udp (const unsigned int flags) +create_socket_udp (struct addrinfo* addrinfo, const unsigned int flags) { socket_descriptor_t sd; - if ((sd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) - msg (M_ERR, "UDP: Cannot create UDP socket"); + ASSERT (addrinfo); + ASSERT (addrinfo->ai_socktype == SOCK_DGRAM); + + if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + msg (M_ERR, "UDP: Cannot create UDP/UDP6 socket"); #if ENABLE_IP_PKTINFO else if (flags & SF_USE_IP_PKTINFO) { int pad = 1; -#ifdef IP_PKTINFO - if (setsockopt (sd, SOL_IP, IP_PKTINFO, - (void*)&pad, sizeof(pad)) < 0) - msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO"); + if(addrinfo->ai_family == AF_INET) + { +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + if (setsockopt (sd, SOL_IP, IP_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) + msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO"); #elif defined(IP_RECVDSTADDR) - if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR, - (void*)&pad, sizeof(pad)) < 0) - msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR"); + if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR, + (void*)&pad, sizeof(pad)) < 0) + msg(M_ERR, "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; -} - -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_ERR, "UDP: Cannot create UDP6 socket"); -#if ENABLE_IP_PKTINFO - else if (flags & SF_USE_IP_PKTINFO) - { - int pad = 1; + } + else if (addrinfo->ai_family == AF_INET6 ) + { #ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */ - if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, - (void*)&pad, sizeof(pad)) < 0) + if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO, + (void*)&pad, sizeof(pad)) < 0) #else - if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, - (void*)&pad, sizeof(pad)) < 0) + if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void*)&pad, sizeof(pad)) < 0) #endif - msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO"); + msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO"); + } } #endif return sd; } +static void bind_local (struct link_socket *sock, const sa_family_t ai_family) +{ + /* bind to local address/port */ + if (sock->bind_local) + { + if (sock->socks_proxy && sock->info.proto == PROTO_UDP) + socket_bind (sock->ctrl_sd, sock->info.lsa->bind_local, + ai_family, "SOCKS", false); + else + socket_bind (sock->sd, sock->info.lsa->bind_local, + ai_family, + "TCP/UDP", sock->info.bind_ipv6_only); + } +} + static void -create_socket (struct link_socket *sock) +create_socket (struct link_socket* sock, struct addrinfo* addr) { - /* create socket */ - if (sock->info.proto == PROTO_UDPv4) + if (addr->ai_protocol == IPPROTO_UDP || addr->ai_socktype == SOCK_DGRAM) { - sock->sd = create_socket_udp (sock->sockflags); + sock->sd = create_socket_udp (addr, sock->sockflags); sock->sockflags |= SF_GETADDRINFO_DGRAM; -#ifdef ENABLE_SOCKS + /* Assume that control socket and data socket to the socks proxy + * are using the same IP family */ if (sock->socks_proxy) - sock->ctrl_sd = create_socket_tcp (AF_INET); -#endif - } - else if (sock->info.proto == PROTO_TCPv4_SERVER - || sock->info.proto == PROTO_TCPv4_CLIENT) - { - sock->sd = create_socket_tcp (AF_INET); - } - else if (sock->info.proto == PROTO_TCPv6_SERVER - || sock->info.proto == PROTO_TCPv6_CLIENT) - { - sock->sd = create_socket_tcp (AF_INET6); + { + /* Construct a temporary addrinfo to create the socket, + * currently resolve two remote addresses is not supported, + * TODO: Rewrite the whole resolve_remote */ + struct addrinfo addrinfo_tmp = *addr; + addrinfo_tmp.ai_socktype = SOCK_STREAM; + addrinfo_tmp.ai_protocol = IPPROTO_TCP; + sock->ctrl_sd = create_socket_tcp (&addrinfo_tmp); + } } - else if (sock->info.proto == PROTO_UDPv6) + else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM) { - sock->sd = create_socket_udp6 (sock->sockflags); - sock->sockflags |= SF_GETADDRINFO_DGRAM; + sock->sd = create_socket_tcp (addr); } else { ASSERT (0); } + /* set socket buffers based on --sndbuf and --rcvbuf options */ + socket_set_buffers (sock->sd, &sock->socket_buffer_sizes); + + /* set socket to --mark packets with given value */ + socket_set_mark (sock->sd, sock->mark); + + bind_local (sock, addr->ai_family); +} + +#ifdef TARGET_ANDROID +static void protect_fd_nonlocal (int fd, const struct sockaddr* addr) +{ + /* pass socket FD to management interface to pass on to VPNService API + * as "protected socket" (exempt from being routed into tunnel) + */ + if (addr_local (addr)) { + msg(D_SOCKET_DEBUG, "Address is local, not protecting socket fd %d", fd); + return; + } + + msg(D_SOCKET_DEBUG, "Protecting socket fd %d", fd); + management->connection.fdtosend = fd; + management_android_control (management, "PROTECTFD", __func__); } +#endif /* * Functions used for establishing a TCP stream connection. */ - static void socket_do_listen (socket_descriptor_t sd, - const struct openvpn_sockaddr *local, + const struct addrinfo *local, bool do_listen, bool do_set_nonblock) { struct gc_arena gc = gc_new (); if (do_listen) { + ASSERT(local); msg (M_INFO, "Listening for incoming TCP connection on %s", - print_sockaddr (local, &gc)); + print_sockaddr (local->ai_addr, &gc)); if (listen (sd, 1)) msg (M_ERR, "TCP: listen() failed"); } @@ -821,8 +984,7 @@ static int socket_listen_accept (socket_descriptor_t sd, struct link_socket_actual *act, const char *remote_dynamic, - bool *remote_changed, - const struct openvpn_sockaddr *local, + const struct addrinfo *local, bool do_listen, bool nowait, volatile int *signal_received) @@ -868,18 +1030,26 @@ socket_listen_accept (socket_descriptor_t sd, if (socket_defined (new_sd)) { - update_remote (remote_dynamic, &remote_verify, remote_changed, 0); - if (addr_defined (&remote_verify) - && !addr_match (&remote_verify, &act->dest)) - { - msg (M_WARN, - "TCP NOTE: Rejected connection attempt from %s due to --remote setting", - print_link_socket_actual (act, &gc)); - if (openvpn_close_socket (new_sd)) - msg (M_ERR, "TCP: close socket failed (new_sd)"); - } + struct addrinfo* ai = NULL; + if(remote_dynamic) + openvpn_getaddrinfo(0, remote_dynamic, NULL, 1, NULL, + remote_verify.addr.sa.sa_family, &ai); + + if(ai && !addrlist_match(&remote_verify, ai)) + { + msg (M_WARN, + "TCP NOTE: Rejected connection attempt from %s due to --remote setting", + print_link_socket_actual (act, &gc)); + if (openvpn_close_socket (new_sd)) + msg (M_ERR, "TCP: close socket failed (new_sd)"); + freeaddrinfo(ai); + } else - break; + { + if(ai) + freeaddrinfo(ai); + break; + } } openvpn_sleep (1); } @@ -893,19 +1063,61 @@ socket_listen_accept (socket_descriptor_t sd, return new_sd; } +/* older mingw versions and WinXP do not have this define, + * but Vista and up support the functionality - just define it here + */ +#ifdef _WIN32 +# ifndef IPV6_V6ONLY +# define IPV6_V6ONLY 27 +# endif +#endif void socket_bind (socket_descriptor_t sd, - struct openvpn_sockaddr *local, - const char *prefix) + struct addrinfo *local, + int ai_family, + const char *prefix, + bool ipv6only) { struct gc_arena gc = gc_new (); - if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family))) + /* FIXME (schwabe) + * getaddrinfo for the bind address might return multiple AF_INET/AF_INET6 + * entries for the requested protocol. + * For example if an address has multiple A records + * What is the correct way to deal with it? + */ + + struct addrinfo* cur; + + ASSERT(local); + + + /* find the first addrinfo with correct ai_family */ + for (cur = local; cur; cur=cur->ai_next) + { + if(cur->ai_family == ai_family) + break; + } + if (!cur) + msg (M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record", + prefix, addr_family_name(ai_family)); + + if (ai_family == AF_INET6) + { + int v6only = ipv6only ? 1: 0; /* setsockopt must have an "int" */ + + msg (M_INFO, "setsockopt(IPV6_V6ONLY=%d)", v6only); + if (setsockopt (sd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only))) + { + msg (M_NONFATAL|M_ERRNO, "Setting IPV6_V6ONLY=%d failed", v6only); + } + } + if (bind (sd, cur->ai_addr, cur->ai_addrlen)) { const int errnum = openvpn_errno (); msg (M_FATAL, "%s: Socket bind failed on local address %s: %s", prefix, - print_sockaddr (local, &gc), + print_sockaddr_ex (local->ai_addr, ":", PS_SHOW_PORT, &gc), strerror_ts (errnum, &gc)); } gc_free (&gc); @@ -913,19 +1125,23 @@ socket_bind (socket_descriptor_t sd, int openvpn_connect (socket_descriptor_t sd, - struct openvpn_sockaddr *remote, + const struct sockaddr *remote, int connect_timeout, volatile int *signal_received) { int status = 0; +#ifdef TARGET_ANDROID + protect_fd_nonlocal(sd, remote); +#endif + #ifdef CONNECT_NONBLOCK set_nonblock (sd); - status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); + status = connect (sd, remote, af_addr_size(remote->sa_family)); if (status) status = openvpn_errno (); if ( -#ifdef WIN32 +#ifdef _WIN32 status == WSAEWOULDBLOCK #else status == EINPROGRESS @@ -968,7 +1184,7 @@ openvpn_connect (socket_descriptor_t sd, { if (--connect_timeout < 0) { -#ifdef WIN32 +#ifdef _WIN32 status = WSAETIMEDOUT; #else status = ETIMEDOUT; @@ -995,7 +1211,7 @@ openvpn_connect (socket_descriptor_t sd, } } #else - status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family)); + status = connect (sd, remote, af_addr_size(remote->sa_family)); if (status) status = openvpn_errno (); #endif @@ -1003,85 +1219,72 @@ openvpn_connect (socket_descriptor_t sd, return status; } +void set_actual_address (struct link_socket_actual* actual, struct addrinfo* ai) +{ + CLEAR (*actual); + ASSERT (ai); + + if (ai->ai_family == AF_INET) + actual->dest.addr.in4 = + *((struct sockaddr_in*) ai->ai_addr); + else if (ai->ai_family == AF_INET6) + actual->dest.addr.in6 = + *((struct sockaddr_in6*) ai->ai_addr); + else + ASSERT(0); + +} + void -socket_connect (socket_descriptor_t *sd, - struct openvpn_sockaddr *local, - bool bind_local, - struct openvpn_sockaddr *remote, - const bool connection_profiles_defined, - const char *remote_dynamic, - bool *remote_changed, - const int connect_retry_seconds, - const int connect_timeout, - const int connect_retry_max, - const unsigned int sockflags, - volatile int *signal_received) +socket_connect (socket_descriptor_t* sd, + const struct sockaddr* dest, + const int connect_timeout, + struct signal_info* sig_info) { struct gc_arena gc = gc_new (); - int retry = 0; + int status; #ifdef CONNECT_NONBLOCK - msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]", - print_sockaddr (remote, &gc)); + msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]", + print_sockaddr (dest, &gc)); #else - msg (M_INFO, "Attempting to establish TCP connection with %s", - print_sockaddr (remote, &gc)); + msg (M_INFO, "Attempting to establish TCP connection with %s", + print_sockaddr (dest, &gc)); #endif - while (true) - { - int status; - #ifdef ENABLE_MANAGEMENT - if (management) + if (management) management_set_state (management, OPENVPN_STATE_TCP_CONNECT, NULL, - (in_addr_t)0, - (in_addr_t)0); + NULL, + NULL, + NULL, + NULL); #endif - status = openvpn_connect (*sd, remote, connect_timeout, signal_received); - - get_signal (signal_received); - if (*signal_received) - goto done; - - if (!status) - break; - - msg (D_LINK_ERRORS, - "TCP: connect to %s failed, will try again in %d seconds: %s", - print_sockaddr (remote, &gc), - connect_retry_seconds, - strerror_ts (status, &gc)); - - gc_reset (&gc); - - openvpn_close_socket (*sd); - *sd = SOCKET_UNDEFINED; - - if ((connect_retry_max > 0 && ++retry >= connect_retry_max) || connection_profiles_defined) - { - *signal_received = SIGUSR1; - goto done; - } - - openvpn_sleep (connect_retry_seconds); + /* Set the actual address */ + status = openvpn_connect (*sd, dest, connect_timeout, &sig_info->signal_received); - get_signal (signal_received); - if (*signal_received) + get_signal (&sig_info->signal_received); + if (sig_info->signal_received) goto done; - *sd = create_socket_tcp (local->addr.sa.sa_family); + if (status) { - if (bind_local) - socket_bind (*sd, local, "TCP Client"); - update_remote (remote_dynamic, remote, remote_changed, sockflags); - } + msg (D_LINK_ERRORS, + "TCP: connect to %s failed: %s", + print_sockaddr (dest, &gc), + strerror_ts (status, &gc)); - msg (M_INFO, "TCP connection established with %s", - print_sockaddr (remote, &gc)); + openvpn_close_socket (*sd); + *sd = SOCKET_UNDEFINED; + sig_info->signal_received = SIGUSR1; + sig_info->source = SIG_SOURCE_CONNECTION_FAILED; + } else { + msg (M_INFO, "TCP connection established with %s", + print_sockaddr (dest, &gc)); + } done: gc_free (&gc); @@ -1093,7 +1296,7 @@ socket_connect (socket_descriptor_t *sd, static void socket_frame_init (const struct frame *frame, struct link_socket *sock) { -#ifdef WIN32 +#ifdef _WIN32 overlapped_io_init (&sock->reads, frame, FALSE, false); overlapped_io_init (&sock->writes, frame, TRUE, false); sock->rw_handle.read = sock->reads.overlapped.hEvent; @@ -1102,7 +1305,7 @@ socket_frame_init (const struct frame *frame, struct link_socket *sock) if (link_socket_connection_oriented (sock)) { -#ifdef WIN32 +#ifdef _WIN32 stream_buf_init (&sock->stream_buf, &sock->reads.buf_init, sock->sockflags, @@ -1132,70 +1335,39 @@ frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto) } static void -resolve_bind_local (struct link_socket *sock) +resolve_bind_local (struct link_socket *sock, const sa_family_t af) { struct gc_arena gc = gc_new (); /* resolve local address if undefined */ - if (!addr_defined (&sock->info.lsa->local)) - { - /* may return AF_{INET|INET6} guessed from local_host */ - switch(addr_guess_family(sock->info.proto, sock->local_host)) - { - case AF_INET: - 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); - break; - case AF_INET6: - { - int status; - CLEAR(sock->info.lsa->local.addr.in6); - if (sock->local_host) - { - struct addrinfo *ai; - - status = openvpn_getaddrinfo(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL, - sock->local_host, 0, NULL, AF_INET6, &ai); - if(status ==0) { - sock->info.lsa->local.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); - freeaddrinfo(ai); - } - } - else - { - sock->info.lsa->local.addr.in6.sin6_family = AF_INET6; - sock->info.lsa->local.addr.in6.sin6_addr = in6addr_any; - status = 0; - } - if (!status == 0) - { - msg (M_FATAL, "getaddr6() failed for local \"%s\": %s", - sock->local_host, - gai_strerror(status)); - } - sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port); - } - break; - } - } - - /* bind to local address/port */ - if (sock->bind_local) + if (!sock->info.lsa->bind_local) { -#ifdef ENABLE_SOCKS - if (sock->socks_proxy && sock->info.proto == PROTO_UDPv4) - socket_bind (sock->ctrl_sd, &sock->info.lsa->local, "SOCKS"); - else -#endif - socket_bind (sock->sd, &sock->info.lsa->local, "TCP/UDP"); + int flags = GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | + GETADDR_FATAL | GETADDR_PASSIVE; + int status; + + if(proto_is_dgram(sock->info.proto)) + flags |= GETADDR_DATAGRAM; + + /* will return AF_{INET|INET6}from local_host */ + status = get_cached_dns_entry (sock->dns_cache, + sock->local_host, + sock->local_port, + af, + flags, + &sock->info.lsa->bind_local); + + if (status) + status = openvpn_getaddrinfo(flags, sock->local_host, sock->local_port, 0, + NULL, af, &sock->info.lsa->bind_local); + + if(status !=0) { + msg (M_FATAL, "getaddrinfo() failed for local \"%s:%s\": %s", + sock->local_host, sock->local_port, + gai_strerror(status)); + } } + gc_free (&gc); } @@ -1206,132 +1378,113 @@ resolve_remote (struct link_socket *sock, volatile int *signal_received) { struct gc_arena gc = gc_new (); - int af; - if (!sock->did_resolve_remote) + /* resolve remote address if undefined */ + if (!sock->info.lsa->remote_list) { - /* resolve remote address if undefined */ - if (!addr_defined (&sock->info.lsa->remote)) + if (sock->remote_host) { - af = addr_guess_family(sock->info.proto, sock->remote_host); - switch(af) - { - case AF_INET: - sock->info.lsa->remote.addr.in4.sin_family = AF_INET; - sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0; - 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; - } - - if (sock->remote_host) + unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags); + int retry = 0; + int status = -1; + struct addrinfo* ai; + if (proto_is_dgram(sock->info.proto)) + flags |= GETADDR_DATAGRAM; + + if (sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) { - unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags); - int retry = 0; - int status = -1; - struct addrinfo* ai; - - if (sock->connection_profiles_defined && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE) + if (phase == 2) + flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); + retry = 0; + } + else if (phase == 1) + { + if (sock->resolve_retry_seconds) { - if (phase == 2) - flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL); retry = 0; } - else if (phase == 1) + else { - if (sock->resolve_retry_seconds) - { - retry = 0; - } - else - { - flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); - retry = 0; - } + flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY); + retry = 0; } - else if (phase == 2) + } + else if (phase == 2) + { + if (sock->resolve_retry_seconds) { - if (sock->resolve_retry_seconds) - { - flags |= GETADDR_FATAL; - retry = sock->resolve_retry_seconds; - } - else - { - ASSERT (0); - } + flags |= GETADDR_FATAL; + retry = sock->resolve_retry_seconds; } else { ASSERT (0); } + } + else + { + ASSERT (0); + } - /* Temporary fix, this need to be changed for dual stack */ - status = openvpn_getaddrinfo(flags, sock->remote_host, retry, - signal_received, af, &ai); - if(status == 0) - { - if ( ai->ai_family == AF_INET6 ) - sock->info.lsa->remote.addr.in6 = *((struct sockaddr_in6*)(ai->ai_addr)); - else - sock->info.lsa->remote.addr.in4 = *((struct sockaddr_in*)(ai->ai_addr)); - freeaddrinfo(ai); - - dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", - flags, - phase, - retry, - signal_received ? *signal_received : -1, - status); - } - if (signal_received) - { - if (*signal_received) - goto done; - } + + status = get_cached_dns_entry (sock->dns_cache, + sock->remote_host, + sock->remote_port, + sock->info.af, + flags, &ai); + if (status) + status = openvpn_getaddrinfo (flags, sock->remote_host, sock->remote_port, + retry, signal_received, sock->info.af, &ai); + + if(status == 0) { + sock->info.lsa->remote_list = ai; + sock->info.lsa->current_remote = ai; + + dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d", + flags, + phase, + retry, + signal_received ? *signal_received : -1, + status); + } + if (signal_received) + { + if (*signal_received) + goto done; + } if (status!=0) { if (signal_received) *signal_received = SIGUSR1; goto done; } - } - switch(af) - { - case AF_INET: - sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port); - break; - case AF_INET6: - sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port); - break; - } } + } - /* should we re-use previous active remote address? */ - if (link_socket_actual_defined (&sock->info.lsa->actual)) - { - msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s", - print_link_socket_actual (&sock->info.lsa->actual, &gc)); - if (remote_dynamic) - *remote_dynamic = NULL; - } - else + /* should we re-use previous active remote address? */ + if (link_socket_actual_defined (&sock->info.lsa->actual)) + { + msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s", + print_link_socket_actual (&sock->info.lsa->actual, &gc)); + if (remote_dynamic) + *remote_dynamic = NULL; + } + else + { + CLEAR (sock->info.lsa->actual); + if(sock->info.lsa->current_remote) { - CLEAR (sock->info.lsa->actual); - sock->info.lsa->actual.dest = sock->info.lsa->remote; + set_actual_address (&sock->info.lsa->actual, + sock->info.lsa->current_remote); } - - /* remember that we finished */ - sock->did_resolve_remote = true; } done: gc_free (&gc); } + + struct link_socket * link_socket_new (void) { @@ -1339,29 +1492,24 @@ link_socket_new (void) ALLOC_OBJ_CLEAR (sock, struct link_socket); sock->sd = SOCKET_UNDEFINED; -#ifdef ENABLE_SOCKS sock->ctrl_sd = SOCKET_UNDEFINED; -#endif return sock; } -/* bind socket if necessary */ void link_socket_init_phase1 (struct link_socket *sock, - const bool connection_profiles_defined, const char *local_host, - int local_port, + const char *local_port, const char *remote_host, - int remote_port, + const char *remote_port, + struct cached_dns_entry *dns_cache, int proto, + sa_family_t af, + bool bind_ipv6_only, int mode, const struct link_socket *accept_from, -#ifdef ENABLE_HTTP_PROXY struct http_proxy_info *http_proxy, -#endif -#ifdef ENABLE_SOCKS struct socks_proxy_info *socks_proxy, -#endif #ifdef ENABLE_DEBUG int gremlin, #endif @@ -1372,38 +1520,25 @@ link_socket_init_phase1 (struct link_socket *sock, const char *ipchange_command, const struct plugin_list *plugins, int resolve_retry_seconds, - int connect_retry_seconds, - int connect_timeout, - int connect_retry_max, int mtu_discover_type, int rcvbuf, int sndbuf, int mark, + struct event_timeout* server_poll_timeout, unsigned int sockflags) { ASSERT (sock); - sock->connection_profiles_defined = connection_profiles_defined; - sock->local_host = local_host; sock->local_port = local_port; sock->remote_host = remote_host; sock->remote_port = remote_port; - -#ifdef ENABLE_HTTP_PROXY + sock->dns_cache = dns_cache; sock->http_proxy = http_proxy; -#endif - -#ifdef ENABLE_SOCKS sock->socks_proxy = socks_proxy; -#endif - sock->bind_local = bind_local; sock->inetd = inetd; sock->resolve_retry_seconds = resolve_retry_seconds; - sock->connect_retry_seconds = connect_retry_seconds; - sock->connect_timeout = connect_timeout; - sock->connect_retry_max = connect_retry_max; sock->mtu_discover_type = mtu_discover_type; #ifdef ENABLE_DEBUG @@ -1413,114 +1548,321 @@ link_socket_init_phase1 (struct link_socket *sock, sock->socket_buffer_sizes.rcvbuf = rcvbuf; sock->socket_buffer_sizes.sndbuf = sndbuf; - sock->sockflags = sockflags; + sock->sockflags = sockflags; + sock->mark = mark; + + sock->info.proto = proto; + sock->info.af = af; + sock->info.remote_float = remote_float; + sock->info.lsa = lsa; + sock->info.bind_ipv6_only = bind_ipv6_only; + sock->info.ipchange_command = ipchange_command; + sock->info.plugins = plugins; + sock->server_poll_timeout = server_poll_timeout; + + sock->mode = mode; + if (mode == LS_MODE_TCP_ACCEPT_FROM) + { + ASSERT (accept_from); + ASSERT (sock->info.proto == PROTO_TCP_SERVER); + ASSERT (!sock->inetd); + sock->sd = accept_from->sd; + } + + /* are we running in HTTP proxy mode? */ + if (sock->http_proxy) + { + ASSERT (sock->info.proto == PROTO_TCP_CLIENT); + ASSERT (!sock->inetd); + + /* the proxy server */ + sock->remote_host = http_proxy->options.server; + sock->remote_port = http_proxy->options.port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + /* or in Socks proxy mode? */ + else if (sock->socks_proxy) + { + ASSERT (!sock->inetd); + + /* the proxy server */ + sock->remote_host = socks_proxy->server; + sock->remote_port = socks_proxy->port; + + /* the OpenVPN server we will use the proxy to connect to */ + sock->proxy_dest_host = remote_host; + sock->proxy_dest_port = remote_port; + } + else + { + sock->remote_host = remote_host; + sock->remote_port = remote_port; + } + + /* bind behavior for TCP server vs. client */ + if (sock->info.proto == PROTO_TCP_SERVER) + { + if (sock->mode == LS_MODE_TCP_ACCEPT_FROM) + sock->bind_local = false; + else + sock->bind_local = true; + } + + /* were we started by inetd or xinetd? */ + if (sock->inetd) + { + ASSERT (sock->info.proto != PROTO_TCP_CLIENT); + ASSERT (socket_defined (inetd_socket_descriptor)); + sock->sd = inetd_socket_descriptor; + } + else if (mode != LS_MODE_TCP_ACCEPT_FROM) + { + if (sock->bind_local) { + resolve_bind_local (sock, sock->info.af); + } + resolve_remote (sock, 1, NULL, NULL); + } +} + +static +void phase2_inetd (struct link_socket* sock, const struct frame *frame, + const char *remote_dynamic, volatile int *signal_received) +{ + bool remote_changed = false; + + if (sock->info.proto == PROTO_TCP_SERVER) { + /* AF_INET as default (and fallback) for inetd */ + sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET; +#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, &local_addr.addr.sa, &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, sock->info.af, 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, sock->info.af, 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 + sock->sd = + socket_listen_accept (sock->sd, + &sock->info.lsa->actual, + remote_dynamic, + sock->info.lsa->bind_local, + false, + sock->inetd == INETD_NOWAIT, + signal_received); + + } + ASSERT (!remote_changed); +} + +static void +phase2_set_socket_flags (struct link_socket* sock) +{ + /* set misc socket parameters */ + socket_set_flags (sock->sd, sock->sockflags); + + /* set socket to non-blocking mode */ + set_nonblock (sock->sd); + + /* set socket file descriptor to not pass across execs, so that + scripts don't have access to it */ + set_cloexec (sock->sd); + + if (socket_defined (sock->ctrl_sd)) + set_cloexec (sock->ctrl_sd); + + /* set Path MTU discovery options on the socket */ + set_mtu_discover_type (sock->sd, sock->mtu_discover_type, sock->info.af); + +#if EXTENDED_SOCKET_ERROR_CAPABILITY + /* if the OS supports it, enable extended error passing on the socket */ + set_sock_extended_error_passing (sock->sd); +#endif +} + + +static void +linksock_print_addr (struct link_socket *sock) +{ + struct gc_arena gc = gc_new (); + const int msglevel = (sock->mode == LS_MODE_TCP_ACCEPT_FROM) ? D_INIT_MEDIUM : M_INFO; + + /* print local address */ + if (sock->inetd) + msg (msglevel, "%s link local: [inetd]", proto2ascii (sock->info.proto, sock->info.af, true)); + else if (sock->bind_local) + { + sa_family_t ai_family = sock->info.lsa->actual.dest.addr.sa.sa_family; + /* Socket is always bound on the first matching address, + * For bound sockets with no remote addr this is the element of + * the list */ + struct addrinfo *cur; + for (cur = sock->info.lsa->bind_local; cur; cur=cur->ai_next) + { + if(!ai_family || ai_family == cur->ai_family) + break; + } + ASSERT (cur); + msg (msglevel, "%s link local (bound): %s", + proto2ascii (sock->info.proto, sock->info.af, true), + print_sockaddr(cur->ai_addr,&gc)); + } + else + msg (msglevel, "%s link local: (not bound)", + proto2ascii (sock->info.proto, sock->info.af, true)); + + /* print active remote address */ + msg (msglevel, "%s link remote: %s", + proto2ascii (sock->info.proto, sock->info.af, true), + print_link_socket_actual_ex (&sock->info.lsa->actual, + ":", + PS_SHOW_PORT_IF_DEFINED, + &gc)); + gc_free(&gc); +} + +static void +phase2_tcp_server (struct link_socket *sock, const char *remote_dynamic, + volatile int *signal_received) +{ + switch (sock->mode) + { + case LS_MODE_DEFAULT: + sock->sd = socket_listen_accept (sock->sd, + &sock->info.lsa->actual, + remote_dynamic, + sock->info.lsa->bind_local, + true, + false, + signal_received); + break; + case LS_MODE_TCP_LISTEN: + socket_do_listen (sock->sd, + sock->info.lsa->bind_local, + true, + false); + break; + case LS_MODE_TCP_ACCEPT_FROM: + sock->sd = socket_do_accept (sock->sd, + &sock->info.lsa->actual, + false); + if (!socket_defined (sock->sd)) + { + *signal_received = SIGTERM; + return; + } + tcp_connection_established (&sock->info.lsa->actual); + break; + default: + ASSERT (0); + } +} + - sock->info.proto = proto; - sock->info.remote_float = remote_float; - sock->info.lsa = lsa; - sock->info.ipchange_command = ipchange_command; - sock->info.plugins = plugins; +static void +phase2_tcp_client (struct link_socket *sock, struct signal_info *sig_info) +{ + bool proxy_retry = false; + do { + socket_connect (&sock->sd, + sock->info.lsa->current_remote->ai_addr, + get_server_poll_remaining_time (sock->server_poll_timeout), + sig_info); - sock->mode = mode; - if (mode == LS_MODE_TCP_ACCEPT_FROM) - { - ASSERT (accept_from); - ASSERT (sock->info.proto == PROTO_TCPv4_SERVER - || sock->info.proto == PROTO_TCPv6_SERVER - ); - ASSERT (!sock->inetd); - sock->sd = accept_from->sd; - } + if (sig_info->signal_received) + return; - if (false) - ; -#ifdef ENABLE_HTTP_PROXY - /* are we running in HTTP proxy mode? */ - else if (sock->http_proxy) - { - ASSERT (sock->info.proto == PROTO_TCPv4_CLIENT); - ASSERT (!sock->inetd); + if (sock->http_proxy) + { + proxy_retry = establish_http_proxy_passthru (sock->http_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + sock->server_poll_timeout, + &sock->stream_buf.residual, + &sig_info->signal_received); + } + else if (sock->socks_proxy) + { + establish_socks_proxy_passthru (sock->socks_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + &sig_info->signal_received); + } + if (proxy_retry) + { + openvpn_close_socket (sock->sd); + sock->sd = create_socket_tcp (sock->info.lsa->current_remote); + } - /* the proxy server */ - sock->remote_host = http_proxy->options.server; - sock->remote_port = http_proxy->options.port; + } while (proxy_retry); - /* the OpenVPN server we will use the proxy to connect to */ - sock->proxy_dest_host = remote_host; - sock->proxy_dest_port = remote_port; - } -#endif -#ifdef ENABLE_SOCKS - /* or in Socks proxy mode? */ - else if (sock->socks_proxy) - { - ASSERT (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_UDPv4); - ASSERT (!sock->inetd); +} - /* the proxy server */ - sock->remote_host = socks_proxy->server; - sock->remote_port = socks_proxy->port; +static void +phase2_socks_client (struct link_socket *sock, struct signal_info *sig_info) +{ + socket_connect (&sock->ctrl_sd, + sock->info.lsa->current_remote->ai_addr, + get_server_poll_remaining_time (sock->server_poll_timeout), + sig_info); - /* the OpenVPN server we will use the proxy to connect to */ - sock->proxy_dest_host = remote_host; - sock->proxy_dest_port = remote_port; - } -#endif - else - { - sock->remote_host = remote_host; - sock->remote_port = remote_port; - } + if (sig_info->signal_received) + return; - /* bind behavior for TCP server vs. client */ - if (sock->info.proto == PROTO_TCPv4_SERVER) - { - if (sock->mode == LS_MODE_TCP_ACCEPT_FROM) - sock->bind_local = false; - else - sock->bind_local = true; - } + establish_socks_proxy_udpassoc (sock->socks_proxy, + sock->ctrl_sd, + sock->sd, + &sock->socks_relay.dest, + &sig_info->signal_received); - /* were we started by inetd or xinetd? */ - if (sock->inetd) - { - ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT - && sock->info.proto != PROTO_TCPv6_CLIENT); - ASSERT (socket_defined (inetd_socket_descriptor)); - sock->sd = inetd_socket_descriptor; - } - else if (mode != LS_MODE_TCP_ACCEPT_FROM) - { - create_socket (sock); + if (sig_info->signal_received) + return; - /* set socket buffers based on --sndbuf and --rcvbuf options */ - socket_set_buffers (sock->sd, &sock->socket_buffer_sizes); + sock->remote_host = sock->proxy_dest_host; + sock->remote_port = sock->proxy_dest_port; - /* set socket to --mark packets with given value */ - socket_set_mark (sock->sd, mark); + addr_zero_host(&sock->info.lsa->actual.dest); + if (sock->info.lsa->remote_list) + { + freeaddrinfo(sock->info.lsa->remote_list); + sock->info.lsa->current_remote = NULL; + sock->info.lsa->remote_list = NULL; + } - resolve_bind_local (sock); - resolve_remote (sock, 1, NULL, NULL); - } + resolve_remote (sock, 1, NULL, &sig_info->signal_received); } /* finalize socket initialization */ void link_socket_init_phase2 (struct link_socket *sock, const struct frame *frame, - volatile int *signal_received) + struct signal_info *sig_info) { - struct gc_arena gc = gc_new (); const char *remote_dynamic = NULL; - bool remote_changed = false; int sig_save = 0; ASSERT (sock); + ASSERT (sig_info); - if (signal_received && *signal_received) + if (sig_info->signal_received) { - sig_save = *signal_received; - *signal_received = 0; + sig_save = sig_info->signal_received; + sig_info->signal_received = 0; } /* initialize buffers */ @@ -1537,246 +1879,83 @@ 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 - || sock->info.proto == PROTO_TCPv6_SERVER) { - /* AF_INET as default (and fallback) for inetd */ - sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET; -#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 - sock->sd = - socket_listen_accept (sock->sd, - &sock->info.lsa->actual, - remote_dynamic, - &remote_changed, - &sock->info.lsa->local, - false, - sock->inetd == INETD_NOWAIT, - signal_received); - } - ASSERT (!remote_changed); - if (*signal_received) + phase2_inetd (sock, frame, remote_dynamic, &sig_info->signal_received); + if (sig_info->signal_received) goto done; + } else { - resolve_remote (sock, 2, &remote_dynamic, signal_received); + /* Second chance to resolv/create socket */ + resolve_remote (sock, 2, &remote_dynamic, &sig_info->signal_received); - if (*signal_received) - goto done; + /* If a valid remote has been found, create the socket with its addrinfo */ + if (sock->info.lsa->current_remote) + create_socket (sock, sock->info.lsa->current_remote); - /* TCP client/server */ - if (sock->info.proto == PROTO_TCPv4_SERVER - ||sock->info.proto == PROTO_TCPv6_SERVER) + /* If socket has not already been created create it now */ + if (sock->sd == SOCKET_UNDEFINED) { - switch (sock->mode) + /* If we have no --remote and have still not figured out the + * protocol family to use we will use the first of the bind */ + + if (sock->bind_local && !sock->remote_host && sock->info.lsa->bind_local) { - case LS_MODE_DEFAULT: - sock->sd = socket_listen_accept (sock->sd, - &sock->info.lsa->actual, - remote_dynamic, - &remote_changed, - &sock->info.lsa->local, - true, - false, - signal_received); - break; - case LS_MODE_TCP_LISTEN: - socket_do_listen (sock->sd, - &sock->info.lsa->local, - true, - false); - break; - case LS_MODE_TCP_ACCEPT_FROM: - sock->sd = socket_do_accept (sock->sd, - &sock->info.lsa->actual, - false); - if (!socket_defined (sock->sd)) - { - *signal_received = SIGTERM; - goto done; + /* Warn if this is because neither v4 or v6 was specified + * and we should not connect a remote */ + if (sock->info.af == AF_UNSPEC) + { + msg (M_WARN, "Could not determine IPv4/IPv6 protocol. Using %s", + addr_family_name(sock->info.lsa->bind_local->ai_family)); + sock->info.af = sock->info.lsa->bind_local->ai_family; } - tcp_connection_established (&sock->info.lsa->actual); - break; - default: - ASSERT (0); + + create_socket (sock, sock->info.lsa->bind_local); } } - else if (sock->info.proto == PROTO_TCPv4_CLIENT - ||sock->info.proto == PROTO_TCPv6_CLIENT) - { - -#ifdef GENERAL_PROXY_SUPPORT - bool proxy_retry = false; -#else - const bool proxy_retry = false; -#endif - do { - socket_connect (&sock->sd, - &sock->info.lsa->local, - sock->bind_local, - &sock->info.lsa->actual.dest, - sock->connection_profiles_defined, - remote_dynamic, - &remote_changed, - sock->connect_retry_seconds, - sock->connect_timeout, - sock->connect_retry_max, - sock->sockflags, - signal_received); - if (*signal_received) - goto done; - - if (false) - ; -#ifdef ENABLE_HTTP_PROXY - else if (sock->http_proxy) - { - proxy_retry = establish_http_proxy_passthru (sock->http_proxy, - sock->sd, - sock->proxy_dest_host, - sock->proxy_dest_port, - &sock->stream_buf.residual, - signal_received); - } -#endif -#ifdef ENABLE_SOCKS - else if (sock->socks_proxy) - { - establish_socks_proxy_passthru (sock->socks_proxy, - sock->sd, - sock->proxy_dest_host, - sock->proxy_dest_port, - signal_received); - } -#endif - if (proxy_retry) - { - openvpn_close_socket (sock->sd); - sock->sd = create_socket_tcp (AF_INET); - } - } while (proxy_retry); - } -#ifdef ENABLE_SOCKS - else if (sock->info.proto == PROTO_UDPv4 && sock->socks_proxy) + /* Socket still undefined, give a warning and abort connection */ + if (sock->sd == SOCKET_UNDEFINED) { - socket_connect (&sock->ctrl_sd, - &sock->info.lsa->local, - sock->bind_local, - &sock->info.lsa->actual.dest, - sock->connection_profiles_defined, - remote_dynamic, - &remote_changed, - sock->connect_retry_seconds, - sock->connect_timeout, - sock->connect_retry_max, - sock->sockflags, - signal_received); - - if (*signal_received) - goto done; - - establish_socks_proxy_udpassoc (sock->socks_proxy, - sock->ctrl_sd, - sock->sd, - &sock->socks_relay.dest, - signal_received); - - if (*signal_received) - goto done; - - sock->remote_host = sock->proxy_dest_host; - sock->remote_port = sock->proxy_dest_port; - sock->did_resolve_remote = false; - - addr_zero_host(&sock->info.lsa->actual.dest); - addr_zero_host(&sock->info.lsa->remote); - - resolve_remote (sock, 1, NULL, signal_received); - - if (*signal_received) - goto done; + msg (M_WARN, "Could not determine IPv4/IPv6 protocol"); + sig_info->signal_received = SIGUSR1; + goto done; } -#endif - if (*signal_received) + if (sig_info->signal_received) goto done; - if (remote_changed) + if (sock->info.proto == PROTO_TCP_SERVER) { - msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment"); - addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest); + phase2_tcp_server (sock, remote_dynamic, + &sig_info->signal_received); } - } - - /* set misc socket parameters */ - socket_set_flags (sock->sd, sock->sockflags); - - /* set socket to non-blocking mode */ - set_nonblock (sock->sd); - - /* set socket file descriptor to not pass across execs, so that - scripts don't have access to it */ - set_cloexec (sock->sd); - -#ifdef ENABLE_SOCKS - if (socket_defined (sock->ctrl_sd)) - set_cloexec (sock->ctrl_sd); -#endif - - /* set Path MTU discovery options on the socket */ - set_mtu_discover_type (sock->sd, sock->mtu_discover_type); + else if (sock->info.proto == PROTO_TCP_CLIENT) + { + phase2_tcp_client (sock, sig_info); -#if EXTENDED_SOCKET_ERROR_CAPABILITY - /* if the OS supports it, enable extended error passing on the socket */ - set_sock_extended_error_passing (sock->sd); + } + else if (sock->info.proto == PROTO_UDP && sock->socks_proxy) + { + phase2_socks_client (sock, sig_info); + } +#ifdef TARGET_ANDROID + if (sock->sd != -1) + protect_fd_nonlocal (sock->sd, &sock->info.lsa->actual.dest.addr.sa); #endif + if (sig_info->signal_received) + goto done; + } - /* print local address */ - { - 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), - print_link_socket_actual_ex (&sock->info.lsa->actual, - ":", - PS_SHOW_PORT_IF_DEFINED, - &gc)); - } + phase2_set_socket_flags(sock); + linksock_print_addr(sock); done: - if (sig_save && signal_received) + if (sig_save) { - if (!*signal_received) - *signal_received = sig_save; + if (!sig_info->signal_received) + sig_info->signal_received = sig_save; } - gc_free (&gc); } void @@ -1792,7 +1971,7 @@ link_socket_close (struct link_socket *sock) if (socket_defined (sock->sd)) { -#ifdef WIN32 +#ifdef _WIN32 close_net_event_win32 (&sock->listen_handle, sock->sd, 0); #endif if (!gremlin) @@ -1802,7 +1981,7 @@ link_socket_close (struct link_socket *sock) msg (M_WARN | M_ERRNO, "TCP/UDP: Close Socket failed"); } sock->sd = SOCKET_UNDEFINED; -#ifdef WIN32 +#ifdef _WIN32 if (!gremlin) { overlapped_io_close (&sock->reads); @@ -1811,14 +1990,12 @@ link_socket_close (struct link_socket *sock) #endif } -#ifdef ENABLE_SOCKS if (socket_defined (sock->ctrl_sd)) { if (openvpn_close_socket (sock->ctrl_sd)) msg (M_WARN | M_ERRNO, "TCP/UDP: Close Socket (ctrl_sd) failed"); sock->ctrl_sd = SOCKET_UNDEFINED; } -#endif stream_buf_close (&sock->stream_buf); free_buf (&sock->stream_buf_data); @@ -1844,17 +2021,15 @@ setenv_trusted (struct env_set *es, const struct link_socket_info *info) static void ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc) { - const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc); - const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc); + const char *host = print_sockaddr_ex (&info->lsa->actual.dest.addr.sa, " ", PS_SHOW_PORT , gc); if (include_cmd) - argv_printf (argv, "%sc %s %s", - info->ipchange_command, - ip, - port); + { + argv_parse_cmd (argv, info->ipchange_command); + argv_printf_cat (argv, "%s", host); + } else - argv_printf (argv, "%s %s", - ip, - port); + argv_printf (argv, "%s", host); + } void @@ -1911,6 +2086,7 @@ link_socket_bad_incoming_addr (struct buffer *buf, const struct link_socket_actual *from_addr) { struct gc_arena gc = gc_new (); + struct addrinfo* ai; switch(from_addr->dest.addr.sa.sa_family) { @@ -1920,7 +2096,12 @@ link_socket_bad_incoming_addr (struct buffer *buf, "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)); + print_sockaddr_ex (info->lsa->remote_list->ai_addr,":" ,PS_SHOW_PORT, &gc)); + /* print additional remote addresses */ + for(ai=info->lsa->remote_list->ai_next;ai;ai=ai->ai_next) { + msg(D_LINK_ERRORS,"or from peer address: %s", + print_sockaddr_ex(ai->ai_addr,":",PS_SHOW_PORT, &gc)); + } break; } buf->len = 0; @@ -1945,18 +2126,43 @@ link_socket_current_remote (const struct link_socket_info *info) * Maybe in the future consider PF_INET6 endpoints also ... * by now just ignore it * + * For --remote entries with multiple addresses this + * only return the actual endpoint we have sucessfully connected to */ if (lsa->actual.dest.addr.sa.sa_family != AF_INET) return IPV4_INVALID_ADDR; if (link_socket_actual_defined (&lsa->actual)) return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr); - else if (addr_defined (&lsa->remote)) - return ntohl (lsa->remote.addr.in4.sin_addr.s_addr); + else if (lsa->current_remote) + return ntohl (((struct sockaddr_in*)lsa->current_remote->ai_addr) + ->sin_addr.s_addr); else return 0; } +const struct in6_addr * +link_socket_current_remote_ipv6 (const struct link_socket_info *info) +{ + const struct link_socket_addr *lsa = info->lsa; + +/* This logic supports "redirect-gateway" semantic, + * for PF_INET6 routes over PF_INET6 endpoints + * + * For --remote entries with multiple addresses this + * only return the actual endpoint we have sucessfully connected to + */ + if (lsa->actual.dest.addr.sa.sa_family != AF_INET6) + return NULL; + + if (link_socket_actual_defined (&lsa->actual)) + return &(lsa->actual.dest.addr.in6.sin6_addr); + else if (lsa->current_remote) + return &(((struct sockaddr_in6*)lsa->current_remote->ai_addr) ->sin6_addr); + else + return NULL; +} + /* * Return a status string describing socket state. */ @@ -1970,7 +2176,7 @@ socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena { buf_printf (&out, "S%s", (s->rwflags_debug & EVENT_READ) ? "R" : "r"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&s->reads)); #endif @@ -1979,7 +2185,7 @@ socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena { buf_printf (&out, "S%s", (s->rwflags_debug & EVENT_WRITE) ? "W" : "w"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&s->writes)); #endif @@ -2019,7 +2225,7 @@ stream_buf_init (struct stream_buf *sb, sb->residual = alloc_buf (sb->maxlen); sb->error = false; #if PORT_SHARE - sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCPv4_SERVER)) + sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCP_SERVER)) ? PS_ENABLED : PS_DISABLED; #endif @@ -2154,7 +2360,7 @@ stream_buf_close (struct stream_buf* sb) event_t socket_listen_event_handle (struct link_socket *s) { -#ifdef WIN32 +#ifdef _WIN32 if (!defined_net_event_win32 (&s->listen_handle)) init_net_event_win32 (&s->listen_handle, FD_ACCEPT, s->sd, 0); return &s->listen_handle; @@ -2168,67 +2374,65 @@ socket_listen_event_handle (struct link_socket *s) */ const char * -print_sockaddr (const struct openvpn_sockaddr *addr, struct gc_arena *gc) -{ - return print_sockaddr_ex (addr, ":", PS_SHOW_PORT, gc); -} - -const char * -print_sockaddr_ex (const struct openvpn_sockaddr *addr, - const char* separator, - const unsigned int flags, - struct gc_arena *gc) +print_sockaddr_ex (const struct sockaddr *sa, + const char* separator, + const unsigned int flags, + struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); - bool addr_is_defined; - addr_is_defined = addr_defined (addr); - if (!addr_is_defined) { - return "[undef]"; - } - switch(addr->addr.sa.sa_family) + bool addr_is_defined = false; + char hostaddr[NI_MAXHOST] = ""; + char servname[NI_MAXSERV] = ""; + int status; + + socklen_t salen = 0; + switch(sa->sa_family) { case AF_INET: - { - 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_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED))) - && port) - { - if (separator) - buf_printf (&out, "%s", separator); - - buf_printf (&out, "%d", port); - } - } + if (!(flags & PS_DONT_SHOW_FAMILY)) + buf_puts (&out, "[AF_INET]"); + salen = sizeof (struct sockaddr_in); + addr_is_defined = ((struct sockaddr_in*) sa)->sin_addr.s_addr != 0; break; case AF_INET6: - { - 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); - } - } + if (!(flags & PS_DONT_SHOW_FAMILY)) + buf_puts (&out, "[AF_INET6]"); + salen = sizeof (struct sockaddr_in6); + addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*) sa)->sin6_addr); break; + case AF_UNSPEC: + if (!(flags & PS_DONT_SHOW_FAMILY)) + return "[AF_UNSPEC]"; + else + return ""; default: ASSERT(0); } + + status = getnameinfo(sa, salen, hostaddr, sizeof (hostaddr), + servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV); + + if(status!=0) { + buf_printf(&out,"[nameinfo() err: %s]",gai_strerror(status)); + return BSTR(&out); + } + + if (!(flags & PS_DONT_SHOW_ADDR)) + { + if (addr_is_defined) + buf_puts (&out, hostaddr); + else + buf_puts (&out, "[undef]"); + } + + if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED)) + { + if (separator) + buf_puts (&out, separator); + + buf_puts (&out, servname); + } + return BSTR (&out); } @@ -2252,7 +2456,7 @@ print_link_socket_actual_ex (const struct link_socket_actual *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)); + buf_printf (&out, "%s", print_sockaddr_ex (&act->dest.addr.sa, separator, flags, gc)); #if ENABLE_IP_PKTINFO if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act)) { @@ -2263,7 +2467,7 @@ print_link_socket_actual_ex (const struct link_socket_actual *act, struct openvpn_sockaddr sa; CLEAR (sa); sa.addr.in4.sin_family = AF_INET; -#ifdef IP_PKTINFO +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst; if_indextoname(act->pi.in4.ipi_ifindex, ifname); #elif defined(IP_RECVDSTADDR) @@ -2273,7 +2477,7 @@ print_link_socket_actual_ex (const struct link_socket_actual *act, #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), + print_sockaddr_ex (&sa.addr.sa, separator, 0, gc), ifname); } break; @@ -2392,9 +2596,20 @@ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openv } 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); + if ( IN6_IS_ADDR_V4MAPPED( &addr->addr.in6.sin6_addr )) + { + struct in_addr ia; + memcpy (&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12], + sizeof (ia.s_addr)); + openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix); + openvpn_snprintf (buf, sizeof(buf), "%s", inet_ntoa(ia) ); + } + else + { + 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) @@ -2451,22 +2666,28 @@ 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; + sa_family_t proto_af; + int proto; }; /* Indexed by PROTO_x */ -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}, - {"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}, +static const struct proto_names proto_names[] = { + {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE}, + /* try IPv4 and IPv6 (client), bind dual-stack (server) */ + {"udp", "UDP", AF_UNSPEC, PROTO_UDP}, + {"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER}, + {"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT}, + {"tcp", "TCP", AF_UNSPEC, PROTO_TCP}, + /* force IPv4 */ + {"udp4", "UDPv4", AF_INET, PROTO_UDP}, + {"tcp4-server","TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER}, + {"tcp4-client","TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT}, + {"tcp4", "TCPv4", AF_INET, PROTO_TCP}, + /* force IPv6 */ + {"udp6" ,"UDPv6", AF_INET6, PROTO_UDP}, + {"tcp6-server","TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER}, + {"tcp6-client","TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT}, + {"tcp6" ,"TCPv6", AF_INET6, PROTO_TCP}, }; bool @@ -2474,59 +2695,66 @@ proto_is_net(int proto) { if (proto < 0 || proto >= PROTO_N) ASSERT(0); - return proto_names[proto].is_net; + return proto != PROTO_NONE; } bool proto_is_dgram(int proto) { - if (proto < 0 || proto >= PROTO_N) - ASSERT(0); - return proto_names[proto].is_dgram; + return proto_is_udp(proto); } + 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; + return proto == PROTO_UDP; } + 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; + return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER; } int ascii2proto (const char* proto_name) { int i; - ASSERT (PROTO_N == SIZE (proto_names)); - for (i = 0; i < PROTO_N; ++i) + for (i = 0; i < SIZE (proto_names); ++i) if (!strcmp (proto_name, proto_names[i].short_form)) - return i; + return proto_names[i].proto; return -1; } +sa_family_t +ascii2af (const char* proto_name) +{ + int i; + for (i = 0; i < SIZE (proto_names); ++i) + if (!strcmp (proto_name, proto_names[i].short_form)) + return proto_names[i].proto_af; + return 0; +} + const char * -proto2ascii (int proto, bool display_form) +proto2ascii (int proto, sa_family_t af, bool display_form) { - ASSERT (PROTO_N == SIZE (proto_names)); - if (proto < 0 || proto >= PROTO_N) - return "[unknown protocol]"; - else if (display_form) - return proto_names[proto].display_form; - else - return proto_names[proto].short_form; + unsigned int i; + for (i = 0; i < SIZE (proto_names); ++i) + { + if(proto_names[i].proto_af == af && proto_names[i].proto == proto) + { + if(display_form) + return proto_names[i].display_form; + else + return proto_names[i].short_form; + } + } + + return "[unknown protocol]"; } const char * @@ -2535,40 +2763,15 @@ proto2ascii_all (struct gc_arena *gc) struct buffer out = alloc_buf_gc (256, gc); int i; - ASSERT (PROTO_N == SIZE (proto_names)); - for (i = 0; i < PROTO_N; ++i) + for (i = 0; i < SIZE (proto_names); ++i) { if (i) buf_printf(&out, " "); - buf_printf(&out, "[%s]", proto2ascii(i, false)); + buf_printf(&out, "[%s]", proto_names[i].short_form); } return BSTR (&out); } -int -addr_guess_family(int proto, const char *name) -{ - unsigned short ret; - if (proto) - { - return proto_sa_family(proto); /* already stamped */ - } - 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; - } - } - return AF_INET; /* default */ -} const char * addr_family_name (int af) { @@ -2592,31 +2795,22 @@ addr_family_name (int af) * has always sent UDPv4, TCPv4 over the wire. Keep these * strings for backward compatbility */ -int +const char* proto_remote (int proto, bool remote) { ASSERT (proto >= 0 && proto < PROTO_N); - if (remote) - { - switch (proto) - { - case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT; - case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER; - case PROTO_TCPv6_SERVER: return PROTO_TCPv4_CLIENT; - case PROTO_TCPv6_CLIENT: return PROTO_TCPv4_SERVER; - case PROTO_UDPv6: return PROTO_UDPv4; - } - } - else - { - switch (proto) - { - case PROTO_TCPv6_SERVER: return PROTO_TCPv4_SERVER; - case PROTO_TCPv6_CLIENT: return PROTO_TCPv4_CLIENT; - case PROTO_UDPv6: return PROTO_UDPv4; - } - } - return proto; + if (proto == PROTO_UDP) + return "UDPv4"; + + if ( (remote && proto == PROTO_TCP_CLIENT) || + (!remote && proto == PROTO_TCP_SERVER)) + return "TCPv4_SERVER"; + if ( (remote && proto == PROTO_TCP_SERVER) || + (!remote && proto == PROTO_TCP_CLIENT)) + return "TCPv4_CLIENT"; + + ASSERT (0); + return ""; /* Make the compiler happy */ } /* @@ -2643,7 +2837,7 @@ link_socket_read_tcp (struct link_socket *sock, if (!sock->stream_buf.residual_fully_formed) { -#ifdef WIN32 +#ifdef _WIN32 len = socket_finalize (sock->sd, &sock->reads, buf, NULL); #else struct buffer frag; @@ -2668,51 +2862,39 @@ link_socket_read_tcp (struct link_socket *sock, return buf->len = 0; /* no error, but packet is still incomplete */ } -#ifndef WIN32 +#ifndef _WIN32 #if ENABLE_IP_PKTINFO -#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */ -struct openvpn_in4_pktinfo -{ - struct cmsghdr cmsghdr; -#ifdef HAVE_IN_PKTINFO - struct in_pktinfo pi4; -#elif defined(IP_RECVDSTADDR) - struct in_addr pi4; +/* make the buffer large enough to handle ancilliary socket data for + * both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292) + */ +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) +#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof (struct in6_pktinfo)), \ + CMSG_SPACE(sizeof (struct in_pktinfo)) ) +#else +#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof (struct in6_pktinfo)), \ + CMSG_SPACE(sizeof (struct in_addr)) ) #endif -}; -struct openvpn_in6_pktinfo -{ - struct cmsghdr cmsghdr; - struct in6_pktinfo pi6; -}; - -union openvpn_pktinfo { - struct openvpn_in4_pktinfo msgpi4; - struct openvpn_in6_pktinfo msgpi6; -}; -#pragma pack() static socklen_t link_socket_read_udp_posix_recvmsg (struct link_socket *sock, struct buffer *buf, - int maxsize, struct link_socket_actual *from) { struct iovec iov; - union openvpn_pktinfo opi; + uint8_t pktinfo_buf[PKTINFO_BUF_SIZE]; struct msghdr mesg; socklen_t fromlen = sizeof (from->dest.addr); iov.iov_base = BPTR (buf); - iov.iov_len = maxsize; + iov.iov_len = buf_forward_capacity_total (buf); mesg.msg_iov = &iov; mesg.msg_iovlen = 1; mesg.msg_name = &from->dest.addr; mesg.msg_namelen = fromlen; - mesg.msg_control = &opi; - mesg.msg_controllen = sizeof opi; + mesg.msg_control = pktinfo_buf; + mesg.msg_controllen = sizeof pktinfo_buf; buf->len = recvmsg (sock->sd, &mesg, 0); if (buf->len >= 0) { @@ -2721,18 +2903,19 @@ 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 +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) && cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_pktinfo)) ) #elif defined(IP_RECVDSTADDR) && cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_addr)) ) #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 +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg); from->pi.in4.ipi_ifindex = pkti->ipi_ifindex; from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; @@ -2746,13 +2929,18 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock, && CMSG_NXTHDR (&mesg, cmsg) == NULL && cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO - && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo)) + && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct 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; } + else if (cmsg != NULL) + { + msg(M_WARN, "CMSG received that cannot be parsed (cmsg_level=%d, cmsg_type=%d, cmsg=len=%d)", (int)cmsg->cmsg_level, (int)cmsg->cmsg_type, (int)cmsg->cmsg_len ); + } } + return fromlen; } #endif @@ -2760,21 +2948,20 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock, int link_socket_read_udp_posix (struct link_socket *sock, struct buffer *buf, - int maxsize, struct link_socket_actual *from) { socklen_t fromlen = sizeof (from->dest.addr); - socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto)); + socklen_t expectedlen = af_addr_size(sock->info.af); addr_zero_host(&from->dest); - ASSERT (buf_safe (buf, maxsize)); #if ENABLE_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); + if (sock->info.proto == PROTO_UDP && sock->sockflags & SF_USE_IP_PKTINFO) + fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, from); else #endif - buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0, + buf->len = recvfrom (sock->sd, BPTR (buf), buf_forward_capacity(buf), 0, &from->dest.addr.sa, &fromlen); + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ if (buf->len >= 0 && expectedlen && fromlen != expectedlen) bad_address_length (fromlen, expectedlen); return buf->len; @@ -2796,7 +2983,7 @@ link_socket_write_tcp (struct link_socket *sock, ASSERT (len <= sock->stream_buf.maxlen); len = htonps (len); ASSERT (buf_write_prepend (buf, &len, sizeof (len))); -#ifdef WIN32 +#ifdef _WIN32 return link_socket_write_win32 (sock, buf, to); #else return link_socket_write_tcp_posix (sock, buf, to); @@ -2805,7 +2992,7 @@ link_socket_write_tcp (struct link_socket *sock, #if ENABLE_IP_PKTINFO -int +size_t link_socket_write_udp_posix_sendmsg (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) @@ -2813,24 +3000,24 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, struct iovec iov; struct msghdr mesg; struct cmsghdr *cmsg; - union openvpn_pktinfo opi; + uint8_t pktinfo_buf[PKTINFO_BUF_SIZE]; iov.iov_base = BPTR (buf); iov.iov_len = BLEN (buf); mesg.msg_iov = &iov; mesg.msg_iovlen = 1; - switch (sock->info.lsa->remote.addr.sa.sa_family) + switch (to->dest.addr.sa.sa_family) { case AF_INET: { mesg.msg_name = &to->dest.addr.sa; mesg.msg_namelen = sizeof (struct sockaddr_in); - mesg.msg_control = &opi; + mesg.msg_control = pktinfo_buf; mesg.msg_flags = 0; -#ifdef HAVE_IN_PKTINFO - mesg.msg_controllen = sizeof (struct openvpn_in4_pktinfo); +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) + mesg.msg_controllen = CMSG_SPACE(sizeof (struct in_pktinfo)); cmsg = CMSG_FIRSTHDR (&mesg); - cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo); + cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_pktinfo)); cmsg->cmsg_level = SOL_IP; cmsg->cmsg_type = IP_PKTINFO; { @@ -2841,7 +3028,7 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, pkti->ipi_addr.s_addr = 0; } #elif defined(IP_RECVDSTADDR) - ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(opi) ); + ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(pktinfo_buf) ); mesg.msg_controllen = CMSG_SPACE(sizeof (struct in_addr)); cmsg = CMSG_FIRSTHDR (&mesg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); @@ -2858,13 +3045,16 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, struct in6_pktinfo *pkti6; mesg.msg_name = &to->dest.addr.sa; mesg.msg_namelen = sizeof (struct sockaddr_in6); - mesg.msg_control = &opi; - mesg.msg_controllen = sizeof (struct openvpn_in6_pktinfo); + + ASSERT( CMSG_SPACE(sizeof (struct in6_pktinfo)) <= sizeof(pktinfo_buf) ); + mesg.msg_control = pktinfo_buf; + mesg.msg_controllen = CMSG_SPACE(sizeof (struct in6_pktinfo)); mesg.msg_flags = 0; cmsg = CMSG_FIRSTHDR (&mesg); - cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct 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; @@ -2881,7 +3071,7 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock, * Win32 overlapped socket I/O functions. */ -#ifdef WIN32 +#ifdef _WIN32 int socket_recv_queue (struct link_socket *sock, int maxsize) @@ -2919,10 +3109,7 @@ socket_recv_queue (struct link_socket *sock, int maxsize) if (proto_is_udp(sock->info.proto)) { sock->reads.addr_defined = true; - if (sock->info.proto == PROTO_UDPv6) - sock->reads.addrlen = sizeof (sock->reads.addr6); - else - sock->reads.addrlen = sizeof (sock->reads.addr); + sock->reads.addrlen = sizeof (sock->reads.addr6); status = WSARecvFrom( sock->sd, wsabuf, @@ -2954,9 +3141,10 @@ socket_recv_queue (struct link_socket *sock, int maxsize) if (!status) /* operation completed immediately? */ { - 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); + /* FIXME: won't do anything when sock->info.af == AF_UNSPEC */ + int af_len = af_addr_size (sock->info.af); + if (sock->reads.addr_defined && af_len && sock->reads.addrlen != af_len) + bad_address_length (sock->reads.addrlen, af_len); sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN; /* since we got an immediate return, we must signal the event object ourselves */ @@ -3018,7 +3206,7 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li { /* set destination address for UDP writes */ sock->writes.addr_defined = true; - if (sock->info.proto == PROTO_UDPv6) + if (to->dest.addr.sa.sa_family == AF_INET6) { sock->writes.addr6 = to->dest.addr.in6; sock->writes.addrlen = sizeof (sock->writes.addr6); @@ -3191,7 +3379,7 @@ socket_finalize (SOCKET s, case sizeof(struct sockaddr_in): case sizeof(struct sockaddr_in6): /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 - * under WIN32*/ + * under _WIN32*/ case sizeof(struct sockaddr_in6)-4: break; default: @@ -3217,7 +3405,7 @@ socket_finalize (SOCKET s, return ret; } -#endif /* WIN32 */ +#endif /* _WIN32 */ /* * Socket event notification @@ -3238,7 +3426,7 @@ socket_set (struct link_socket *s, rwflags &= ~EVENT_READ; } -#ifdef WIN32 +#ifdef _WIN32 if (rwflags & EVENT_READ) socket_recv_queue (s, 0); #endif diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index b7a4e01..2a82d88 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -39,7 +39,7 @@ /* * OpenVPN's default port number as assigned by IANA. */ -#define OPENVPN_PORT 1194 +#define OPENVPN_PORT "1194" /* * Number of seconds that "resolv-retry infinite" @@ -72,14 +72,25 @@ struct openvpn_sockaddr } addr; }; +/* struct to hold preresolved host names */ +struct cached_dns_entry { + const char *hostname; + const char *servname; + int ai_family; + int flags; + struct addrinfo *ai; + struct cached_dns_entry *next; +}; + /* actual address of remote, based on source address of received packets */ 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 union { -#ifdef HAVE_IN_PKTINFO +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) struct in_pktinfo in4; #elif defined(IP_RECVDSTADDR) struct in_addr in4; @@ -92,8 +103,10 @@ struct link_socket_actual /* IP addresses which are persistant across SIGUSR1s */ struct link_socket_addr { - struct openvpn_sockaddr local; - struct openvpn_sockaddr remote; /* initial remote */ + struct addrinfo* bind_local; + struct addrinfo* remote_list; /* complete remote list */ + struct addrinfo* current_remote; /* remote used in the + current connection attempt */ struct link_socket_actual actual; /* reply to this address */ }; @@ -105,6 +118,8 @@ struct link_socket_info const struct plugin_list *plugins; bool remote_float; int proto; /* Protocol (PROTO_x defined below) */ + sa_family_t af; /* Address family like AF_INET, AF_INET6 or AF_UNSPEC*/ + bool bind_ipv6_only; int mtu_changed; /* Set to true when mtu value is changed */ }; @@ -152,12 +167,9 @@ struct link_socket struct link_socket_info info; socket_descriptor_t sd; - -#ifdef ENABLE_SOCKS socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */ -#endif -#ifdef WIN32 +#ifdef _WIN32 struct overlapped_io reads; struct overlapped_io writes; struct rw_handle rw_handle; @@ -170,13 +182,11 @@ struct link_socket /* used for long-term queueing of pre-accepted socket listen */ bool listen_persistent_queued; - /* Does config file contain any ... blocks? */ - bool connection_profiles_defined; - const char *remote_host; - int remote_port; + const char *remote_port; const char *local_host; - int local_port; + const char *local_port; + struct cached_dns_entry *dns_cache; bool bind_local; # define INETD_NONE 0 @@ -190,45 +200,39 @@ struct link_socket int mode; int resolve_retry_seconds; - int connect_retry_seconds; - int connect_timeout; - int connect_retry_max; int mtu_discover_type; struct socket_buffer_size socket_buffer_sizes; int mtu; /* OS discovered MTU, or 0 if unknown */ - bool did_resolve_remote; - # define SF_USE_IP_PKTINFO (1<<0) # 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; + int mark; /* for stream sockets */ struct stream_buf stream_buf; struct buffer stream_buf_data; bool stream_reset; -#ifdef ENABLE_HTTP_PROXY /* HTTP proxy */ struct http_proxy_info *http_proxy; -#endif -#ifdef ENABLE_SOCKS /* Socks proxy */ struct socks_proxy_info *socks_proxy; struct link_socket_actual socks_relay; /* Socks UDP relay address */ -#endif -#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS) /* The OpenVPN server we will use the proxy to connect to */ const char *proxy_dest_host; - int proxy_dest_port; -#endif + const char *proxy_dest_port; + + /* Pointer to the server-poll to trigger the timeout in function which have + * their own loop instead of using the main oop */ + struct event_timeout* server_poll_timeout; #if PASSTOS_CAPABILITY /* used to get/set TOS. */ @@ -253,7 +257,7 @@ struct link_socket #define MSG_NOSIGNAL 0 #endif -#ifdef WIN32 +#ifdef _WIN32 #define openvpn_close_socket(s) closesocket(s) @@ -278,34 +282,36 @@ int socket_finalize ( struct link_socket *link_socket_new (void); void socket_bind (socket_descriptor_t sd, - struct openvpn_sockaddr *local, - const char *prefix); + struct addrinfo *local, + int af_family, + const char *prefix, + bool ipv6only); int openvpn_connect (socket_descriptor_t sd, - struct openvpn_sockaddr *remote, + const struct sockaddr *remote, int connect_timeout, volatile int *signal_received); + + /* * Initialize link_socket object. */ void link_socket_init_phase1 (struct link_socket *sock, - const bool connection_profiles_defined, const char *local_host, - int local_port, + const char *local_port, const char *remote_host, - int remote_port, + const char *remote_port, + struct cached_dns_entry *dns_cache, int proto, + sa_family_t af, + bool bind_ipv6_only, int mode, const struct link_socket *accept_from, -#ifdef ENABLE_HTTP_PROXY struct http_proxy_info *http_proxy, -#endif -#ifdef ENABLE_SOCKS struct socks_proxy_info *socks_proxy, -#endif #ifdef ENABLE_DEBUG int gremlin, #endif @@ -316,18 +322,18 @@ link_socket_init_phase1 (struct link_socket *sock, const char *ipchange_command, const struct plugin_list *plugins, int resolve_retry_seconds, - int connect_retry_seconds, - int connect_timeout, - int connect_retry_max, int mtu_discover_type, int rcvbuf, int sndbuf, int mark, + struct event_timeout* server_poll_timeout, unsigned int sockflags); void link_socket_init_phase2 (struct link_socket *sock, const struct frame *frame, - volatile int *signal_received); + struct signal_info *sig_info); + +void do_preresolve(struct context *c); void socket_adjust_frame_parameters (struct frame *frame, int proto); @@ -341,15 +347,37 @@ void sd_close (socket_descriptor_t *sd); #define PS_SHOW_PORT (1<<1) #define PS_SHOW_PKTINFO (1<<2) #define PS_DONT_SHOW_ADDR (1<<3) +#define PS_DONT_SHOW_FAMILY (1<<4) -const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr, +const char *print_sockaddr_ex (const struct sockaddr *addr, const char* separator, const unsigned int flags, struct gc_arena *gc); +static inline +const char *print_openvpn_sockaddr_ex (const struct openvpn_sockaddr *addr, + const char* separator, + const unsigned int flags, + struct gc_arena *gc) +{ + return print_sockaddr_ex(&addr->addr.sa, separator, flags, gc); +} + +static inline +const char *print_openvpn_sockaddr (const struct openvpn_sockaddr *addr, + struct gc_arena *gc) +{ + return print_sockaddr_ex (&addr->addr.sa, ":", PS_SHOW_PORT, gc); +} + +static inline +const char *print_sockaddr (const struct sockaddr *addr, + struct gc_arena *gc) +{ + return print_sockaddr_ex (addr, ":", PS_SHOW_PORT, gc); +} + -const char *print_sockaddr (const struct openvpn_sockaddr *addr, - struct gc_arena *gc); const char *print_link_socket_actual_ex (const struct link_socket_actual *act, const char* separator, @@ -395,6 +423,8 @@ void bad_address_length (int actual, int expected); */ #define IPV4_INVALID_ADDR 0xffffffff in_addr_t link_socket_current_remote (const struct link_socket_info *info); +const struct in6_addr * link_socket_current_remote_ipv6 + (const struct link_socket_info *info); void link_socket_connection_initiated (const struct buffer *buf, struct link_socket_info *info, @@ -406,6 +436,9 @@ void link_socket_bad_incoming_addr (struct buffer *buf, const struct link_socket_info *info, const struct link_socket_actual *from_addr); +void set_actual_address (struct link_socket_actual* actual, + struct addrinfo* ai); + void link_socket_bad_outgoing_addr (void); void setenv_trusted (struct env_set *es, const struct link_socket_info *info); @@ -429,7 +462,7 @@ bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn); bool mac_addr_safe (const char *mac_addr); bool ipv6_addr_safe (const char *ipv6_text_addr); -socket_descriptor_t create_socket_tcp (int af); +socket_descriptor_t create_socket_tcp (struct addrinfo*); socket_descriptor_t socket_do_accept (socket_descriptor_t sd, struct link_socket_actual *act, @@ -481,6 +514,10 @@ bool unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int * #define GETADDR_TRY_ONCE (1<<7) #define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8) #define GETADDR_RANDOMIZE (1<<9) +#define GETADDR_PASSIVE (1<<10) +#define GETADDR_DATAGRAM (1<<11) + +#define GETADDR_CACHE_MASK (GETADDR_DATAGRAM|GETADDR_PASSIVE) in_addr_t getaddr (unsigned int flags, const char *hostname, @@ -490,6 +527,7 @@ in_addr_t getaddr (unsigned int flags, int openvpn_getaddrinfo (unsigned int flags, const char *hostname, + const char *servname, int resolve_retry_seconds, volatile int *signal_received, int ai_family, @@ -505,21 +543,18 @@ int openvpn_getaddrinfo (unsigned int flags, */ enum proto_num { PROTO_NONE, /* catch for uninitialized */ - PROTO_UDPv4, - PROTO_TCPv4_SERVER, - PROTO_TCPv4_CLIENT, - PROTO_TCPv4, - PROTO_UDPv6, - PROTO_TCPv6_SERVER, - PROTO_TCPv6_CLIENT, - PROTO_TCPv6, + PROTO_UDP, + PROTO_TCP, + PROTO_TCP_SERVER, + PROTO_TCP_CLIENT, PROTO_N }; int ascii2proto (const char* proto_name); -const char *proto2ascii (int proto, bool display_form); +sa_family_t ascii2af (const char* proto_name); +const char *proto2ascii (int proto, sa_family_t af, bool display_form); const char *proto2ascii_all (struct gc_arena *gc); -int proto_remote (int proto, bool remote); +const char *proto_remote (int proto, bool remote); const char *addr_family_name(int af); /* @@ -543,12 +578,6 @@ datagram_overhead (int proto) * Misc inline functions */ -static inline bool -legal_ipv4_port (int port) -{ - return port > 0 && port < 65536; -} - static inline bool link_socket_proto_connection_oriented (int proto) { @@ -574,13 +603,30 @@ addr_defined (const struct openvpn_sockaddr *addr) default: return 0; } } + +static inline bool +addr_local (const struct sockaddr *addr) +{ + if (!addr) + return false; + switch (addr->sa_family) { + case AF_INET: + return ((const struct sockaddr_in*)addr)->sin_addr.s_addr == htonl(INADDR_LOOPBACK); + case AF_INET6: + return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6*)addr)->sin6_addr); + default: + return false; + } +} + + 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 +#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0; #elif defined(IP_RECVDSTADDR) case AF_INET: return lsa->pi.in4.s_addr != 0; @@ -613,6 +659,29 @@ addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2 return false; } +static inline bool +addrlist_match (const struct openvpn_sockaddr *a1, const struct addrinfo *addrlist) +{ + const struct addrinfo *curele; + for (curele = addrlist; curele; curele=curele->ai_next) + { + switch(a1->addr.sa.sa_family) + { + case AF_INET: + if (a1->addr.in4.sin_addr.s_addr == ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr) + return true; + break; + case AF_INET6: + if (IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &((struct sockaddr_in6*) curele->ai_addr)->sin6_addr)) + return true; + break; + default: + ASSERT(0); + } + } + return false; +} + static inline in_addr_t addr_host (const struct openvpn_sockaddr *addr) { @@ -626,6 +695,36 @@ addr_host (const struct openvpn_sockaddr *addr) return ntohl (addr->addr.in4.sin_addr.s_addr); } + +static inline bool +addrlist_port_match (const struct openvpn_sockaddr *a1, const struct addrinfo *a2) +{ + const struct addrinfo *curele; + for(curele=a2;curele;curele = curele->ai_next) + { + switch(a1->addr.sa.sa_family) + { + case AF_INET: + if (curele->ai_family == AF_INET + && a1->addr.in4.sin_addr.s_addr == ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr + && a1->addr.in4.sin_port == ((struct sockaddr_in*)curele->ai_addr)->sin_port) + return true; + break; + case AF_INET6: + if (curele->ai_family == AF_INET6 + && IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &((struct sockaddr_in6*) curele->ai_addr)->sin6_addr) + && a1->addr.in6.sin6_port == ((struct sockaddr_in6*) curele->ai_addr)->sin6_port) + return true; + break; + default: + ASSERT(0); + } + } + return false; +} + + + static inline bool addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) { @@ -634,7 +733,7 @@ addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockadd 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; case AF_INET6: - return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr) + 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; } ASSERT(0); @@ -651,6 +750,17 @@ addr_match_proto (const struct openvpn_sockaddr *a1, : addr_port_match (a1, a2); } + +static inline bool +addrlist_match_proto (const struct openvpn_sockaddr *a1, + struct addrinfo *addr_list, + const int proto) +{ + return link_socket_proto_connection_oriented (proto) + ? addrlist_match (a1, addr_list) + : addrlist_port_match (a1, addr_list); +} + static inline void addr_zero_host(struct openvpn_sockaddr *addr) { @@ -670,28 +780,15 @@ 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; - case AF_INET6: - dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr; - break; - } -} - 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); +int addr_guess_family(sa_family_t af,const char *name); static inline int -af_addr_size(unsigned short af) +af_addr_size(sa_family_t af) { switch(af) { case AF_INET: return sizeof (struct sockaddr_in); @@ -745,7 +842,7 @@ socket_connection_reset (const struct link_socket *sock, int status) else if (status < 0) { const int err = openvpn_errno (); -#ifdef WIN32 +#ifdef _WIN32 return err == WSAECONNRESET || err == WSAECONNABORTED; #else return err == ECONNRESET; @@ -767,9 +864,9 @@ link_socket_verify_incoming_addr (struct buffer *buf, case AF_INET: if (!link_socket_actual_defined (from_addr)) return false; - if (info->remote_float || !addr_defined (&info->lsa->remote)) + if (info->remote_float || (!info->lsa->remote_list)) return true; - if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) + if (addrlist_match_proto (&from_addr->dest, info->lsa->remote_list, info->proto)) return true; } } @@ -806,13 +903,15 @@ link_socket_set_outgoing_addr (const struct buffer *buf, { struct link_socket_addr *lsa = info->lsa; if ( - /* new or changed address? */ - (!info->connection_established - || !addr_match_proto (&act->dest, &lsa->actual.dest, info->proto)) - /* address undef or address == remote or --float */ - && (info->remote_float - || !addr_defined (&lsa->remote) - || addr_match_proto (&act->dest, &lsa->remote, info->proto)) + /* new or changed address? */ + (!info->connection_established + || !addr_match_proto (&act->dest, &lsa->actual.dest, info->proto) + ) + && + /* address undef or address == remote or --float */ + (info->remote_float || + (!lsa->remote_list || addrlist_match_proto (&act->dest, lsa->remote_list, info->proto)) + ) ) { link_socket_connection_initiated (buf, info, act, common_name, es); @@ -851,7 +950,7 @@ stream_buf_read_setup (struct link_socket* sock) int link_socket_read_tcp (struct link_socket *sock, struct buffer *buf); -#ifdef WIN32 +#ifdef _WIN32 static inline int link_socket_read_udp_win32 (struct link_socket *sock, @@ -865,7 +964,6 @@ link_socket_read_udp_win32 (struct link_socket *sock, int link_socket_read_udp_posix (struct link_socket *sock, struct buffer *buf, - int maxsize, struct link_socket_actual *from); #endif @@ -874,17 +972,16 @@ int link_socket_read_udp_posix (struct link_socket *sock, static inline int link_socket_read (struct link_socket *sock, struct buffer *buf, - int maxsize, struct link_socket_actual *from) { if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ { int res; -#ifdef WIN32 +#ifdef _WIN32 res = link_socket_read_udp_win32 (sock, buf, from); #else - res = link_socket_read_udp_posix (sock, buf, maxsize, from); + res = link_socket_read_udp_posix (sock, buf, from); #endif return res; } @@ -909,7 +1006,7 @@ int link_socket_write_tcp (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to); -#ifdef WIN32 +#ifdef _WIN32 static inline int link_socket_write_win32 (struct link_socket *sock, @@ -936,13 +1033,13 @@ link_socket_write_win32 (struct link_socket *sock, #else -static inline int +static inline size_t link_socket_write_udp_posix (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) { #if ENABLE_IP_PKTINFO - int link_socket_write_udp_posix_sendmsg (struct link_socket *sock, + size_t link_socket_write_udp_posix_sendmsg (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to); @@ -956,7 +1053,7 @@ link_socket_write_udp_posix (struct link_socket *sock, (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); } -static inline int +static inline size_t link_socket_write_tcp_posix (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) @@ -966,12 +1063,12 @@ link_socket_write_tcp_posix (struct link_socket *sock, #endif -static inline int +static inline size_t link_socket_write_udp (struct link_socket *sock, struct buffer *buf, struct link_socket_actual *to) { -#ifdef WIN32 +#ifdef _WIN32 return link_socket_write_win32 (sock, buf, to); #else return link_socket_write_udp_posix (sock, buf, to); @@ -1041,7 +1138,7 @@ socket_read_residual (const struct link_socket *s) static inline event_t socket_event_handle (const struct link_socket *s) { -#ifdef WIN32 +#ifdef _WIN32 return &s->rw_handle; #else return s->sd; @@ -1072,7 +1169,7 @@ socket_set_listen_persistent (struct link_socket *s, static inline void socket_reset_listen_persistent (struct link_socket *s) { -#ifdef WIN32 +#ifdef _WIN32 reset_net_event_win32 (&s->listen_handle, s->sd); #endif } diff --git a/src/openvpn/socks.c b/src/openvpn/socks.c index 57dc02a..5a9ea6c 100644 --- a/src/openvpn/socks.c +++ b/src/openvpn/socks.c @@ -38,8 +38,6 @@ #include "syshead.h" -#ifdef ENABLE_SOCKS - #include "common.h" #include "misc.h" #include "win32.h" @@ -55,22 +53,21 @@ void socks_adjust_frame_parameters (struct frame *frame, int proto) { - if (proto == PROTO_UDPv4) + if (proto == PROTO_UDP) frame_add_to_extra_link (frame, 10); } struct socks_proxy_info * socks_proxy_new (const char *server, - int port, - const char *authfile, - bool retry) + const char *port, + const char *authfile) { struct socks_proxy_info *p; ALLOC_OBJ_CLEAR (p, struct socks_proxy_info); ASSERT (server); - ASSERT (legal_ipv4_port (port)); + ASSERT (port); strncpynt (p->server, server, sizeof (p->server)); p->port = port; @@ -80,7 +77,6 @@ socks_proxy_new (const char *server, else p->authfile[0] = 0; - p->retry = retry; p->defined = true; return p; @@ -404,11 +400,27 @@ recv_socks_reply (socket_descriptor_t sd, return true; } +static int +port_from_servname(const char* servname) +{ + int port =0; + port = atoi(servname); + if(port >0 && port < 65536) + return port; + + struct servent* service; + service = getservbyname(servname, NULL); + if(service) + return service->s_port; + + return 0; +} + void establish_socks_proxy_passthru (struct socks_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ - const int port, /* openvpn server port */ + const char *servname, /* openvpn server port */ volatile int *signal_received) { char buf[128]; @@ -429,6 +441,13 @@ establish_socks_proxy_passthru (struct socks_proxy_info *p, buf[4] = (char) len; memcpy(buf + 5, host, len); + int port = port_from_servname (servname); + if (port ==0) + { + msg (D_LINK_ERRORS, "establish_socks_proxy_passthrough: Cannot convert %s to port number", servname); + goto error; + } + buf[5 + len] = (char) (port >> 8); buf[5 + len + 1] = (char) (port & 0xff); @@ -441,6 +460,7 @@ establish_socks_proxy_passthru (struct socks_proxy_info *p, } } + /* receive reply from Socks proxy and discard */ if (!recv_socks_reply (sd, NULL, signal_received)) goto error; @@ -448,9 +468,8 @@ establish_socks_proxy_passthru (struct socks_proxy_info *p, return; error: - /* on error, should we exit or restart? */ if (!*signal_received) - *signal_received = (p->retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- socks error */ + *signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- socks error */ return; } @@ -486,9 +505,8 @@ establish_socks_proxy_udpassoc (struct socks_proxy_info *p, return; error: - /* on error, should we exit or restart? */ if (!*signal_received) - *signal_received = (p->retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- socks error */ + *signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- socks error */ return; } @@ -553,7 +571,3 @@ socks_process_outgoing_udp (struct buffer *buf, return 10; } - -#else -static void dummy(void) {} -#endif /* ENABLE_SOCKS */ diff --git a/src/openvpn/socks.h b/src/openvpn/socks.h index b55ff6f..a2843b9 100644 --- a/src/openvpn/socks.h +++ b/src/openvpn/socks.h @@ -30,8 +30,6 @@ #ifndef SOCKS_H #define SOCKS_H -#ifdef ENABLE_SOCKS - #include "buffer.h" struct openvpn_sockaddr; @@ -39,26 +37,24 @@ struct link_socket_actual; struct socks_proxy_info { bool defined; - bool retry; char server[128]; - int port; + const char *port; char authfile[256]; }; void socks_adjust_frame_parameters (struct frame *frame, int proto); struct socks_proxy_info *socks_proxy_new (const char *server, - int port, - const char *authfile, - bool retry); + const char *port, + const char *authfile); void socks_proxy_close (struct socks_proxy_info *sp); void establish_socks_proxy_passthru (struct socks_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ - const int port, /* openvpn server port */ + const char *servname, /* openvpn server port */ volatile int *signal_received); void establish_socks_proxy_udpassoc (struct socks_proxy_info *p, @@ -74,4 +70,3 @@ int socks_process_outgoing_udp (struct buffer *buf, const struct link_socket_actual *to); #endif -#endif diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 9e31c3c..dc06350 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -35,7 +35,6 @@ * Both the TLS session and the data channel are multiplexed * over the same TCP/UDP port. */ - #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) @@ -45,11 +44,10 @@ #include "syshead.h" #include "win32.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(ENABLE_CRYPTO) #include "error.h" #include "common.h" -#include "integer.h" #include "socket.h" #include "misc.h" #include "fdmisc.h" @@ -58,9 +56,8 @@ #include "status.h" #include "gremlin.h" #include "pkcs11.h" -#include "list.h" -#include "base64.h" #include "route.h" +#include "tls_crypt.h" #include "ssl.h" #include "ssl_verify.h" @@ -150,6 +147,7 @@ static const tls_cipher_name_pair tls_cipher_name_translation_table[] = { {"DHE-RSA-CAMELLIA128-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA"}, {"DHE-RSA-CAMELLIA256-SHA256", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, {"DHE-RSA-CAMELLIA256-SHA", "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA"}, + {"DHE-RSA-CHACHA20-POLY1305", "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256"}, {"DHE-RSA-SEED-SHA", "TLS-DHE-RSA-WITH-SEED-CBC-SHA"}, {"DH-RSA-SEED-SHA", "TLS-DH-RSA-WITH-SEED-CBC-SHA"}, {"ECDH-ECDSA-AES128-GCM-SHA256", "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256"}, @@ -178,6 +176,7 @@ static const tls_cipher_name_pair tls_cipher_name_translation_table[] = { {"ECDHE-ECDSA-CAMELLIA128-SHA", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA"}, {"ECDHE-ECDSA-CAMELLIA256-SHA256", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA256"}, {"ECDHE-ECDSA-CAMELLIA256-SHA", "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA"}, + {"ECDHE-ECDSA-CHACHA20-POLY1305", "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"}, {"ECDHE-ECDSA-DES-CBC3-SHA", "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA"}, {"ECDHE-ECDSA-DES-CBC-SHA", "TLS-ECDHE-ECDSA-WITH-DES-CBC-SHA"}, {"ECDHE-ECDSA-RC4-SHA", "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA"}, @@ -193,6 +192,7 @@ static const tls_cipher_name_pair tls_cipher_name_translation_table[] = { {"ECDHE-RSA-CAMELLIA128-SHA", "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA"}, {"ECDHE-RSA-CAMELLIA256-SHA256", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"}, {"ECDHE-RSA-CAMELLIA256-SHA", "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA"}, + {"ECDHE-RSA-CHACHA20-POLY1305", "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"}, {"ECDHE-RSA-DES-CBC3-SHA", "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA"}, {"ECDHE-RSA-DES-CBC-SHA", "TLS-ECDHE-RSA-WITH-DES-CBC-SHA"}, {"ECDHE-RSA-RC4-SHA", "TLS-ECDHE-RSA-WITH-RC4-128-SHA"}, @@ -237,21 +237,37 @@ static const tls_cipher_name_pair tls_cipher_name_translation_table[] = { {"SRP-RSA-AES-128-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA"}, {"SRP-RSA-AES-256-CBC-SHA", "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA"}, #ifdef ENABLE_CRYPTO_OPENSSL + /* OpenSSL-specific group names */ {"DEFAULT", "DEFAULT"}, {"ALL", "ALL"}, - {"HIGH", "HIGH"}, - {"MEDIUM", "MEDIUM"}, - {"LOW", "LOW"}, - {"ECDH", "ECDH"}, - {"ECDSA", "ECDSA"}, - {"EDH", "EDH"}, - {"EXP", "EXP"}, - {"RSA", "RSA"}, - {"SRP", "SRP"}, + {"HIGH", "HIGH"}, {"!HIGH", "!HIGH"}, + {"MEDIUM", "MEDIUM"}, {"!MEDIUM", "!MEDIUM"}, + {"LOW", "LOW"}, {"!LOW", "!LOW"}, + {"ECDH", "ECDH"}, {"!ECDH", "!ECDH"}, + {"ECDSA", "ECDSA"}, {"!ECDSA", "!ECDSA"}, + {"EDH", "EDH"}, {"!EDH", "!EDH"}, + {"EXP", "EXP"}, {"!EXP", "!EXP"}, + {"RSA", "RSA"}, {"!RSA", "!RSA"}, + {"kRSA", "kRSA"}, {"!kRSA", "!kRSA"}, + {"SRP", "SRP"}, {"!SRP", "!SRP"}, #endif {NULL, NULL} }; +/** + * Update the implicit IV for a key_ctx_bi based on TLS session ids and cipher + * used. + * + * Note that the implicit IV is based on the HMAC key, but only in AEAD modes + * where the HMAC key is not used for an actual HMAC. + * + * @param ctx Encrypt/decrypt key context + * @param key HMAC key, used to calculate implicit IV + * @param key_len HMAC key length + */ +static void +key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len); + const tls_cipher_name_pair * tls_get_cipher_name_pair (const char * cipher_name, size_t len) { const tls_cipher_name_pair * pair = tls_cipher_name_translation_table; @@ -268,6 +284,27 @@ tls_get_cipher_name_pair (const char * cipher_name, size_t len) { return NULL; } +/** + * Limit the reneg_bytes value when using a small-block (<128 bytes) cipher. + * + * @param cipher The current cipher (may be NULL). + * @param reneg_bytes Pointer to the current reneg_bytes, updated if needed. + * May *not* be NULL. + */ +static void +tls_limit_reneg_bytes (const cipher_kt_t *cipher, int *reneg_bytes) +{ + if (cipher && (cipher_kt_block_size(cipher) < 128/8)) + { + if (*reneg_bytes == -1) /* Not user-specified */ + { + msg (M_WARN, "WARNING: cipher with small block size in use, " + "reducing reneg-bytes to 64MB to mitigate SWEET32 attacks."); + *reneg_bytes = 64 * 1024 * 1024; + } + } +} + /* * Max number of bytes we will add * for data structures common to both @@ -488,12 +525,15 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) if (options->tls_server) { - tls_ctx_server_new(new_ctx, options->ssl_flags); - tls_ctx_load_dh_params(new_ctx, options->dh_file, options->dh_file_inline); + tls_ctx_server_new(new_ctx); + + if (options->dh_file) + tls_ctx_load_dh_params(new_ctx, options->dh_file, + options->dh_file_inline); } else /* if client */ { - tls_ctx_client_new(new_ctx, options->ssl_flags); + tls_ctx_client_new(new_ctx); } tls_ctx_set_options(new_ctx, options->ssl_flags); @@ -522,10 +562,19 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) } #endif #ifdef MANAGMENT_EXTERNAL_KEY - else if ((options->management_flags & MF_EXTERNAL_KEY) && options->cert_file) - { - tls_ctx_use_external_private_key(new_ctx, options->cert_file, - options->cert_file_inline); + else if ((options->management_flags & MF_EXTERNAL_KEY) && + (options->cert_file || options->management_flags & MF_EXTERNAL_CERT)) + { + if (options->cert_file) { + tls_ctx_use_external_private_key(new_ctx, options->cert_file, + options->cert_file_inline); + } else { + char *external_certificate = management_query_cert(management, + options->management_certificate); + tls_ctx_use_external_private_key(new_ctx, INLINE_FILE_TAG, + external_certificate); + free(external_certificate); + } } #endif else @@ -552,7 +601,7 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) /* 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) + if (options->extra_certs_file) { tls_ctx_load_extra_certs(new_ctx, options->extra_certs_file, options->extra_certs_file_inline); } @@ -560,10 +609,20 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) /* Check certificate notBefore and notAfter */ tls_ctx_check_cert_time(new_ctx); + /* Read CRL */ + if (options->crl_file && !(options->ssl_flags & SSLF_CRL_VERIFY_DIR)) + { + tls_ctx_reload_crl(new_ctx, options->crl_file, options->crl_file_inline); + } + + /* Once keys and cert are loaded, load ECDH parameters */ + if (options->tls_server) + tls_ctx_load_ecdh_params(new_ctx, options->ecdh_curve); + /* Allowable ciphers */ tls_ctx_restrict_ciphers(new_ctx, options->cipher_list); -#ifdef ENABLE_CRYPTO_POLARSSL +#ifdef ENABLE_CRYPTO_MBEDTLS /* Personalise the random by mixing in the certificate */ tls_ctx_personalise_random (new_ctx); #endif @@ -766,11 +825,17 @@ key_state_init (struct tls_session *session, struct key_state *ks) reliable_set_timeout (ks->send_reliable, session->opt->packet_timeout); /* init packet ID tracker */ - packet_id_init (&ks->packet_id, - session->opt->tcp_mode, - session->opt->replay_window, - session->opt->replay_time, - "SSL", ks->key_id); + if (session->opt->replay) + { + packet_id_init (&ks->crypto_options.packet_id, + session->opt->replay_window, session->opt->replay_time, "SSL", + ks->key_id); + } + + ks->crypto_options.pid_persist = NULL; + ks->crypto_options.flags = session->opt->crypto_flags; + ks->crypto_options.flags &= session->opt->crypto_flags_and; + ks->crypto_options.flags |= session->opt->crypto_flags_or; #ifdef MANAGEMENT_DEF_AUTH ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++; @@ -798,7 +863,7 @@ key_state_free (struct key_state *ks, bool clear) key_state_ssl_free(&ks->ks_ssl); - free_key_ctx_bi (&ks->key); + free_key_ctx_bi (&ks->crypto_options.key_ctx_bi); free_buf (&ks->plaintext_read_buf); free_buf (&ks->plaintext_write_buf); free_buf (&ks->ack_write_buf); @@ -822,7 +887,7 @@ key_state_free (struct key_state *ks, bool clear) if (ks->key_src) free (ks->key_src); - packet_id_free (&ks->packet_id); + packet_id_free (&ks->crypto_options.packet_id); #ifdef PLUGIN_DEF_AUTH key_state_rm_auth_control_file (ks); @@ -837,11 +902,23 @@ key_state_free (struct key_state *ks, bool clear) /** @} addtogroup control_processor */ -/* - * Must be called if we move a tls_session in memory. +/** + * Returns whether or not the server should check for username/password + * + * @param session The current TLS session + * + * @return true if username and password verification is enabled, + * false if not. */ -static inline void tls_session_set_self_referential_pointers (struct tls_session* session) { - session->tls_auth.packet_id = &session->tls_auth_pid; +static inline bool +tls_session_user_pass_enabled(struct tls_session *session) +{ + return (session->opt->auth_user_pass_verify_script + || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) +#ifdef MANAGEMENT_DEF_AUTH + || management_enable_def_auth (management) +#endif + ); } @@ -895,20 +972,18 @@ tls_session_init (struct tls_multi *multi, struct tls_session *session) } /* Initialize control channel authentication parameters */ - session->tls_auth = session->opt->tls_auth; - - /* Set session internal pointers (also called if session object is moved in memory) */ - tls_session_set_self_referential_pointers (session); + session->tls_wrap = session->opt->tls_wrap; + session->tls_wrap.work = alloc_buf (TLS_CHANNEL_BUF_SIZE); /* initialize packet ID replay window for --tls-auth */ - packet_id_init (session->tls_auth.packet_id, - session->opt->tcp_mode, + packet_id_init (&session->tls_wrap.opt.packet_id, session->opt->replay_window, session->opt->replay_time, - "TLS_AUTH", session->key_id); + "TLS_WRAP", 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); + packet_id_persist_load_obj (session->tls_wrap.opt.pid_persist, + &session->tls_wrap.opt.packet_id); key_state_init (session, &session->key[KS_PRIMARY]); @@ -935,8 +1010,10 @@ tls_session_free (struct tls_session *session, bool clear) { int i; - if (session->tls_auth.packet_id) - packet_id_free (session->tls_auth.packet_id); + if (packet_id_initialized(&session->tls_wrap.opt.packet_id)) + packet_id_free (&session->tls_wrap.opt.packet_id); + + free_buf (&session->tls_wrap.work); for (i = 0; i < KS_SIZE; ++i) key_state_free (&session->key[i], false); @@ -967,7 +1044,6 @@ move_session (struct tls_multi* multi, int dest, int src, bool reinit_src) ASSERT (dest >= 0 && dest < TM_SIZE); tls_session_free (&multi->session[dest], false); multi->session[dest] = multi->session[src]; - tls_session_set_self_referential_pointers (&multi->session[dest]); if (reinit_src) tls_session_init (multi, &multi->session[src]); @@ -984,22 +1060,6 @@ reset_session (struct tls_multi *multi, struct tls_session *session) tls_session_init (multi, session); } -#if 0 -/* - * Transmit a TLS reset on our untrusted channel. - */ -static void -initiate_untrusted_session (struct tls_multi *multi, struct sockaddr_in *to) -{ - struct tls_session *session = &multi->session[TM_UNTRUSTED]; - struct key_state *ks = &session->key[KS_PRIMARY]; - - reset_session (multi, session); - ks->remote_addr = *to; - msg (D_TLS_DEBUG_LOW, "TLS: initiate_untrusted_session: addr=%s", print_sockaddr (to)); -} -#endif - /* * Used to determine in how many seconds we should be * called again. @@ -1048,9 +1108,6 @@ tls_multi_init (struct tls_options *tls_options) /* get command line derived options */ ret->opt = *tls_options; - /* set up pointer to HMAC object for TLS packet authentication */ - ret->opt.tls_auth.key_ctx_bi = &ret->opt.tls_auth_key; - /* set up list of keys to be scanned by data channel encrypt and decrypt routines */ ASSERT (SIZE (ret->key_scan) == 3); ret->key_scan[0] = &ret->session[TM_ACTIVE].key[KS_PRIMARY]; @@ -1088,10 +1145,14 @@ tls_auth_standalone_init (struct tls_options *tls_options, ALLOC_OBJ_CLEAR_GC (tas, struct tls_auth_standalone, gc); - /* set up pointer to HMAC object for TLS packet authentication */ - tas->tls_auth_key = tls_options->tls_auth_key; - tas->tls_auth_options.key_ctx_bi = &tas->tls_auth_key; - tas->tls_auth_options.flags |= CO_PACKET_ID_LONG_FORM; + tas->tls_wrap = tls_options->tls_wrap; + + /* + * Standalone tls-auth is in read-only mode with respect to TLS + * control channel state. After we build a new client instance + * object, we will process this session-initiating packet for real. + */ + tas->tls_wrap.opt.flags |= CO_IGNORE_PACKET_ID; /* get initial frame parms, still need to finalize */ tas->frame = tls_options->frame; @@ -1136,6 +1197,8 @@ tls_multi_free (struct tls_multi *multi, bool clear) #ifdef MANAGEMENT_DEF_AUTH man_def_auth_set_client_reason(multi, NULL); +#endif +#if P2MP_SERVER free (multi->peer_info); #endif @@ -1147,6 +1210,12 @@ tls_multi_free (struct tls_multi *multi, bool clear) cert_hash_free (multi->locked_cert_hash_set); + if (multi->auth_token) + { + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); + free (multi->auth_token); + } + for (i = 0; i < TM_SIZE; ++i) tls_session_free (&multi->session[i], false); @@ -1171,11 +1240,11 @@ tls_multi_free (struct tls_multi *multi, bool clear) static bool swap_hmac (struct buffer *buf, const struct crypto_options *co, bool incoming) { - struct key_ctx *ctx; + const struct key_ctx *ctx; ASSERT (co); - ctx = (incoming ? &co->key_ctx_bi->decrypt : &co->key_ctx_bi->encrypt); + ctx = (incoming ? &co->key_ctx_bi.decrypt : &co->key_ctx_bi.encrypt); ASSERT (ctx->hmac); { @@ -1230,20 +1299,34 @@ write_control_auth (struct tls_session *session, int max_ack, bool prepend_ack) { - uint8_t *header; + uint8_t header = ks->key_id | (opcode << P_OPCODE_SHIFT); struct buffer null = clear_buf (); ASSERT (link_socket_actual_defined (&ks->remote_addr)); ASSERT (reliable_ack_write (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack)); - ASSERT (session_id_write_prepend (&session->session_id, buf)); - ASSERT (header = buf_prepend (buf, 1)); - *header = ks->key_id | (opcode << P_OPCODE_SHIFT); - if (session->tls_auth.key_ctx_bi->encrypt.hmac) + + if (session->tls_wrap.mode == TLS_WRAP_AUTH || + session->tls_wrap.mode == TLS_WRAP_NONE) + { + ASSERT (session_id_write_prepend (&session->session_id, buf)); + ASSERT (buf_write_prepend (buf, &header, sizeof(header))); + } + if (session->tls_wrap.mode == TLS_WRAP_AUTH) { /* no encryption, only write hmac */ - openvpn_encrypt (buf, null, &session->tls_auth, NULL); - ASSERT (swap_hmac (buf, &session->tls_auth, false)); + openvpn_encrypt (buf, null, &session->tls_wrap.opt); + ASSERT (swap_hmac (buf, &session->tls_wrap.opt, false)); + } + else if (session->tls_wrap.mode == TLS_WRAP_CRYPT) + { + buf_init (&session->tls_wrap.work, buf->offset); + ASSERT (buf_write (&session->tls_wrap.work, &header, sizeof(header))); + ASSERT (session_id_write (&session->session_id, &session->tls_wrap.work)); + ASSERT (tls_crypt_wrap (buf, &session->tls_wrap.work, &session->tls_wrap.opt)); + /* Don't change the original data in buf, it's used by the reliability + * layer to resend on failure. */ + *buf = session->tls_wrap.work; } *to_link_addr = &ks->remote_addr; } @@ -1253,17 +1336,18 @@ write_control_auth (struct tls_session *session, */ static bool read_control_auth (struct buffer *buf, - const struct crypto_options *co, + struct tls_wrap_ctx *ctx, const struct link_socket_actual *from) { struct gc_arena gc = gc_new (); + bool ret = false; - if (co->key_ctx_bi->decrypt.hmac) + if (ctx->mode == TLS_WRAP_AUTH) { struct buffer null = clear_buf (); /* move the hmac record to the front of the packet */ - if (!swap_hmac (buf, co, true)) + if (!swap_hmac (buf, &ctx->opt, true)) { msg (D_TLS_ERRORS, "TLS Error: cannot locate HMAC in incoming packet from %s", @@ -1274,24 +1358,41 @@ read_control_auth (struct buffer *buf, /* authenticate only (no decrypt) and remove the hmac record from the head of the buffer */ - openvpn_decrypt (buf, null, co, NULL); + openvpn_decrypt (buf, null, &ctx->opt, NULL, BPTR (buf)); if (!buf->len) { msg (D_TLS_ERRORS, "TLS Error: incoming packet authentication failed from %s", print_link_socket_actual (from, &gc)); - gc_free (&gc); - return false; + goto cleanup; } } + else if (ctx->mode == TLS_WRAP_CRYPT) + { + struct buffer tmp = alloc_buf (buf_forward_capacity_total (buf)); + if (!tls_crypt_unwrap (buf, &tmp, &ctx->opt)) + { + msg (D_TLS_ERRORS, "TLS Error: tls-crypt unwrapping failed from %s", + print_link_socket_actual (from, &gc)); + goto cleanup; + } + ASSERT (buf_init (buf, buf->offset)); + ASSERT (buf_copy (buf, &tmp)); + free_buf (&tmp); + } - /* advance buffer pointer past opcode & session_id since our caller - already read it */ - buf_advance (buf, SID_SIZE + 1); + if (ctx->mode == TLS_WRAP_NONE || ctx->mode == TLS_WRAP_AUTH) + { + /* advance buffer pointer past opcode & session_id since our caller + already read it */ + buf_advance (buf, SID_SIZE + 1); + } + ret = true; +cleanup: gc_free (&gc); - return true; + return ret; } /* @@ -1352,7 +1453,7 @@ tls1_P_hash(const md_kt_t *md_kt, int olen) { struct gc_arena gc = gc_new (); - int chunk,n; + int chunk; hmac_ctx_t ctx; hmac_ctx_t ctx_tmp; uint8_t A1[MAX_HMAC_KEY_LENGTH]; @@ -1378,7 +1479,6 @@ tls1_P_hash(const md_kt_t *md_kt, hmac_ctx_update(&ctx,seed,seed_len); hmac_ctx_final(&ctx, A1); - n=0; for (;;) { hmac_ctx_reset(&ctx); @@ -1429,7 +1529,7 @@ tls1_P_hash(const md_kt_t *md_kt, * (2) The pre-master secret is generated by the client. */ static void -tls1_PRF(uint8_t *label, +tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec, int slen, @@ -1581,6 +1681,13 @@ generate_key_expansion (struct key_ctx_bi *key, OPENVPN_OP_DECRYPT, "Data Channel Decrypt"); + /* Initialize implicit IVs */ + key_ctx_update_implicit_iv (&key->encrypt, key2.keys[(int)server].hmac, + MAX_HMAC_KEY_LENGTH); + key_ctx_update_implicit_iv (&key->decrypt, key2.keys[1-(int)server].hmac, + MAX_HMAC_KEY_LENGTH); + + key->initialized = true; ret = true; exit: @@ -1590,6 +1697,96 @@ generate_key_expansion (struct key_ctx_bi *key, return ret; } +static void +key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) { + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher); + + /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */ + if (cipher_kt_mode_aead (cipher_kt)) + { + size_t impl_iv_len = 0; + ASSERT (cipher_kt_iv_size (cipher_kt) >= OPENVPN_AEAD_MIN_IV_LEN); + impl_iv_len = cipher_kt_iv_size (cipher_kt) - sizeof (packet_id_type); + ASSERT (impl_iv_len <= OPENVPN_MAX_IV_LENGTH); + ASSERT (impl_iv_len <= key_len); + memcpy (ctx->implicit_iv, key, impl_iv_len); + ctx->implicit_iv_len = impl_iv_len; + } +} + +static bool +item_in_list(const char *item, const char *list) +{ + char *tmp_ciphers = string_alloc (list, NULL); + char *tmp_ciphers_orig = tmp_ciphers; + + const char *token = strtok (tmp_ciphers, ":"); + while(token) + { + if (0 == strcmp (token, item)) + break; + token = strtok (NULL, ":"); + } + free(tmp_ciphers_orig); + + return token != NULL; +} + +bool +tls_session_update_crypto_params(struct tls_session *session, + const struct options *options, struct frame *frame) +{ + bool ret = false; + struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ + + ASSERT (ks->authenticated); + + if (!session->opt->server && + 0 != strcmp(options->ciphername, session->opt->config_ciphername) && + !item_in_list(options->ciphername, options->ncp_ciphers)) + { + msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", + options->ciphername, session->opt->config_ciphername, + options->ncp_ciphers); + return false; + } + + init_key_type (&session->opt->key_type, options->ciphername, + options->authname, options->keysize, true, true); + + bool packet_id_long_form = cipher_kt_mode_ofb_cfb (session->opt->key_type.cipher); + session->opt->crypto_flags_and &= ~(CO_PACKET_ID_LONG_FORM); + if (packet_id_long_form) + session->opt->crypto_flags_and = CO_PACKET_ID_LONG_FORM; + + /* Update frame parameters: undo worst-case overhead, add actual overhead */ + frame_add_to_extra_frame (frame, -(crypto_max_overhead())); + crypto_adjust_frame_parameters (frame, &session->opt->key_type, + options->use_iv, options->replay, packet_id_long_form); + frame_finalize(frame, options->ce.link_mtu_defined, options->ce.link_mtu, + options->ce.tun_mtu_defined, options->ce.tun_mtu); + frame_init_mssfix(frame, options); + frame_print (frame, D_MTU_INFO, "Data Channel MTU parms"); + + const struct session_id *client_sid = session->opt->server ? + &ks->session_id_remote : &session->session_id; + const struct session_id *server_sid = !session->opt->server ? + &ks->session_id_remote : &session->session_id; + if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, + &session->opt->key_type, ks->key_src, client_sid, server_sid, + session->opt->server)) + { + msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed"); + goto cleanup; + } + tls_limit_reneg_bytes (session->opt->key_type.cipher, + &session->opt->renegotiate_bytes); + ret = true; +cleanup: + CLEAR (*ks->key_src); + return ret; +} + static bool random_bytes_to_buf (struct buffer *buf, uint8_t *out, @@ -1762,7 +1959,6 @@ key_method_1_write (struct buffer *buf, struct tls_session *session) { struct key key; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ ASSERT (session->opt->key_method == 1); ASSERT (buf_init (buf, 0)); @@ -1780,8 +1976,9 @@ key_method_1_write (struct buffer *buf, struct tls_session *session) return false; } - init_key_ctx (&ks->key.encrypt, &key, &session->opt->key_type, - OPENVPN_OP_ENCRYPT, "Data Channel Encrypt"); + init_key_ctx (&ks->crypto_options.key_ctx_bi.encrypt, &key, + &session->opt->key_type, OPENVPN_OP_ENCRYPT, + "Data Channel Encrypt"); CLEAR (key); /* send local options string */ @@ -1827,17 +2024,30 @@ push_peer_info(struct buffer *buf, struct tls_session *session) buf_printf (&out, "IV_PLAT=netbsd\n"); #elif defined(TARGET_FREEBSD) buf_printf (&out, "IV_PLAT=freebsd\n"); -#elif defined(WIN32) +#elif defined(TARGET_ANDROID) + buf_printf (&out, "IV_PLAT=android\n"); +#elif defined(_WIN32) buf_printf (&out, "IV_PLAT=win\n"); #endif - /* push LZO status */ -#ifdef ENABLE_LZO_STUB - buf_printf (&out, "IV_LZO_STUB=1\n"); -#endif /* support for P_DATA_V2 */ buf_printf(&out, "IV_PROTO=2\n"); + /* support for Negotiable Crypto Paramters */ + if (session->opt->ncp_enabled && + (session->opt->mode == MODE_SERVER || session->opt->pull)) + { + buf_printf(&out, "IV_NCP=2\n"); + } + + /* push compression status */ +#ifdef USE_COMP + comp_generate_peer_info_string(&session->opt->comp_options, &out); +#endif + + /* support for redirecting IPv6 gateway */ + buf_printf(&out, "IV_RGI6=1\n"); + if (session->opt->push_peer_info_detail >= 2) { /* push mac addr */ @@ -1846,17 +2056,19 @@ push_peer_info(struct buffer *buf, struct tls_session *session) if (rgi.flags & RGI_HWADDR_DEFINED) buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (rgi.hwaddr, 6, 0, 1, ":", &gc)); buf_printf (&out, "IV_SSL=%s\n", get_ssl_library_version() ); -#if defined(WIN32) +#if defined(_WIN32) buf_printf (&out, "IV_PLAT_VER=%s\n", win32_version_string (&gc, false)); #endif } - /* push env vars that begin with UV_ and IV_GUI_VER */ + /* push env vars that begin with UV_, IV_PLAT_VER and IV_GUI_VER */ for (e=es->list; e != NULL; e=e->next) { if (e->string) { - if (((strncmp(e->string, "UV_", 3)==0 && session->opt->push_peer_info_detail >= 2) + if ((((strncmp(e->string, "UV_", 3)==0 || + strncmp(e->string, "IV_PLAT_VER=", sizeof("IV_PLAT_VER=")-1)==0) + && session->opt->push_peer_info_detail >= 2) || (strncmp(e->string,"IV_GUI_VER=",sizeof("IV_GUI_VER=")-1)==0)) && buf_safe(&out, strlen(e->string)+1)) buf_printf (&out, "%s\n", e->string); @@ -1883,7 +2095,6 @@ static bool key_method_2_write (struct buffer *buf, struct tls_session *session) { struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ ASSERT (session->opt->key_method == 2); ASSERT (buf_init (buf, 0)); @@ -1931,14 +2142,17 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) if (!push_peer_info (buf, session)) goto error; - /* - * generate tunnel keys if server + /* Generate tunnel keys if we're a TLS server. + * If we're a p2mp server and IV_NCP >= 2 is negotiated, the first key + * generation is postponed until after the pull/push, so we can process pushed + * cipher directives. */ - if (session->opt->server) + if (session->opt->server && !(session->opt->ncp_enabled && + session->opt->mode == MODE_SERVER && ks->key_id <= 0)) { if (ks->authenticated) { - if (!generate_key_expansion (&ks->key, + if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, &session->opt->key_type, ks->key_src, &ks->session_id_remote, @@ -1951,6 +2165,8 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) } CLEAR (*ks->key_src); + tls_limit_reneg_bytes (session->opt->key_type.cipher, + &session->opt->renegotiate_bytes); } return true; @@ -1967,7 +2183,6 @@ key_method_1_read (struct buffer *buf, struct tls_session *session) int status; struct key key; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ ASSERT (session->opt->key_method == 1); @@ -2010,8 +2225,9 @@ key_method_1_read (struct buffer *buf, struct tls_session *session) buf_clear (buf); - init_key_ctx (&ks->key.decrypt, &key, &session->opt->key_type, - OPENVPN_OP_DECRYPT, "Data Channel Decrypt"); + init_key_ctx (&ks->crypto_options.key_ctx_bi.decrypt, &key, + &session->opt->key_type, OPENVPN_OP_DECRYPT, + "Data Channel Decrypt"); CLEAR (key); ks->authenticated = true; return true; @@ -2026,13 +2242,13 @@ static bool key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_session *session) { struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct key_state *ks_lame = &session->key[KS_LAME_DUCK]; /* retiring key */ int key_method_flags; bool username_status, password_status; struct gc_arena gc = gc_new (); char *options; + struct user_pass *up; /* allocate temporary objects */ ALLOC_ARRAY_CLEAR_GC (options, char, TLS_OPTIONS_LEN, &gc); @@ -2072,15 +2288,31 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi ks->authenticated = false; - if (verify_user_pass_enabled(session)) - { - /* Perform username/password authentication */ - struct user_pass *up; + /* always extract username + password fields from buf, even if not + * authenticating for it, because otherwise we can't get at the + * peer_info data which follows behind + */ + ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc); + username_status = read_string (buf, up->username, USER_PASS_LEN); + password_status = read_string (buf, up->password, USER_PASS_LEN); + +#if P2MP_SERVER + /* get peer info from control channel */ + free (multi->peer_info); + multi->peer_info = read_string_alloc (buf); + if ( multi->peer_info ) + output_peer_info_env (session->opt->es, multi->peer_info); - ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc); - username_status = read_string (buf, up->username, USER_PASS_LEN); - password_status = read_string (buf, up->password, USER_PASS_LEN); + if (tls_peer_info_ncp_ver (multi->peer_info) < 2) + { + /* Peer does not support NCP */ + session->opt->ncp_enabled = false; + } +#endif + if (tls_session_user_pass_enabled(session)) + { + /* Perform username/password authentication */ if (!username_status || !password_status) { CLEAR (*up); @@ -2091,14 +2323,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi } } -#ifdef MANAGEMENT_DEF_AUTH - /* get peer info from control channel */ - free (multi->peer_info); - multi->peer_info = read_string_alloc (buf); -#endif - verify_user_pass(up, multi, session); - CLEAR (*up); } else { @@ -2112,6 +2337,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi ks->authenticated = true; } + /* clear username and password from memory */ + CLEAR (*up); + /* Perform final authentication checks */ if (ks->authenticated) { @@ -2140,16 +2368,22 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi */ if (ks->authenticated && plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) { + key_state_export_keying_material(&ks->ks_ssl, session); + if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS) ks->authenticated = false; + + setenv_del (session->opt->es, "exported_keying_material"); } /* - * Generate tunnel keys if client + * Generate tunnel keys if we're a client. + * If --pull is enabled, the first key generation is postponed until after the + * pull/push, so we can process pushed cipher directives. */ - if (!session->opt->server) + if (!session->opt->server && (!session->opt->pull || ks->key_id > 0)) { - if (!generate_key_expansion (&ks->key, + if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, &session->opt->key_type, ks->key_src, &session->session_id, @@ -2159,8 +2393,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed"); goto error; } - + CLEAR (*ks->key_src); + tls_limit_reneg_bytes (session->opt->key_type.cipher, + &session->opt->renegotiate_bytes); } gc_free (&gc); @@ -2217,11 +2453,11 @@ tls_process (struct tls_multi *multi, if (ks->state >= S_ACTIVE && ((session->opt->renegotiate_seconds && now >= ks->established + session->opt->renegotiate_seconds) - || (session->opt->renegotiate_bytes + || (session->opt->renegotiate_bytes > 0 && ks->n_bytes >= session->opt->renegotiate_bytes) || (session->opt->renegotiate_packets && ks->n_packets >= session->opt->renegotiate_packets) - || (packet_id_close_to_wrapping (&ks->packet_id.send)))) + || (packet_id_close_to_wrapping (&ks->crypto_options.packet_id.send)))) { msg (D_TLS_DEBUG_LOW, "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d", @@ -2257,269 +2493,274 @@ tls_process (struct tls_multi *multi, * CHANGED with 2.0 -> now we may send tunnel configuration * info over the control channel. */ - if (true) + + /* Initial handshake */ + if (ks->state == S_INITIAL) { - /* Initial handshake */ - if (ks->state == S_INITIAL) + buf = reliable_get_buf_output_sequenced (ks->send_reliable); + if (buf) { - buf = reliable_get_buf_output_sequenced (ks->send_reliable); - if (buf) - { - ks->must_negotiate = now + session->opt->handshake_window; - ks->auth_deferred_expire = now + auth_deferred_expire_window (session->opt); + ks->must_negotiate = now + session->opt->handshake_window; + ks->auth_deferred_expire = now + auth_deferred_expire_window (session->opt); - /* null buffer */ - reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode); - INCR_GENERATED; + /* null buffer */ + reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode); + INCR_GENERATED; - ks->state = S_PRE_START; - state_change = true; - dmsg (D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s", - session_id_print (&session->session_id, &gc)); + ks->state = S_PRE_START; + state_change = true; + dmsg (D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s", + session_id_print (&session->session_id, &gc)); #ifdef ENABLE_MANAGEMENT - if (management && ks->initial_opcode != P_CONTROL_SOFT_RESET_V1) - { - management_set_state (management, - OPENVPN_STATE_WAIT, - NULL, - 0, - 0); - } -#endif + if (management && ks->initial_opcode != P_CONTROL_SOFT_RESET_V1) + { + management_set_state (management, + OPENVPN_STATE_WAIT, + NULL, + NULL, + NULL, + NULL, + NULL); } +#endif } + } - /* Are we timed out on receive? */ - if (now >= ks->must_negotiate) + /* Are we timed out on receive? */ + if (now >= ks->must_negotiate) + { + if (ks->state < S_ACTIVE) { - if (ks->state < S_ACTIVE) - { - msg (D_TLS_ERRORS, - "TLS Error: TLS key negotiation failed to occur within %d seconds (check your network connectivity)", - session->opt->handshake_window); - goto error; - } - else /* assume that ks->state == S_ACTIVE */ - { - dmsg (D_TLS_DEBUG_MED, "STATE S_NORMAL_OP"); - ks->state = S_NORMAL_OP; - ks->must_negotiate = 0; - } + msg (D_TLS_ERRORS, + "TLS Error: TLS key negotiation failed to occur within %d seconds (check your network connectivity)", + session->opt->handshake_window); + goto error; } + else /* assume that ks->state == S_ACTIVE */ + { + dmsg (D_TLS_DEBUG_MED, "STATE S_NORMAL_OP"); + ks->state = S_NORMAL_OP; + ks->must_negotiate = 0; + } + } - /* Wait for Initial Handshake ACK */ - if (ks->state == S_PRE_START && FULL_SYNC) + /* Wait for Initial Handshake ACK */ + if (ks->state == S_PRE_START && FULL_SYNC) + { + ks->state = S_START; + state_change = true; + + /* Reload the CRL before TLS negotiation */ + if (session->opt->crl_file && + !(session->opt->ssl_flags & SSLF_CRL_VERIFY_DIR)) { - ks->state = S_START; - state_change = true; - dmsg (D_TLS_DEBUG_MED, "STATE S_START"); + tls_ctx_reload_crl(&session->opt->ssl_ctx, + session->opt->crl_file, session->opt->crl_file_inline); } - /* Wait for ACK */ - if (((ks->state == S_GOT_KEY && !session->opt->server) || - (ks->state == S_SENT_KEY && session->opt->server))) + dmsg (D_TLS_DEBUG_MED, "STATE S_START"); + } + + /* Wait for ACK */ + if (((ks->state == S_GOT_KEY && !session->opt->server) || + (ks->state == S_SENT_KEY && session->opt->server))) + { + if (FULL_SYNC) { - if (FULL_SYNC) - { - ks->established = now; - dmsg (D_TLS_DEBUG_MED, "STATE S_ACTIVE"); - if (check_debug_level (D_HANDSHAKE)) - print_details (&ks->ks_ssl, "Control Channel:"); - state_change = true; - ks->state = S_ACTIVE; - INCR_SUCCESS; + ks->established = now; + dmsg (D_TLS_DEBUG_MED, "STATE S_ACTIVE"); + if (check_debug_level (D_HANDSHAKE)) + print_details (&ks->ks_ssl, "Control Channel:"); + state_change = true; + ks->state = S_ACTIVE; + INCR_SUCCESS; - /* Set outgoing address for data channel packets */ - link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es); + /* Set outgoing address for data channel packets */ + link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es); - /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */ - flush_payload_buffer (ks); + /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */ + flush_payload_buffer (ks); #ifdef MEASURE_TLS_HANDSHAKE_STATS - show_tls_performance_stats(); + show_tls_performance_stats(); #endif - } } + } - /* Reliable buffer to outgoing TCP/UDP (send up to CONTROL_SEND_ACK_MAX ACKs - for previously received packets) */ - if (!to_link->len && reliable_can_send (ks->send_reliable)) - { - int opcode; - struct buffer b; - - buf = reliable_send (ks->send_reliable, &opcode); - ASSERT (buf); - b = *buf; - INCR_SENT; - - write_control_auth (session, ks, &b, to_link_addr, opcode, - CONTROL_SEND_ACK_MAX, true); - *to_link = b; - active = true; - state_change = true; - dmsg (D_TLS_DEBUG, "Reliable -> TCP/UDP"); - break; - } + /* Reliable buffer to outgoing TCP/UDP (send up to CONTROL_SEND_ACK_MAX ACKs + for previously received packets) */ + if (!to_link->len && reliable_can_send (ks->send_reliable)) + { + int opcode; + struct buffer b; + + buf = reliable_send (ks->send_reliable, &opcode); + ASSERT (buf); + b = *buf; + INCR_SENT; + + write_control_auth (session, ks, &b, to_link_addr, opcode, + CONTROL_SEND_ACK_MAX, true); + *to_link = b; + active = true; + state_change = true; + dmsg (D_TLS_DEBUG, "Reliable -> TCP/UDP"); + break; + } #ifndef TLS_AGGREGATE_ACK - /* Send 1 or more ACKs (each received control packet gets one ACK) */ - if (!to_link->len && !reliable_ack_empty (ks->rec_ack)) - { - buf = &ks->ack_write_buf; - ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame))); - write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1, - RELIABLE_ACK_SIZE, false); - *to_link = *buf; - active = true; - state_change = true; - dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); - break; - } + /* Send 1 or more ACKs (each received control packet gets one ACK) */ + if (!to_link->len && !reliable_ack_empty (ks->rec_ack)) + { + buf = &ks->ack_write_buf; + ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame))); + write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1, + RELIABLE_ACK_SIZE, false); + *to_link = *buf; + active = true; + state_change = true; + dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); + break; + } #endif - /* Write incoming ciphertext to TLS object */ - buf = reliable_get_buf_sequenced (ks->rec_reliable); - if (buf) - { - int status = 0; - if (buf->len) - { - status = key_state_write_ciphertext (&ks->ks_ssl, buf); - if (status == -1) - { - msg (D_TLS_ERRORS, - "TLS Error: Incoming Ciphertext -> TLS object write error"); - goto error; - } - } - else - { - status = 1; - } - if (status == 1) - { - reliable_mark_deleted (ks->rec_reliable, buf, true); - state_change = true; - dmsg (D_TLS_DEBUG, "Incoming Ciphertext -> TLS"); - } - } - - /* Read incoming plaintext from TLS object */ - buf = &ks->plaintext_read_buf; - if (!buf->len) + /* Write incoming ciphertext to TLS object */ + buf = reliable_get_buf_sequenced (ks->rec_reliable); + if (buf) + { + int status = 0; + if (buf->len) { - int status; - - ASSERT (buf_init (buf, 0)); - status = key_state_read_plaintext (&ks->ks_ssl, buf, TLS_CHANNEL_BUF_SIZE); - update_time (); + status = key_state_write_ciphertext (&ks->ks_ssl, buf); if (status == -1) { - msg (D_TLS_ERRORS, "TLS Error: TLS object -> incoming plaintext read error"); + msg (D_TLS_ERRORS, + "TLS Error: Incoming Ciphertext -> TLS object write error"); goto error; } - if (status == 1) - { - state_change = true; - dmsg (D_TLS_DEBUG, "TLS -> Incoming Plaintext"); - } -#if 0 /* show null plaintext reads */ - if (!status) - msg (M_INFO, "TLS plaintext read -> NULL return"); -#endif } - - /* Send Key */ - buf = &ks->plaintext_write_buf; - if (!buf->len && ((ks->state == S_START && !session->opt->server) || - (ks->state == S_GOT_KEY && session->opt->server))) + else { - if (session->opt->key_method == 1) - { - if (!key_method_1_write (buf, session)) - goto error; - } - else if (session->opt->key_method == 2) - { - if (!key_method_2_write (buf, session)) - goto error; - } - else - { - ASSERT (0); - } + status = 1; + } + if (status == 1) + { + reliable_mark_deleted (ks->rec_reliable, buf, true); + state_change = true; + dmsg (D_TLS_DEBUG, "Incoming Ciphertext -> TLS"); + } + } + + /* Read incoming plaintext from TLS object */ + buf = &ks->plaintext_read_buf; + if (!buf->len) + { + int status; + ASSERT (buf_init (buf, 0)); + status = key_state_read_plaintext (&ks->ks_ssl, buf, TLS_CHANNEL_BUF_SIZE); + update_time (); + if (status == -1) + { + msg (D_TLS_ERRORS, "TLS Error: TLS object -> incoming plaintext read error"); + goto error; + } + if (status == 1) + { state_change = true; - dmsg (D_TLS_DEBUG_MED, "STATE S_SENT_KEY"); - ks->state = S_SENT_KEY; + dmsg (D_TLS_DEBUG, "TLS -> Incoming Plaintext"); } + } - /* Receive Key */ - buf = &ks->plaintext_read_buf; - if (buf->len - && ((ks->state == S_SENT_KEY && !session->opt->server) - || (ks->state == S_START && session->opt->server))) + /* Send Key */ + buf = &ks->plaintext_write_buf; + if (!buf->len && ((ks->state == S_START && !session->opt->server) || + (ks->state == S_GOT_KEY && session->opt->server))) + { + if (session->opt->key_method == 1) { - if (session->opt->key_method == 1) - { - if (!key_method_1_read (buf, session)) - goto error; - } - else if (session->opt->key_method == 2) - { - if (!key_method_2_read (buf, multi, session)) - goto error; - } - else - { - ASSERT (0); - } + if (!key_method_1_write (buf, session)) + goto error; + } + else if (session->opt->key_method == 2) + { + if (!key_method_2_write (buf, session)) + goto error; + } + else + { + ASSERT (0); + } + + state_change = true; + dmsg (D_TLS_DEBUG_MED, "STATE S_SENT_KEY"); + ks->state = S_SENT_KEY; + } + /* Receive Key */ + buf = &ks->plaintext_read_buf; + if (buf->len + && ((ks->state == S_SENT_KEY && !session->opt->server) + || (ks->state == S_START && session->opt->server))) + { + if (session->opt->key_method == 1) + { + if (!key_method_1_read (buf, session)) + goto error; + } + else if (session->opt->key_method == 2) + { + if (!key_method_2_read (buf, multi, session)) + goto error; + } + else + { + ASSERT (0); + } + + state_change = true; + dmsg (D_TLS_DEBUG_MED, "STATE S_GOT_KEY"); + ks->state = S_GOT_KEY; + } + + /* Write outgoing plaintext to TLS object */ + buf = &ks->plaintext_write_buf; + if (buf->len) + { + int status = key_state_write_plaintext (&ks->ks_ssl, buf); + if (status == -1) + { + msg (D_TLS_ERRORS, + "TLS ERROR: Outgoing Plaintext -> TLS object write error"); + goto error; + } + if (status == 1) + { state_change = true; - dmsg (D_TLS_DEBUG_MED, "STATE S_GOT_KEY"); - ks->state = S_GOT_KEY; + dmsg (D_TLS_DEBUG, "Outgoing Plaintext -> TLS"); } + } - /* Write outgoing plaintext to TLS object */ - buf = &ks->plaintext_write_buf; - if (buf->len) + /* Outgoing Ciphertext to reliable buffer */ + if (ks->state >= S_START) + { + buf = reliable_get_buf_output_sequenced (ks->send_reliable); + if (buf) { - int status = key_state_write_plaintext (&ks->ks_ssl, buf); + int status = key_state_read_ciphertext (&ks->ks_ssl, buf, PAYLOAD_SIZE_DYNAMIC (&multi->opt.frame)); if (status == -1) { msg (D_TLS_ERRORS, - "TLS ERROR: Outgoing Plaintext -> TLS object write error"); + "TLS Error: Ciphertext -> reliable TCP/UDP transport read error"); goto error; } if (status == 1) { + reliable_mark_active_outgoing (ks->send_reliable, buf, P_CONTROL_V1); + INCR_GENERATED; state_change = true; - dmsg (D_TLS_DEBUG, "Outgoing Plaintext -> TLS"); - } - } - - /* Outgoing Ciphertext to reliable buffer */ - if (ks->state >= S_START) - { - buf = reliable_get_buf_output_sequenced (ks->send_reliable); - if (buf) - { - int status = key_state_read_ciphertext (&ks->ks_ssl, buf, PAYLOAD_SIZE_DYNAMIC (&multi->opt.frame)); - if (status == -1) - { - msg (D_TLS_ERRORS, - "TLS Error: Ciphertext -> reliable TCP/UDP transport read error"); - goto error; - } - if (status == 1) - { - reliable_mark_active_outgoing (ks->send_reliable, buf, P_CONTROL_V1); - INCR_GENERATED; - state_change = true; - dmsg (D_TLS_DEBUG, "Outgoing Ciphertext -> Reliable"); - } + dmsg (D_TLS_DEBUG, "Outgoing Ciphertext -> Reliable"); } } } @@ -2532,11 +2773,11 @@ tls_process (struct tls_multi *multi, /* Send 1 or more ACKs (each received control packet gets one ACK) */ if (!to_link->len && !reliable_ack_empty (ks->rec_ack)) { - buf = &ks->ack_write_buf; - ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame))); - write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1, + struct buffer buf = ks->ack_write_buf; + ASSERT (buf_init (&buf, FRAME_HEADROOM (&multi->opt.frame))); + write_control_auth (session, ks, &buf, to_link_addr, P_ACK_V1, RELIABLE_ACK_SIZE, false); - *to_link = *buf; + *to_link = buf; active = true; state_change = true; dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP"); @@ -2775,7 +3016,9 @@ bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt) + struct crypto_options **opt, + bool floated, + const uint8_t **ad_start) { struct gc_arena gc = gc_new (); bool ret = false; @@ -2819,17 +3062,28 @@ tls_pre_decrypt (struct tls_multi *multi, #ifdef ENABLE_DEF_AUTH && !ks->auth_deferred #endif - && link_socket_actual_match (from, &ks->remote_addr)) + && (floated || link_socket_actual_match (from, &ks->remote_addr))) { - /* return appropriate data channel decrypt key in opt */ - opt->key_ctx_bi = &ks->key; - opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; - opt->flags |= multi->opt.crypto_flags_or; + if (!ks->crypto_options.key_ctx_bi.initialized) + { + msg (D_TLS_DEBUG_LOW, + "Key %s [%d] not initialized (yet), dropping packet.", + print_link_socket_actual (from, &gc), key_id); + goto error_lite; + } - ASSERT (buf_advance (buf, 1)); + /* return appropriate data channel decrypt key in opt */ + *opt = &ks->crypto_options; if (op == P_DATA_V2) + { + *ad_start = BPTR(buf); + } + ASSERT (buf_advance (buf, 1)); + if (op == P_DATA_V1) + { + *ad_start = BPTR(buf); + } + else if (op == P_DATA_V2) { if (buf->len < 4) { @@ -2848,23 +3102,6 @@ tls_pre_decrypt (struct tls_multi *multi, gc_free (&gc); return ret; } -#if 0 /* keys out of sync? */ - else - { - dmsg (D_TLS_ERRORS, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d def=%d match=%d", - i, - DECRYPT_KEY_ENABLED (multi, ks), - key_id, - ks->key_id, - ks->authenticated, -#ifdef ENABLE_DEF_AUTH - ks->auth_deferred, -#else - -1, -#endif - link_socket_actual_match (from, &ks->remote_addr)); - } -#endif } msg (D_TLS_ERRORS, @@ -2991,8 +3228,10 @@ tls_pre_decrypt (struct tls_multi *multi, management_set_state (management, OPENVPN_STATE_AUTH, NULL, - 0, - 0); + NULL, + NULL, + NULL, + NULL); } #endif @@ -3036,7 +3275,7 @@ tls_pre_decrypt (struct tls_multi *multi, goto error; } - if (!read_control_auth (buf, &session->tls_auth, from)) + if (!read_control_auth (buf, &session->tls_wrap, from)) goto error; /* @@ -3087,7 +3326,7 @@ tls_pre_decrypt (struct tls_multi *multi, if (op == P_CONTROL_SOFT_RESET_V1 && DECRYPT_KEY_ENABLED (multi, ks)) { - if (!read_control_auth (buf, &session->tls_auth, from)) + if (!read_control_auth (buf, &session->tls_wrap, from)) goto error; key_state_soft_reset (session); @@ -3104,7 +3343,7 @@ tls_pre_decrypt (struct tls_multi *multi, if (op == P_CONTROL_SOFT_RESET_V1) do_burst = true; - if (!read_control_auth (buf, &session->tls_auth, from)) + if (!read_control_auth (buf, &session->tls_wrap, from)) goto error; dmsg (D_TLS_DEBUG, @@ -3215,10 +3454,7 @@ tls_pre_decrypt (struct tls_multi *multi, done: buf->len = 0; - opt->key_ctx_bi = NULL; - opt->packet_id = NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; + *opt = NULL; gc_free (&gc); return ret; @@ -3297,18 +3533,11 @@ tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, { struct buffer newbuf = clone_buf (buf); - struct crypto_options co = tas->tls_auth_options; + struct tls_wrap_ctx tls_wrap_tmp = tas->tls_wrap; bool status; - /* - * We are in read-only mode at this point with respect to TLS - * control channel state. After we build a new client instance - * object, we will process this session-initiating packet for real. - */ - co.flags |= CO_IGNORE_PACKET_ID; - /* HMAC test, if --tls-auth was specified */ - status = read_control_auth (&newbuf, &co, from); + status = read_control_auth (&newbuf, &tls_wrap_tmp, from); free_buf (&newbuf); if (!status) goto error; @@ -3344,7 +3573,7 @@ tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, /* Choose the key with which to encrypt a data packet */ void tls_pre_encrypt (struct tls_multi *multi, - struct buffer *buf, struct crypto_options *opt) + struct buffer *buf, struct crypto_options **opt) { multi->save_ks = NULL; if (buf->len > 0) @@ -3356,6 +3585,7 @@ tls_pre_encrypt (struct tls_multi *multi, struct key_state *ks = multi->key_scan[i]; if (ks->state >= S_ACTIVE && ks->authenticated + && ks->crypto_options.key_ctx_bi.initialized #ifdef ENABLE_DEF_AUTH && !ks->auth_deferred #endif @@ -3373,11 +3603,7 @@ tls_pre_encrypt (struct tls_multi *multi, if (ks_select) { - opt->key_ctx_bi = &ks_select->key; - opt->packet_id = multi->opt.replay ? &ks_select->packet_id : NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; - opt->flags |= multi->opt.crypto_flags_or; + *opt = &ks_select->crypto_options; multi->save_ks = ks_select; dmsg (D_TLS_KEYSELECT, "TLS: tls_pre_encrypt: key_id=%d", ks_select->key_id); return; @@ -3392,36 +3618,48 @@ tls_pre_encrypt (struct tls_multi *multi, } buf->len = 0; - opt->key_ctx_bi = NULL; - opt->packet_id = NULL; - opt->pid_persist = NULL; - opt->flags &= multi->opt.crypto_flags_and; + *opt = NULL; } -/* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */ void -tls_post_encrypt (struct tls_multi *multi, struct buffer *buf) +tls_prepend_opcode_v1 (const struct tls_multi *multi, struct buffer *buf) { - struct key_state *ks; - uint8_t *op; + struct key_state *ks = multi->save_ks; + uint8_t op; + + msg (D_TLS_DEBUG, __func__); + + ASSERT (ks); + + op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id; + ASSERT (buf_write_prepend (buf, &op, 1)); +} + +void +tls_prepend_opcode_v2 (const struct tls_multi *multi, struct buffer *buf) +{ + struct key_state *ks = multi->save_ks; uint32_t peer; - ks = multi->save_ks; + msg (D_TLS_DEBUG, __func__); + + ASSERT (ks); + + peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 + | (multi->peer_id & 0xFFFFFF)); + ASSERT (buf_write_prepend (buf, &peer, 4)); +} + +void +tls_post_encrypt (struct tls_multi *multi, struct buffer *buf) +{ + struct key_state *ks = multi->save_ks; multi->save_ks = NULL; + if (buf->len > 0) { ASSERT (ks); - if (!multi->opt.server && multi->use_peer_id) - { - peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 | (multi->peer_id & 0xFFFFFF)); - ASSERT (buf_write_prepend (buf, &peer, 4)); - } - else - { - ASSERT (op = buf_prepend (buf, 1)); - *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id; - } ++ks->n_packets; ks->n_bytes += buf->len; } @@ -3494,6 +3732,70 @@ tls_rec_payload (struct tls_multi *multi, return ret; } +void +tls_update_remote_addr (struct tls_multi *multi, const struct link_socket_actual *addr) +{ + struct gc_arena gc = gc_new (); + int i, j; + + for (i = 0; i < TM_SIZE; ++i) + { + struct tls_session *session = &multi->session[i]; + + for (j = 0; j < KS_SIZE; ++j) + { + struct key_state *ks = &session->key[j]; + + if (!link_socket_actual_defined(&ks->remote_addr) || + link_socket_actual_match (addr, &ks->remote_addr)) + continue; + + dmsg (D_TLS_KEYSELECT, "TLS: tls_update_remote_addr from IP=%s to IP=%s", + print_link_socket_actual (&ks->remote_addr, &gc), + print_link_socket_actual (addr, &gc)); + + ks->remote_addr = *addr; + } + } + gc_free (&gc); +} + +int +tls_peer_info_ncp_ver(const char *peer_info) +{ + const char *ncpstr = peer_info ? strstr (peer_info, "IV_NCP=") : NULL; + if (ncpstr) + { + int ncp = 0; + int r = sscanf(ncpstr, "IV_NCP=%d", &ncp); + if (r == 1) + return ncp; + } + return 0; +} + +bool +tls_check_ncp_cipher_list(const char *list) { + bool unsupported_cipher_found = false; + + ASSERT (list); + + char * const tmp_ciphers = string_alloc (list, NULL); + const char *token = strtok (tmp_ciphers, ":"); + while (token) + { + if (!cipher_kt_get (translate_cipher_name_from_openvpn (token))) + { + msg (M_WARN, "Unsupported cipher in --ncp-ciphers: %s", token); + unsupported_cipher_found = true; + } + token = strtok (NULL, ":"); + } + free (tmp_ciphers); + + return 0 < strlen(list) && !unsupported_cipher_found; +} + /* * Dump a human-readable rendition of an openvpn packet * into a garbage collectable string which is returned. @@ -3594,4 +3896,4 @@ done: #else static void dummy(void) {} -#endif /* ENABLE_CRYPTO && ENABLE_SSL*/ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 6a14768..777b621 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -30,7 +30,7 @@ #ifndef OPENVPN_SSL_H #define OPENVPN_SSL_H -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(ENABLE_CRYPTO) #include "basic.h" #include "common.h" @@ -44,7 +44,6 @@ #include "plugin.h" #include "ssl_common.h" -#include "ssl_verify.h" #include "ssl_backend.h" /* Used in the TLS PRF function */ @@ -137,8 +136,7 @@ */ struct tls_auth_standalone { - struct key_ctx_bi tls_auth_key; - struct crypto_options tls_auth_options; + struct tls_wrap_ctx tls_wrap; struct frame frame; }; @@ -294,9 +292,10 @@ int tls_multi_process (struct tls_multi *multi, * of this packet. * @param from - The source address of the packet. * @param buf - A buffer structure containing the incoming packet. - * @param opt - A crypto options structure that will be loaded with the - * appropriate security parameters to handle the packet if it is a - * data channel packet. + * @param opt - Returns a crypto options structure with the appropriate security + * parameters to handle the packet if it is a data channel packet. + * @param ad_start - Returns a pointer to the start of the authenticated data of + * of this packet * * @return * @li True if the packet is a control channel packet that has been @@ -307,7 +306,9 @@ int tls_multi_process (struct tls_multi *multi, bool tls_pre_decrypt (struct tls_multi *multi, const struct link_socket_actual *from, struct buffer *buf, - struct crypto_options *opt); + struct crypto_options **opt, + bool floated, + const uint8_t **ad_start); /**************************************************************************/ @@ -356,20 +357,53 @@ bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas, * @ingroup data_crypto * * If no appropriate security parameters can be found, or if some other - * error occurs, then the buffer is set to empty. + * error occurs, then the buffer is set to empty, and the parameters to a NULL + * pointer. * * @param multi - The TLS state for this packet's destination VPN tunnel. * @param buf - The buffer containing the outgoing packet. - * @param opt - The crypto options structure into which the appropriate - * security parameters should be loaded. + * @param opt - Returns a crypto options structure with the security parameters. */ void tls_pre_encrypt (struct tls_multi *multi, - struct buffer *buf, struct crypto_options *opt); + struct buffer *buf, struct crypto_options **opt); /** - * Prepend the one-byte OpenVPN header to the packet, and perform some - * accounting for the key state used. + * Prepend a one-byte OpenVPN data channel P_DATA_V1 opcode to the packet. + * + * The opcode identifies the packet as a V1 data channel packet and gives the + * low-permutation version of the key-id to the recipient, so it knows which + * decrypt key to use. + * + * @param multi - The TLS state for this packet's destination VPN tunnel. + * @param buf - The buffer to write the header to. + * + * @ingroup data_crypto + */ +void +tls_prepend_opcode_v1 (const struct tls_multi *multi, struct buffer *buf); + +/** + * Prepend an OpenVPN data channel P_DATA_V2 header to the packet. The + * P_DATA_V2 header consists of a 1-byte opcode, followed by a 3-byte peer-id. + * + * The opcode identifies the packet as a V2 data channel packet and gives the + * low-permutation version of the key-id to the recipient, so it knows which + * decrypt key to use. + * + * The peer-id is sent by clients to servers to help the server determine to + * select the decrypt key when the client is roaming between addresses/ports. + * + * @param multi - The TLS state for this packet's destination VPN tunnel. + * @param buf - The buffer to write the header to. + * + * @ingroup data_crypto + */ +void +tls_prepend_opcode_v2 (const struct tls_multi *multi, struct buffer *buf); + +/** + * Perform some accounting for the key state used. * @ingroup data_crypto * * @param multi - The TLS state for this packet's destination VPN tunnel. @@ -432,6 +466,29 @@ bool tls_send_payload (struct tls_multi *multi, bool tls_rec_payload (struct tls_multi *multi, struct buffer *buf); +/** + * Updates remote address in TLS sessions. + * + * @param multi - Tunnel to update + * @param addr - new address + */ +void tls_update_remote_addr (struct tls_multi *multi, + const struct link_socket_actual *addr); + +/** + * Update TLS session crypto parameters (cipher and auth) and derive data + * channel keys based on the supplied options. + * + * @param session The TLS session to update. + * @param options The options to use when updating session. + * @param frame The frame options for this session (frame overhead is + * adjusted based on the selected cipher/auth). + * + * @return true if updating succeeded, false otherwise. + */ +bool tls_session_update_crypto_params(struct tls_session *session, + const struct options *options, struct frame *frame); + #ifdef MANAGEMENT_DEF_AUTH static inline char * tls_get_peer_info(const struct tls_multi *multi) @@ -440,6 +497,21 @@ tls_get_peer_info(const struct tls_multi *multi) } #endif +/** + * Return the Negotiable Crypto Parameters version advertised in the peer info + * string, or 0 if none specified. + */ +int tls_peer_info_ncp_ver(const char *peer_info); + +/** + * Check whether the ciphers in the supplied list are supported. + * + * @param list Colon-separated list of ciphers + * + * @returns true iff all ciphers in list are supported. + */ +bool tls_check_ncp_cipher_list(const char *list); + /* * inline functions */ @@ -503,6 +575,6 @@ void show_tls_performance_stats(void); /*#define EXTRACT_X509_FIELD_TEST*/ void extract_x509_field_test (void); -#endif /* ENABLE_CRYPTO && ENABLE_SSL */ +#endif /* ENABLE_CRYPTO */ #endif diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index 4b35e51..0777c61 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -38,10 +38,10 @@ #include "ssl_verify_openssl.h" #define SSLAPI SSLAPI_OPENSSL #endif -#ifdef ENABLE_CRYPTO_POLARSSL -#include "ssl_polarssl.h" -#include "ssl_verify_polarssl.h" -#define SSLAPI SSLAPI_POLARSSL +#ifdef ENABLE_CRYPTO_MBEDTLS +#include "ssl_mbedtls.h" +#include "ssl_verify_mbedtls.h" +#define SSLAPI SSLAPI_MBEDTLS #endif /* Ensure that SSLAPI got a sane value if SSL is disabled or unknown */ @@ -124,21 +124,21 @@ int tls_version_parse(const char *vstr, const char *extra); */ int tls_version_max(void); +#ifdef ENABLE_CRYPTO + /** * Initialise a library-specific TLS context for a server. * * @param ctx TLS context to initialise - * @param ssl_flags SSLF_x flags from ssl_common.h */ -void tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags); +void tls_ctx_server_new(struct tls_root_ctx *ctx); /** * Initialises a library-specific TLS context for a client. * * @param ctx TLS context to initialise - * @param ssl_flags SSLF_x flags from ssl_common.h */ -void tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags); +void tls_ctx_client_new(struct tls_root_ctx *ctx); /** * Frees the library-specific TLSv1 context @@ -170,8 +170,9 @@ void tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags); /** * Restrict the list of ciphers that can be used within the TLS context. * - * @param ctx TLS context to restrict - * @param ciphers String containing : delimited cipher names. + * @param ctx TLS context to restrict, must be valid. + * @param ciphers String containing : delimited cipher names, or NULL to use + * sane defaults. */ void tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers); @@ -196,6 +197,16 @@ void tls_ctx_check_cert_time (const struct tls_root_ctx *ctx); void tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file, const char *dh_file_inline); +/** + * Load Elliptic Curve Parameters, and load them into the library-specific + * TLS context. + * + * @param ctx TLS context to use + * @param curve_name The name of the elliptic curve to load. + */ +void tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name + ); + /** * Load PKCS #12 file for key, cert and (optionally) CA certs, and add to * library-specific TLS context. @@ -221,7 +232,7 @@ int tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, */ #ifdef ENABLE_CRYPTOAPI void tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert); -#endif /* WIN32 */ +#endif /* _WIN32 */ /** * Load certificate file into the given TLS context. If the given certificate @@ -299,9 +310,9 @@ void tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs const char *extra_certs_file_inline ); -#ifdef ENABLE_CRYPTO_POLARSSL +#ifdef ENABLE_CRYPTO_MBEDTLS /** - * Add a personalisation string to the PolarSSL RNG, based on the certificate + * Add a personalisation string to the mbed TLS RNG, based on the certificate * loaded into the given context. * * @param ctx TLS context to use @@ -334,6 +345,30 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl, */ void key_state_ssl_free(struct key_state_ssl *ks_ssl); +/** + * Reload the Certificate Revocation List for the SSL channel + * + * @param ssl_ctx The TLS context to use when reloading the CRL + * @param crl_file The file name to load the CRL from, or + * "[[INLINE]]" in the case of inline files. + * @param crl_inline A string containing the CRL + */ +void tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, + const char *crl_file, const char *crl_inline); + +/** + * Keying Material Exporters [RFC 5705] allows additional keying material to be + * derived from existing TLS channel. This exported keying material can then be + * used for a variety of purposes. + * + * @param ks_ssl The SSL channel's state info + * @param session The session associated with the given key_state + */ + +void +key_state_export_keying_material(struct key_state_ssl *ks_ssl, + struct tls_session *session) __attribute__((nonnull)); + /**************************************************************************/ /** @addtogroup control_tls * @{ */ @@ -471,6 +506,11 @@ void print_details (struct key_state_ssl * ks_ssl, const char *prefix); */ void show_available_tls_ciphers (const char *tls_ciphers); +/* + * Show the available elliptic curves in the crypto library + */ +void show_available_curves (void); + /* * The OpenSSL library has a notion of preference in TLS ciphers. Higher * preference == more secure. Return the highest preference cipher. @@ -483,4 +523,5 @@ void get_highest_preference_tls_cipher (char *buf, int size); */ const char * get_ssl_library_version(void); +#endif /* ENABLE_CRYPTO */ #endif /* SSL_BACKEND_H_ */ diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 449172d..28702af 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -149,7 +149,12 @@ struct key_source2 { struct key_state { int state; - int key_id; /* inherited from struct tls_session below */ + + /** + * Key id for this key_state, inherited from struct tls_session. + * @see tls_session::key_id. + */ + int key_id; struct key_state_ssl ks_ssl; /* contains SSL object and BIOs for the control channel */ @@ -160,9 +165,8 @@ struct key_state int initial_opcode; /* our initial P_ opcode */ struct session_id session_id_remote; /* peer's random session ID */ struct link_socket_actual remote_addr; /* peer's IP addr */ - struct packet_id packet_id; /* for data channel, to prevent replay attacks */ - struct key_ctx_bi key; /* data channel keys for encrypt/decrypt/hmac */ + struct crypto_options crypto_options;/* data channel crypto options */ struct key_source2 *key_src; /* source entropy for key expansion */ @@ -200,6 +204,18 @@ struct key_state #endif }; +/** Control channel wrapping (--tls-auth/--tls-crypt) context */ +struct tls_wrap_ctx +{ + enum { + TLS_WRAP_NONE = 0, /**< No control channel wrapping */ + TLS_WRAP_AUTH, /**< Control channel authentication */ + TLS_WRAP_CRYPT, /**< Control channel encryption and authentication */ + } mode; /**< Control channel wrapping mode */ + struct crypto_options opt; /**< Crypto state */ + struct buffer work; /**< Work buffer (only for --tls-crypt) */ +}; + /* * Our const options, obtained directly or derived from * command line options. @@ -232,6 +248,8 @@ struct tls_options #ifdef ENABLE_OCC bool disable_occ; #endif + int mode; + bool pull; #ifdef ENABLE_PUSH_PEER_INFO int push_peer_info_detail; #endif @@ -248,6 +266,7 @@ struct tls_options int verify_x509_type; const char *verify_x509_name; const char *crl_file; + const char *crl_file_inline; int ns_cert_type; unsigned remote_cert_ku[MAX_PARMS]; const char *remote_cert_eku; @@ -259,6 +278,7 @@ struct tls_options bool pass_config_info; /* struct crypto_option flags */ + unsigned int crypto_flags; unsigned int crypto_flags_and; unsigned int crypto_flags_or; @@ -266,9 +286,12 @@ struct tls_options int replay_time; /* --replay-window parm */ bool tcp_mode; - /* packet authentication for TLS handshake */ - struct crypto_options tls_auth; - struct key_ctx_bi tls_auth_key; + const char *config_ciphername; + const char *config_authname; + bool ncp_enabled; + + /** TLS handshake wrapping state */ + struct tls_wrap_ctx tls_wrap; /* frame parameters for TLS control channel */ struct frame frame; @@ -278,6 +301,9 @@ struct tls_options bool auth_user_pass_verify_script_via_file; const char *tmp_dir; const char *auth_user_pass_file; + bool auth_token_generate; /**< Generate auth-tokens on successful user/pass auth, + * set via options->auth_token_generate. */ + unsigned int auth_token_lifetime; /* use the client-config-dir as a positive authenticator */ const char *client_config_dir_exclusive; @@ -286,10 +312,16 @@ struct tls_options struct env_set *es; const struct plugin_list *plugins; + /* compression parms */ +#ifdef USE_COMP + struct compress_options comp_options; +#endif + /* configuration file SSL-related boolean and low-permutation options */ # define SSLF_CLIENT_CERT_NOT_REQUIRED (1<<0) -# define SSLF_USERNAME_AS_COMMON_NAME (1<<1) -# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2) +# define SSLF_CLIENT_CERT_OPTIONAL (1<<1) +# define SSLF_USERNAME_AS_COMMON_NAME (1<<2) +# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<3) # define SSLF_OPT_VERIFY (1<<4) # define SSLF_CRL_VERIFY_DIR (1<<5) # define SSLF_TLS_VERSION_MIN_SHIFT 6 @@ -302,9 +334,7 @@ struct tls_options struct man_def_auth_context *mda_context; #endif -#ifdef ENABLE_X509_TRACK const struct x509_track *x509_track; -#endif #ifdef ENABLE_CLIENT_CR const struct static_challenge_info *sci; @@ -312,6 +342,11 @@ struct tls_options /* --gremlin bits */ int gremlin; + + /* Keying Material Exporter [RFC 5705] parameters */ + const char *ekm_label; + size_t ekm_label_size; + size_t ekm_size; }; /** @addtogroup control_processor @@ -328,6 +363,9 @@ struct tls_options /** @} name Index of key_state objects within a tls_session structure */ /** @} addtogroup control_processor */ +#define AUTH_TOKEN_SIZE 32 /**< Size of server side generated auth tokens. + * 32 bytes == 256 bits + */ /** * Security parameter state of a single session within a VPN tunnel. @@ -354,12 +392,17 @@ struct tls_session bool burst; /* authenticate control packets */ - struct crypto_options tls_auth; - struct packet_id tls_auth_pid; + struct tls_wrap_ctx tls_wrap; int initial_opcode; /* our initial P_ opcode */ struct session_id session_id; /* our random session ID */ - int key_id; /* increments with each soft reset (for key renegotiation) */ + + /** + * The current active key id, used to keep track of renegotiations. + * key_id increments with each soft reset to KEY_ID_MASK then recycles back + * to 1. This way you know that if key_id is 0, it is the first key. + */ + int key_id; int limit_next; /* used for traffic shaping on the control channel */ @@ -481,20 +524,29 @@ struct tls_multi */ char *client_reason; + /* Time of last call to tls_authentication_status */ + time_t tas_last; +#endif + +#if P2MP_SERVER /* * A multi-line string of general-purpose info received from peer * over control channel. */ char *peer_info; - - /* Time of last call to tls_authentication_status */ - time_t tas_last; #endif /* For P_DATA_V2 */ uint32_t peer_id; bool use_peer_id; + char *auth_token; /**< If server sends a generated auth-token, + * this is the token to use for future + * user/pass authentications in this session. + */ + time_t auth_token_tstamp; /**< timestamp of the generated token */ + bool auth_token_sent; /**< If server uses --auth-gen-token and + * token has been sent to client */ /* * Our session objects. */ diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c new file mode 100644 index 0000000..7fa35a7 --- /dev/null +++ b/src/openvpn/ssl_mbedtls.c @@ -0,0 +1,1226 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * Copyright (C) 2006-2010, Brainspark B.V. + * + * 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 + */ + +/** + * @file Control Channel mbed TLS Backend + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) + +#include "errlevel.h" +#include "ssl_backend.h" +#include "base64.h" +#include "buffer.h" +#include "misc.h" +#include "manage.h" +#include "ssl_common.h" + +#include + +#include "ssl_verify_mbedtls.h" +#include +#include +#include +#include +#include +#include +#include + +void +tls_init_lib() +{ +} + +void +tls_free_lib() +{ +} + +void +tls_clear_error() +{ +} + +void +tls_ctx_server_new(struct tls_root_ctx *ctx) +{ + ASSERT(NULL != ctx); + CLEAR(*ctx); + + ALLOC_OBJ_CLEAR(ctx->dhm_ctx, mbedtls_dhm_context); + + ALLOC_OBJ_CLEAR(ctx->ca_chain, mbedtls_x509_crt); + + ctx->endpoint = MBEDTLS_SSL_IS_SERVER; + ctx->initialised = true; +} + +void +tls_ctx_client_new(struct tls_root_ctx *ctx) +{ + ASSERT(NULL != ctx); + CLEAR(*ctx); + + ALLOC_OBJ_CLEAR(ctx->dhm_ctx, mbedtls_dhm_context); + ALLOC_OBJ_CLEAR(ctx->ca_chain, mbedtls_x509_crt); + + ctx->endpoint = MBEDTLS_SSL_IS_CLIENT; + ctx->initialised = true; +} + +void +tls_ctx_free(struct tls_root_ctx *ctx) +{ + if (ctx) + { + mbedtls_pk_free(ctx->priv_key); + if (ctx->priv_key) + free(ctx->priv_key); + + mbedtls_x509_crt_free(ctx->ca_chain); + if (ctx->ca_chain) + free(ctx->ca_chain); + + mbedtls_x509_crt_free(ctx->crt_chain); + if (ctx->crt_chain) + free(ctx->crt_chain); + + mbedtls_dhm_free(ctx->dhm_ctx); + if (ctx->dhm_ctx) + free(ctx->dhm_ctx); + + mbedtls_x509_crl_free(ctx->crl); + if (ctx->crl) + { + free(ctx->crl); + } + +#if defined(ENABLE_PKCS11) + if (ctx->priv_key_pkcs11 != NULL) { + mbedtls_pkcs11_priv_key_free(ctx->priv_key_pkcs11); + free(ctx->priv_key_pkcs11); + } +#endif +#if defined(MANAGMENT_EXTERNAL_KEY) + if (ctx->external_key != NULL) + free(ctx->external_key); +#endif + + if (ctx->allowed_ciphers) + free(ctx->allowed_ciphers); + + CLEAR(*ctx); + + ctx->initialised = false; + + } +} + +bool +tls_ctx_initialised(struct tls_root_ctx *ctx) +{ + ASSERT(NULL != ctx); + return ctx->initialised; +} + +void +key_state_export_keying_material(struct key_state_ssl *ssl, + struct tls_session *session) +{ +} + +void +tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) +{ +} + +static const char * +tls_translate_cipher_name (const char * cipher_name) { + const tls_cipher_name_pair * pair = tls_get_cipher_name_pair(cipher_name, strlen(cipher_name)); + + if (NULL == pair) + { + // No translation found, return original + return cipher_name; + } + + if (0 != strcmp(cipher_name, pair->iana_name)) + { + // Deprecated name found, notify user + msg(M_WARN, "Deprecated cipher suite name '%s', please use IANA name '%s'", pair->openssl_name, pair->iana_name); + } + + return pair->iana_name; +} + +void +tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) +{ + char *tmp_ciphers, *tmp_ciphers_orig, *token; + int i, cipher_count; + int ciphers_len; + + if (NULL == ciphers) + return; /* Nothing to do */ + + ciphers_len = strlen (ciphers); + + ASSERT (NULL != ctx); + ASSERT (0 != ciphers_len); + + /* Get number of ciphers */ + for (i = 0, cipher_count = 1; i < ciphers_len; i++) + if (ciphers[i] == ':') + cipher_count++; + + /* Allocate an array for them */ + ALLOC_ARRAY_CLEAR(ctx->allowed_ciphers, int, cipher_count+1) + + /* Parse allowed ciphers, getting IDs */ + i = 0; + tmp_ciphers_orig = tmp_ciphers = string_alloc (ciphers, NULL); + + token = strtok (tmp_ciphers, ":"); + while(token) + { + ctx->allowed_ciphers[i] = mbedtls_ssl_get_ciphersuite_id ( + tls_translate_cipher_name (token)); + if (0 != ctx->allowed_ciphers[i]) + i++; + token = strtok (NULL, ":"); + } + free(tmp_ciphers_orig); +} + +void +tls_ctx_check_cert_time (const struct tls_root_ctx *ctx) +{ + ASSERT (ctx); + if (ctx->crt_chain == NULL) + { + return; /* Nothing to check if there is no certificate */ + } + + if (mbedtls_x509_time_is_future (&ctx->crt_chain->valid_from)) + { + msg (M_WARN, "WARNING: Your certificate is not yet valid!"); + } + + if (mbedtls_x509_time_is_past (&ctx->crt_chain->valid_to)) + { + msg (M_WARN, "WARNING: Your certificate has expired!"); + } +} + +void +tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file, + const char *dh_inline + ) +{ + if (!strcmp (dh_file, INLINE_FILE_TAG) && dh_inline) + { + if (!mbed_ok(mbedtls_dhm_parse_dhm(ctx->dhm_ctx, + (const unsigned char *) dh_inline, strlen(dh_inline)+1))) + msg (M_FATAL, "Cannot read inline DH parameters"); + } +else + { + if (!mbed_ok(mbedtls_dhm_parse_dhmfile(ctx->dhm_ctx, dh_file))) + msg (M_FATAL, "Cannot read DH parameters from file %s", dh_file); + } + + msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with " counter_format " bit key", + (counter_type) 8 * mbedtls_mpi_size(&ctx->dhm_ctx->P)); +} + +void +tls_ctx_load_ecdh_params (struct tls_root_ctx *ctx, const char *curve_name + ) +{ + if (NULL != curve_name) + msg(M_WARN, "WARNING: mbed TLS builds do not support specifying an ECDH " + "curve, using default curves."); +} + +int +tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, + const char *pkcs12_file_inline, + bool load_ca_file + ) +{ + msg(M_FATAL, "PKCS #12 files not yet supported for mbed TLS."); + return 0; +} + +#ifdef ENABLE_CRYPTOAPI +void +tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) +{ + msg(M_FATAL, "Windows CryptoAPI not yet supported for mbed TLS."); +} +#endif /* _WIN32 */ + +void +tls_ctx_load_cert_file (struct tls_root_ctx *ctx, const char *cert_file, + const char *cert_inline + ) +{ + ASSERT(NULL != ctx); + + if (!ctx->crt_chain) + { + ALLOC_OBJ_CLEAR(ctx->crt_chain, mbedtls_x509_crt); + } + + if (!strcmp (cert_file, INLINE_FILE_TAG) && cert_inline) + { + if (!mbed_ok(mbedtls_x509_crt_parse(ctx->crt_chain, + (const unsigned char *) cert_inline, strlen(cert_inline)+1))) + msg (M_FATAL, "Cannot load inline certificate file"); + } + else + { + if (!mbed_ok(mbedtls_x509_crt_parse_file(ctx->crt_chain, cert_file))) + { + msg (M_FATAL, "Cannot load certificate file %s", cert_file); + } + } +} + +int +tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, + const char *priv_key_inline + ) +{ + int status; + ASSERT(NULL != ctx); + + if (!ctx->priv_key) + { + ALLOC_OBJ_CLEAR(ctx->priv_key, mbedtls_pk_context); + } + + if (!strcmp (priv_key_file, INLINE_FILE_TAG) && priv_key_inline) + { + status = mbedtls_pk_parse_key(ctx->priv_key, + (const unsigned char *) priv_key_inline, strlen(priv_key_inline)+1, + NULL, 0); + + if (MBEDTLS_ERR_PK_PASSWORD_REQUIRED == status) + { + char passbuf[512] = {0}; + pem_password_callback(passbuf, 512, 0, NULL); + status = mbedtls_pk_parse_key(ctx->priv_key, + (const unsigned char *) priv_key_inline, + strlen(priv_key_inline)+1, (unsigned char *) passbuf, + strlen(passbuf)); + } + } + else + { + status = mbedtls_pk_parse_keyfile(ctx->priv_key, priv_key_file, NULL); + if (MBEDTLS_ERR_PK_PASSWORD_REQUIRED == status) + { + char passbuf[512] = {0}; + pem_password_callback(passbuf, 512, 0, NULL); + status = mbedtls_pk_parse_keyfile(ctx->priv_key, priv_key_file, passbuf); + } + } + if (!mbed_ok(status)) + { +#ifdef ENABLE_MANAGEMENT + if (management && (MBEDTLS_ERR_PK_PASSWORD_MISMATCH == status)) + management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); +#endif + msg (M_WARN, "Cannot load private key file %s", priv_key_file); + return 1; + } + + if (!mbed_ok(mbedtls_pk_check_pair(&ctx->crt_chain->pk, ctx->priv_key))) + { + msg (M_WARN, "Private key does not match the certificate"); + return 1; + } + + return 0; +} + +#ifdef MANAGMENT_EXTERNAL_KEY + + +struct external_context { + size_t signature_length; +}; + +/** + * external_pkcs1_sign implements a mbed TLS rsa_sign_func callback, that uses + * the management interface to request an RSA signature for the supplied hash. + * + * @param ctx_voidptr Management external key context. + * @param f_rng (Unused) + * @param p_rng (Unused) + * @param mode RSA mode (should be RSA_PRIVATE). + * @param md_alg Message digest ('hash') algorithm type. + * @param hashlen Length of hash (overridden by length specified by md_alg + * if md_alg != MBEDTLS_MD_NONE). + * @param hash The digest ('hash') to sign. Should have a size + * matching the length of md_alg (if != MBEDTLS_MD_NONE), + * or hashlen otherwise. + * @param sig Buffer that returns the signature. Should be at least of + * size ctx->signature_length. + * + * @return 0 on success, non-zero mbed TLS error code on failure. + */ +static inline int external_pkcs1_sign( void *ctx_voidptr, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int mode, + mbedtls_md_type_t md_alg, unsigned int hashlen, const unsigned char *hash, + unsigned char *sig ) +{ + struct external_context * const ctx = ctx_voidptr; + char *in_b64 = NULL; + char *out_b64 = NULL; + int rv; + unsigned char *p = sig; + size_t asn_len = 0, oid_size = 0, sig_len = 0; + const char *oid = NULL; + + if( NULL == ctx ) + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + + if( MBEDTLS_RSA_PRIVATE != mode ) + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + + /* + * Support a wide range of hashes. TLSv1.1 and before only need SIG_RSA_RAW, + * but TLSv1.2 needs the full suite of hashes. + * + * This code has been taken from mbed TLS pkcs11_sign(), under the GPLv2.0+. + */ + if( md_alg != MBEDTLS_MD_NONE ) + { + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type( md_alg ); + if( md_info == NULL ) + return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); + + if (!mbed_ok(mbedtls_oid_get_oid_by_md( md_alg, &oid, &oid_size ))) + return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA ); + + hashlen = mbedtls_md_get_size( md_info ); + asn_len = 10 + oid_size; + } + + sig_len = ctx->signature_length; + if ( (SIZE_MAX - hashlen) < asn_len || (hashlen + asn_len) > sig_len ) + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + + if( md_alg != MBEDTLS_MD_NONE ) + { + /* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ + *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x08 + oid_size + hashlen ); + *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x04 + oid_size ); + *p++ = MBEDTLS_ASN1_OID; + *p++ = oid_size & 0xFF; + memcpy( p, oid, oid_size ); + p += oid_size; + *p++ = MBEDTLS_ASN1_NULL; + *p++ = 0x00; + *p++ = MBEDTLS_ASN1_OCTET_STRING; + *p++ = hashlen; + + /* Determine added ASN length */ + asn_len = p - sig; + } + + /* Copy the hash to be signed */ + memcpy( p, hash, hashlen ); + + /* convert 'from' to base64 */ + if (openvpn_base64_encode (sig, asn_len + hashlen, &in_b64) <= 0) + { + rv = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + goto done; + } + + /* call MI for signature */ + if (management) + out_b64 = management_query_rsa_sig (management, in_b64); + if (!out_b64) + { + rv = MBEDTLS_ERR_RSA_PRIVATE_FAILED; + goto done; + } + + /* decode base64 signature to binary and verify length */ + if ( openvpn_base64_decode (out_b64, sig, ctx->signature_length) != + ctx->signature_length ) + { + rv = MBEDTLS_ERR_RSA_PRIVATE_FAILED; + goto done; + } + + rv = 0; + +done: + if (in_b64) + free (in_b64); + if (out_b64) + free (out_b64); + return rv; +} + +static inline size_t external_key_len(void *vctx) +{ + struct external_context * const ctx = vctx; + + return ctx->signature_length; +} + +int +tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, + const char *cert_file, const char *cert_file_inline) +{ + ASSERT(NULL != ctx); + + tls_ctx_load_cert_file(ctx, cert_file, cert_file_inline); + + if (ctx->crt_chain == NULL) + return 0; + + ALLOC_OBJ_CLEAR (ctx->external_key, struct external_context); + ctx->external_key->signature_length = mbedtls_pk_get_len (&ctx->crt_chain->pk); + + ALLOC_OBJ_CLEAR (ctx->priv_key, mbedtls_pk_context); + if (!mbed_ok (mbedtls_pk_setup_rsa_alt (ctx->priv_key, ctx->external_key, + NULL, external_pkcs1_sign, external_key_len))) + return 0; + + return 1; +} +#endif + +void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, + const char *ca_inline, const char *ca_path, bool tls_server + ) +{ + if (ca_path) + msg(M_FATAL, "ERROR: mbed TLS cannot handle the capath directive"); + + if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_inline) + { + if (!mbed_ok (mbedtls_x509_crt_parse (ctx->ca_chain, + (const unsigned char *) ca_inline, strlen(ca_inline)+1))) + msg (M_FATAL, "Cannot load inline CA certificates"); + } + else + { + /* Load CA file for verifying peer supplied certificate */ + if (!mbed_ok (mbedtls_x509_crt_parse_file (ctx->ca_chain, ca_file))) + msg (M_FATAL, "Cannot load CA certificate file %s", ca_file); + } +} + +void +tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file, + const char *extra_certs_inline + ) +{ + ASSERT(NULL != ctx); + + if (!ctx->crt_chain) + { + ALLOC_OBJ_CLEAR (ctx->crt_chain, mbedtls_x509_crt); + } + + if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_inline) + { + if (!mbed_ok(mbedtls_x509_crt_parse(ctx->crt_chain, + (const unsigned char *) extra_certs_inline, + strlen(extra_certs_inline)+1))) + msg (M_FATAL, "Cannot load inline extra-certs file"); + } + else + { + if (!mbed_ok(mbedtls_x509_crt_parse_file(ctx->crt_chain, extra_certs_file))) + msg (M_FATAL, "Cannot load extra-certs file: %s", extra_certs_file); + } +} + +/* ************************************** + * + * Key-state specific functions + * + ***************************************/ + +/* + * "Endless buffer" + */ + +static inline void buf_free_entry(buffer_entry *entry) +{ + if (NULL != entry) + { + free(entry->data); + free(entry); + } +} + +static void buf_free_entries(endless_buffer *buf) +{ + while(buf->first_block) + { + buffer_entry *cur_block = buf->first_block; + buf->first_block = cur_block->next_block; + buf_free_entry(cur_block); + } + buf->last_block = NULL; +} + +static int endless_buf_read( endless_buffer *in, unsigned char * out, size_t out_len ) +{ + size_t read_len = 0; + + if (in->first_block == NULL) + return MBEDTLS_ERR_SSL_WANT_READ; + + while (in->first_block != NULL && read_len < out_len) + { + int block_len = in->first_block->length - in->data_start; + if (block_len <= out_len - read_len) + { + buffer_entry *cur_entry = in->first_block; + memcpy(out + read_len, cur_entry->data + in->data_start, + block_len); + + read_len += block_len; + + in->first_block = cur_entry->next_block; + in->data_start = 0; + + if (in->first_block == NULL) + in->last_block = NULL; + + buf_free_entry(cur_entry); + } + else + { + memcpy(out + read_len, in->first_block->data + in->data_start, + out_len - read_len); + in->data_start += out_len - read_len; + read_len = out_len; + } + } + + return read_len; +} + +static int endless_buf_write( endless_buffer *out, const unsigned char *in, size_t len ) +{ + buffer_entry *new_block = malloc(sizeof(buffer_entry)); + if (NULL == new_block) + return MBEDTLS_ERR_NET_SEND_FAILED; + + new_block->data = malloc(len); + if (NULL == new_block->data) + { + free(new_block); + return MBEDTLS_ERR_NET_SEND_FAILED; + } + + new_block->length = len; + new_block->next_block = NULL; + + memcpy(new_block->data, in, len); + + if (NULL == out->first_block) + out->first_block = new_block; + + if (NULL != out->last_block) + out->last_block->next_block = new_block; + + out->last_block = new_block; + + return len; +} + +static int ssl_bio_read( void *ctx, unsigned char *out, size_t out_len) +{ + bio_ctx *my_ctx = (bio_ctx *) ctx; + return endless_buf_read (&my_ctx->in, out, out_len); +} + +static int ssl_bio_write( void *ctx, const unsigned char *in, size_t in_len) +{ + bio_ctx *my_ctx = (bio_ctx *) ctx; + return endless_buf_write (&my_ctx->out, in, in_len); +} + +static void my_debug( void *ctx, int level, const char *file, int line, + const char *str ) +{ + int my_loglevel = (level < 3) ? D_TLS_DEBUG_MED : D_TLS_DEBUG; + msg (my_loglevel, "mbed TLS msg (%s:%d): %s", file, line, str); +} + +/* + * Further personalise the RNG using a hash of the public key + */ +void tls_ctx_personalise_random(struct tls_root_ctx *ctx) +{ + static char old_sha256_hash[32] = {0}; + unsigned char sha256_hash[32] = {0}; + mbedtls_ctr_drbg_context *cd_ctx = rand_ctx_get(); + + if (NULL != ctx->crt_chain) + { + mbedtls_x509_crt *cert = ctx->crt_chain; + + mbedtls_sha256(cert->tbs.p, cert->tbs.len, sha256_hash, false); + if ( 0 != memcmp(old_sha256_hash, sha256_hash, sizeof(sha256_hash))) + { + mbedtls_ctr_drbg_update(cd_ctx, sha256_hash, 32); + memcpy(old_sha256_hash, sha256_hash, sizeof(old_sha256_hash)); + } + } +} + +int +tls_version_max(void) +{ +#if defined(MBEDTLS_SSL_MAJOR_VERSION_3) && defined(MBEDTLS_SSL_MINOR_VERSION_3) + return TLS_VER_1_2; +#elif defined(MBEDTLS_SSL_MAJOR_VERSION_3) && defined(MBEDTLS_SSL_MINOR_VERSION_2) + return TLS_VER_1_1; +#else + return TLS_VER_1_0; +#endif +} + +/** + * Convert an OpenVPN tls-version variable to mbed TLS format (i.e. a major and + * minor ssl version number). + * + * @param tls_ver The tls-version variable to convert. + * @param major Returns the TLS major version in mbed TLS format. + * Must be a valid pointer. + * @param minor Returns the TLS minor version in mbed TLS format. + * Must be a valid pointer. + */ +static void tls_version_to_major_minor(int tls_ver, int *major, int *minor) { + ASSERT(major); + ASSERT(minor); + + switch (tls_ver) + { + case TLS_VER_1_0: + *major = MBEDTLS_SSL_MAJOR_VERSION_3; + *minor = MBEDTLS_SSL_MINOR_VERSION_1; + break; + case TLS_VER_1_1: + *major = MBEDTLS_SSL_MAJOR_VERSION_3; + *minor = MBEDTLS_SSL_MINOR_VERSION_2; + break; + case TLS_VER_1_2: + *major = MBEDTLS_SSL_MAJOR_VERSION_3; + *minor = MBEDTLS_SSL_MINOR_VERSION_3; + break; + default: + msg(M_FATAL, "%s: invalid TLS version %d", __func__, tls_ver); + break; + } +} + +void +tls_ctx_reload_crl(struct tls_root_ctx *ctx, const char *crl_file, + const char *crl_inline) +{ + ASSERT (crl_file); + + if (ctx->crl == NULL) + { + ALLOC_OBJ_CLEAR(ctx->crl, mbedtls_x509_crl); + } + mbedtls_x509_crl_free(ctx->crl); + + if (!strcmp (crl_file, INLINE_FILE_TAG) && crl_inline) + { + if (!mbed_ok(mbedtls_x509_crl_parse(ctx->crl, + (const unsigned char *)crl_inline, strlen(crl_inline)+1))) + { + msg (M_WARN, "CRL: cannot parse inline CRL"); + goto err; + } + } + else + { + if (!mbed_ok(mbedtls_x509_crl_parse_file(ctx->crl, crl_file))) + { + msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file); + goto err; + } + } + return; + +err: + mbedtls_x509_crl_free(ctx->crl); +} + +void key_state_ssl_init(struct key_state_ssl *ks_ssl, + const struct tls_root_ctx *ssl_ctx, bool is_server, struct tls_session *session) +{ + ASSERT(NULL != ssl_ctx); + ASSERT(ks_ssl); + CLEAR(*ks_ssl); + + /* Initialise SSL config */ + mbedtls_ssl_config_init(&ks_ssl->ssl_config); + mbedtls_ssl_config_defaults(&ks_ssl->ssl_config, ssl_ctx->endpoint, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); +#ifdef MBEDTLS_DEBUG_C + mbedtls_debug_set_threshold(3); +#endif + mbedtls_ssl_conf_dbg (&ks_ssl->ssl_config, my_debug, NULL); + mbedtls_ssl_conf_rng (&ks_ssl->ssl_config, mbedtls_ctr_drbg_random, + rand_ctx_get()); + + if (ssl_ctx->allowed_ciphers) + mbedtls_ssl_conf_ciphersuites (&ks_ssl->ssl_config, ssl_ctx->allowed_ciphers); + + /* Disable record splitting (for now). OpenVPN assumes records are sent + * unfragmented, and changing that will require thorough review and + * testing. Since OpenVPN is not susceptible to BEAST, we can just + * disable record splitting as a quick fix. */ +#if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING) + mbedtls_ssl_conf_cbc_record_splitting (&ks_ssl->ssl_config, + MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED); +#endif /* MBEDTLS_SSL_CBC_RECORD_SPLITTING */ + + /* Initialise authentication information */ + if (is_server) + mbed_ok (mbedtls_ssl_conf_dh_param_ctx(&ks_ssl->ssl_config, + ssl_ctx->dhm_ctx)); + + mbed_ok (mbedtls_ssl_conf_own_cert(&ks_ssl->ssl_config, ssl_ctx->crt_chain, + ssl_ctx->priv_key)); + + /* Initialise SSL verification */ +#if P2MP_SERVER + if (session->opt->ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) + { + mbedtls_ssl_conf_authmode(&ks_ssl->ssl_config, MBEDTLS_SSL_VERIFY_OPTIONAL); + } + else if (!(session->opt->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)) +#endif + { + mbedtls_ssl_conf_authmode (&ks_ssl->ssl_config, MBEDTLS_SSL_VERIFY_REQUIRED); + } + mbedtls_ssl_conf_verify (&ks_ssl->ssl_config, verify_callback, session); + + /* TODO: mbed TLS does not currently support sending the CA chain to the client */ + mbedtls_ssl_conf_ca_chain (&ks_ssl->ssl_config, ssl_ctx->ca_chain, ssl_ctx->crl); + + /* Initialize minimum TLS version */ + { + const int tls_version_min = + (session->opt->ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & + SSLF_TLS_VERSION_MIN_MASK; + + /* default to TLS 1.0 */ + int major = MBEDTLS_SSL_MAJOR_VERSION_3; + int minor = MBEDTLS_SSL_MINOR_VERSION_1; + + if (tls_version_min > TLS_VER_UNSPEC) + tls_version_to_major_minor(tls_version_min, &major, &minor); + + mbedtls_ssl_conf_min_version(&ks_ssl->ssl_config, major, minor); + } + + /* Initialize maximum TLS version */ + { + const int tls_version_max = + (session->opt->ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & + SSLF_TLS_VERSION_MAX_MASK; + + if (tls_version_max > TLS_VER_UNSPEC) + { + int major, minor; + tls_version_to_major_minor(tls_version_max, &major, &minor); + mbedtls_ssl_conf_max_version(&ks_ssl->ssl_config, major, minor); + } + } + + /* Initialise SSL context */ + ALLOC_OBJ_CLEAR(ks_ssl->ctx, mbedtls_ssl_context); + mbedtls_ssl_init(ks_ssl->ctx); + mbedtls_ssl_setup(ks_ssl->ctx, &ks_ssl->ssl_config); + + /* Initialise BIOs */ + CLEAR (ks_ssl->bio_ctx); + mbedtls_ssl_set_bio (ks_ssl->ctx, &ks_ssl->bio_ctx, ssl_bio_write, + ssl_bio_read, NULL); +} + +void +key_state_ssl_free(struct key_state_ssl *ks_ssl) +{ + if (ks_ssl) { + if (ks_ssl->ctx) + { + mbedtls_ssl_free(ks_ssl->ctx); + free(ks_ssl->ctx); + } + mbedtls_ssl_config_free(&ks_ssl->ssl_config); + buf_free_entries(&ks_ssl->bio_ctx.in); + buf_free_entries(&ks_ssl->bio_ctx.out); + CLEAR(*ks_ssl); + } +} + +int +key_state_write_plaintext (struct key_state_ssl *ks, struct buffer *buf) +{ + int retval = 0; + + ASSERT (buf); + + retval = key_state_write_plaintext_const(ks, BPTR(buf), BLEN(buf)); + + if (1 == retval) + { + memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */ + buf->len = 0; + } + + return retval; +} + +int +key_state_write_plaintext_const (struct key_state_ssl *ks, const uint8_t *data, int len) +{ + int retval = 0; + perf_push (PERF_BIO_WRITE_PLAINTEXT); + + ASSERT (NULL != ks); + ASSERT (len >= 0); + + if (0 == len) + { + perf_pop (); + return 0; + } + + ASSERT (data); + + retval = mbedtls_ssl_write(ks->ctx, data, len); + + if (retval < 0) + { + perf_pop (); + if (MBEDTLS_ERR_SSL_WANT_WRITE == retval || MBEDTLS_ERR_SSL_WANT_READ == retval) + return 0; + mbed_log_err (D_TLS_ERRORS, retval, + "TLS ERROR: write tls_write_plaintext_const error"); + return -1; + } + + if (retval != len) + { + msg (D_TLS_ERRORS, + "TLS ERROR: write tls_write_plaintext_const incomplete %d/%d", + retval, len); + perf_pop (); + return -1; + } + + /* successful write */ + dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_plaintext_const %d bytes", retval); + + perf_pop (); + return 1; +} + +int +key_state_read_ciphertext (struct key_state_ssl *ks, struct buffer *buf, + int maxlen) +{ + int retval = 0; + int len = 0; + + perf_push (PERF_BIO_READ_CIPHERTEXT); + + ASSERT (NULL != ks); + ASSERT (buf); + ASSERT (buf->len >= 0); + + if (buf->len) + { + perf_pop (); + return 0; + } + + len = buf_forward_capacity (buf); + if (maxlen < len) + len = maxlen; + + retval = endless_buf_read(&ks->bio_ctx.out, BPTR(buf), len); + + /* Error during read, check for retry error */ + if (retval < 0) + { + perf_pop (); + if (MBEDTLS_ERR_SSL_WANT_WRITE == retval || MBEDTLS_ERR_SSL_WANT_READ == retval) + return 0; + mbed_log_err (D_TLS_ERRORS, retval, "TLS_ERROR: read tls_read_ciphertext error"); + buf->len = 0; + return -1; + } + /* Nothing read, try again */ + if (0 == retval) + { + buf->len = 0; + perf_pop (); + return 0; + } + + /* successful read */ + dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_ciphertext %d bytes", retval); + buf->len = retval; + perf_pop (); + return 1; +} + +int +key_state_write_ciphertext (struct key_state_ssl *ks, struct buffer *buf) +{ + int retval = 0; + perf_push (PERF_BIO_WRITE_CIPHERTEXT); + + ASSERT (NULL != ks); + ASSERT (buf); + ASSERT (buf->len >= 0); + + if (0 == buf->len) + { + perf_pop (); + return 0; + } + + retval = endless_buf_write(&ks->bio_ctx.in, BPTR(buf), buf->len); + + if (retval < 0) + { + perf_pop (); + + if (MBEDTLS_ERR_SSL_WANT_WRITE == retval || MBEDTLS_ERR_SSL_WANT_READ == retval) + return 0; + mbed_log_err (D_TLS_ERRORS, retval, + "TLS ERROR: write tls_write_ciphertext error"); + return -1; + } + + if (retval != buf->len) + { + msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_ciphertext incomplete %d/%d", + retval, buf->len); + perf_pop (); + return -1; + } + + /* successful write */ + dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_ciphertext %d bytes", retval); + + memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */ + buf->len = 0; + + perf_pop (); + return 1; +} + +int +key_state_read_plaintext (struct key_state_ssl *ks, struct buffer *buf, + int maxlen) +{ + int retval = 0; + int len = 0; + + perf_push (PERF_BIO_READ_PLAINTEXT); + + ASSERT (NULL != ks); + ASSERT (buf); + ASSERT (buf->len >= 0); + + if (buf->len) + { + perf_pop (); + return 0; + } + + len = buf_forward_capacity (buf); + if (maxlen < len) + len = maxlen; + + retval = mbedtls_ssl_read(ks->ctx, BPTR(buf), len); + + /* Error during read, check for retry error */ + if (retval < 0) + { + if (MBEDTLS_ERR_SSL_WANT_WRITE == retval || MBEDTLS_ERR_SSL_WANT_READ == retval) + return 0; + mbed_log_err (D_TLS_ERRORS, retval, "TLS_ERROR: read tls_read_plaintext error"); + buf->len = 0; + perf_pop (); + return -1; + } + /* Nothing read, try again */ + if (0 == retval) + { + buf->len = 0; + perf_pop (); + return 0; + } + + /* successful read */ + dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_plaintext %d bytes", retval); + buf->len = retval; + + perf_pop (); + return 1; +} + +/* ************************************** + * + * Information functions + * + * Print information for the end user. + * + ***************************************/ +void +print_details (struct key_state_ssl * ks_ssl, const char *prefix) +{ + const mbedtls_x509_crt *cert; + char s1[256]; + char s2[256]; + + s1[0] = s2[0] = 0; + openvpn_snprintf (s1, sizeof (s1), "%s %s, cipher %s", + prefix, + mbedtls_ssl_get_version (ks_ssl->ctx), + mbedtls_ssl_get_ciphersuite (ks_ssl->ctx)); + + cert = mbedtls_ssl_get_peer_cert (ks_ssl->ctx); + if (cert != NULL) + { + openvpn_snprintf (s2, sizeof (s2), ", %u bit key", + (unsigned int) mbedtls_pk_get_bitlen (&cert->pk)); + } + + msg (D_HANDSHAKE, "%s%s", s1, s2); +} + +void +show_available_tls_ciphers (const char *cipher_list) +{ + struct tls_root_ctx tls_ctx; + const int *ciphers = mbedtls_ssl_list_ciphersuites (); + + tls_ctx_server_new(&tls_ctx); + tls_ctx_restrict_ciphers(&tls_ctx, cipher_list); + + if (tls_ctx.allowed_ciphers) + ciphers = tls_ctx.allowed_ciphers; + +#ifndef ENABLE_SMALL + printf ("Available TLS Ciphers,\n"); + printf ("listed in order of preference:\n\n"); +#endif + + while (*ciphers != 0) + { + printf ("%s\n", mbedtls_ssl_get_ciphersuite_name (*ciphers)); + ciphers++; + } + printf ("\n" SHOW_TLS_CIPHER_LIST_WARNING); + + tls_ctx_free(&tls_ctx); +} + +void +show_available_curves (void) +{ + const mbedtls_ecp_curve_info *pcurve = mbedtls_ecp_curve_list (); + + if (NULL == pcurve) + msg (M_FATAL, "Cannot retrieve curve list from mbed TLS"); + + /* Print curve list */ + printf ("Available Elliptic curves, listed in order of preference:\n\n"); + while (MBEDTLS_ECP_DP_NONE != pcurve->grp_id) + { + printf("%s\n", pcurve->name); + pcurve++; + } +} + +void +get_highest_preference_tls_cipher (char *buf, int size) +{ + const char *cipher_name; + const int *ciphers = mbedtls_ssl_list_ciphersuites(); + if (*ciphers == 0) + msg (M_FATAL, "Cannot retrieve list of supported SSL ciphers."); + + cipher_name = mbedtls_ssl_get_ciphersuite_name(*ciphers); + strncpynt (buf, cipher_name, size); +} + +const char * +get_ssl_library_version(void) +{ + static char mbedtls_version[30]; + unsigned int pv = mbedtls_version_get_number(); + sprintf( mbedtls_version, "mbed TLS %d.%d.%d", + (pv>>24)&0xff, (pv>>16)&0xff, (pv>>8)&0xff ); + return mbedtls_version; +} + +#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/ssl_mbedtls.h b/src/openvpn/ssl_mbedtls.h new file mode 100644 index 0000000..3edeedc --- /dev/null +++ b/src/openvpn/ssl_mbedtls.h @@ -0,0 +1,93 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file Control Channel mbed TLS Backend + */ + +#ifndef SSL_MBEDTLS_H_ +#define SSL_MBEDTLS_H_ + +#include "syshead.h" + +#include +#include + +#if defined(ENABLE_PKCS11) +#include +#endif + +typedef struct _buffer_entry buffer_entry; + +struct _buffer_entry { + size_t length; + uint8_t *data; + buffer_entry *next_block; +}; + +typedef struct { + size_t data_start; + buffer_entry *first_block; + buffer_entry *last_block; +} endless_buffer; + +typedef struct { + endless_buffer in; + endless_buffer out; +} bio_ctx; + +/** + * Structure that wraps the TLS context. Contents differ depending on the + * SSL library used. + * + * Either \c priv_key_pkcs11 or \c priv_key must be filled in. + */ +struct tls_root_ctx { + bool initialised; /**< True if the context has been initialised */ + + int endpoint; /**< Whether or not this is a server or a client */ + + mbedtls_dhm_context *dhm_ctx; /**< Diffie-Helmann-Merkle context */ + mbedtls_x509_crt *crt_chain; /**< Local Certificate chain */ + mbedtls_x509_crt *ca_chain; /**< CA chain for remote verification */ + mbedtls_pk_context *priv_key; /**< Local private key */ + mbedtls_x509_crl *crl; /**< Certificate Revocation List */ +#if defined(ENABLE_PKCS11) + mbedtls_pkcs11_context *priv_key_pkcs11; /**< PKCS11 private key */ +#endif +#ifdef MANAGMENT_EXTERNAL_KEY + struct external_context *external_key; /**< Management external key */ +#endif + int * allowed_ciphers; /**< List of allowed ciphers for this connection */ +}; + +struct key_state_ssl { + mbedtls_ssl_config ssl_config; /**< mbedTLS global ssl config */ + mbedtls_ssl_context *ctx; /**< mbedTLS connection context */ + bio_ctx bio_ctx; +}; + + +#endif /* SSL_MBEDTLS_H_ */ diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 1dfbb23..51669fc 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -35,7 +35,7 @@ #include "syshead.h" -#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) #include "errlevel.h" #include "buffer.h" @@ -56,6 +56,9 @@ #include #include #include +#ifndef OPENSSL_NO_EC +#include +#endif /* * Allocate space in SSL objects in which to store a struct tls_session @@ -93,62 +96,23 @@ tls_clear_error() ERR_clear_error (); } -/* - * OpenSSL callback to get a temporary RSA key, mostly - * used for export ciphers. - */ -static RSA * -tmp_rsa_cb (SSL * s, int is_export, int keylength) -{ - static RSA *rsa_tmp = NULL; - if (rsa_tmp == NULL) - { - int ret = -1; - BIGNUM *bn = BN_new(); - rsa_tmp = RSA_new(); - - msg (D_HANDSHAKE, "Generating temp (%d bit) RSA key", keylength); - - if(!bn || !BN_set_word(bn, RSA_F4) || - !RSA_generate_key_ex(rsa_tmp, keylength, bn, NULL)) - crypto_msg(M_FATAL, "Failed to generate temp RSA key"); - - if (bn) BN_free( bn ); - } - return (rsa_tmp); -} - void -tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) +tls_ctx_server_new(struct tls_root_ctx *ctx) { - const int tls_version_max = - (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; - ASSERT(NULL != ctx); - if (tls_version_max == TLS_VER_1_0) - ctx->ctx = SSL_CTX_new (TLSv1_server_method ()); - else - ctx->ctx = SSL_CTX_new (SSLv23_server_method ()); + ctx->ctx = SSL_CTX_new (SSLv23_server_method ()); if (ctx->ctx == NULL) crypto_msg (M_FATAL, "SSL_CTX_new SSLv23_server_method"); - - SSL_CTX_set_tmp_rsa_callback (ctx->ctx, tmp_rsa_cb); } void -tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) +tls_ctx_client_new(struct tls_root_ctx *ctx) { - const int tls_version_max = - (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; - ASSERT(NULL != ctx); - if (tls_version_max == TLS_VER_1_0) - ctx->ctx = SSL_CTX_new (TLSv1_client_method ()); - else - ctx->ctx = SSL_CTX_new (SSLv23_client_method ()); + ctx->ctx = SSL_CTX_new (SSLv23_client_method ()); if (ctx->ctx == NULL) crypto_msg (M_FATAL, "SSL_CTX_new SSLv23_client_method"); @@ -169,6 +133,38 @@ bool tls_ctx_initialised(struct tls_root_ctx *ctx) return NULL != ctx->ctx; } +void +key_state_export_keying_material(struct key_state_ssl *ssl, + struct tls_session *session) +{ + if (session->opt->ekm_size > 0) + { +#if (OPENSSL_VERSION_NUMBER >= 0x10001000) + unsigned int size = session->opt->ekm_size; + struct gc_arena gc = gc_new(); + unsigned char* ekm = (unsigned char*) gc_malloc(size, true, &gc); + + if (SSL_export_keying_material(ssl->ssl, ekm, size, + session->opt->ekm_label, session->opt->ekm_label_size, NULL, 0, 0)) + { + unsigned int len = (size * 2) + 2; + + const char *key = format_hex_ex (ekm, size, len, 0, NULL, &gc); + setenv_str (session->opt->es, "exported_keying_material", key); + + dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s", + __func__, key); + } + else + { + msg (M_WARN, "WARNING: Export keying material failed!"); + setenv_del (session->opt->es, "exported_keying_material"); + } + gc_free(&gc); +#endif + } +} + /* * Print debugging information on SSL/TLS session negotiation. */ @@ -217,14 +213,18 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) { ASSERT(NULL != ctx); + /* default certificate verification flags */ + int flags = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + /* process SSL options including minimum TLS version we will accept from peer */ { long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + int tls_ver_max = TLS_VER_UNSPEC; const int tls_ver_min = (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK; - int tls_ver_max = - (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; + tls_ver_max = + (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; if (tls_ver_max <= TLS_VER_UNSPEC) tls_ver_max = tls_version_max(); @@ -245,6 +245,9 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) SSL_CTX_set_options (ctx->ctx, sslopt); } +#ifdef SSL_MODE_RELEASE_BUFFERS + SSL_CTX_set_mode (ctx->ctx, SSL_MODE_RELEASE_BUFFERS); +#endif SSL_CTX_set_session_cache_mode (ctx->ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_default_passwd_cb (ctx->ctx, pem_password_callback); @@ -252,14 +255,14 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) #if P2MP_SERVER if (ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) { - msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION " - "--client-cert-not-required may accept clients which do not present " - "a certificate"); + flags = 0; + } + else if (ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) + { + flags = SSL_VERIFY_PEER; } - else #endif - SSL_CTX_set_verify (ctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - verify_callback); + SSL_CTX_set_verify (ctx->ctx, flags, verify_callback); SSL_CTX_set_info_callback (ctx->ctx, info_callback); } @@ -275,12 +278,17 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) "DEFAULT" /* Disable export ciphers and openssl's 'low' and 'medium' ciphers */ ":!EXP:!LOW:!MEDIUM" + /* Disable static (EC)DH keys (no forward secrecy) */ + ":!kDH:!kECDH" + /* Disable DSA private keys */ + ":!DSS" /* Disable unsupported TLS modes */ ":!PSK:!SRP:!kRSA")) crypto_msg (M_FATAL, "Failed to set default TLS cipher list."); return; } + /* Parse supplied cipher list and pass on to OpenSSL */ size_t begin_of_cipher, end_of_cipher; const char *current_cipher; @@ -309,8 +317,8 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) // Issue warning on missing translation // %.*s format specifier expects length of type int, so guarantee // that length is small enough and cast to int. - msg (M_WARN, "No valid translation found for TLS cipher '%.*s'", - (int) MIN(current_cipher_len, 256), current_cipher); + msg (D_LOW, "No valid translation found for TLS cipher '%.*s'", + constrain_int(current_cipher_len, 0, 256), current_cipher); } else { @@ -319,7 +327,8 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) current_cipher_len = strlen(current_cipher); if (end_of_cipher - begin_of_cipher == current_cipher_len && - 0 == memcmp (&ciphers[begin_of_cipher], cipher_pair->openssl_name, end_of_cipher - begin_of_cipher)) + 0 != memcmp (&ciphers[begin_of_cipher], cipher_pair->iana_name, + end_of_cipher - begin_of_cipher)) { // Non-IANA name used, show warning msg (M_WARN, "Deprecated TLS cipher name '%s', please use IANA name '%s'", cipher_pair->openssl_name, cipher_pair->iana_name); @@ -436,6 +445,78 @@ tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file, DH_free (dh); } +void +tls_ctx_load_ecdh_params (struct tls_root_ctx *ctx, const char *curve_name + ) +{ +#ifndef OPENSSL_NO_EC + int nid = NID_undef; + EC_KEY *ecdh = NULL; + const char *sname = NULL; + + /* Generate a new ECDH key for each SSL session (for non-ephemeral ECDH) */ + SSL_CTX_set_options(ctx->ctx, SSL_OP_SINGLE_ECDH_USE); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter loading */ + if (NULL == curve_name) { + SSL_CTX_set_ecdh_auto(ctx->ctx, 1); + return; + } +#endif + /* For older OpenSSL, we'll have to do the parameter loading on our own */ + if (curve_name != NULL) + { + /* Use user supplied curve if given */ + msg (D_TLS_DEBUG, "Using user specified ECDH curve (%s)", curve_name); + nid = OBJ_sn2nid(curve_name); + } + else + { + /* Extract curve from key */ + EC_KEY *eckey = NULL; + const EC_GROUP *ecgrp = NULL; + EVP_PKEY *pkey = NULL; + + /* Little hack to get private key ref from SSL_CTX, yay OpenSSL... */ + SSL ssl; + ssl.cert = ctx->ctx->cert; + pkey = SSL_get_privatekey(&ssl); + + msg (D_TLS_DEBUG, "Extracting ECDH curve from private key"); + + if (pkey != NULL && (eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL && + (ecgrp = EC_KEY_get0_group(eckey)) != NULL) + nid = EC_GROUP_get_curve_name(ecgrp); + } + + /* Translate NID back to name , just for kicks */ + sname = OBJ_nid2sn(nid); + if (sname == NULL) sname = "(Unknown)"; + + /* Create new EC key and set as ECDH key */ + if (NID_undef == nid || NULL == (ecdh = EC_KEY_new_by_curve_name(nid))) + { + /* Creating key failed, fall back on sane default */ + ecdh = EC_KEY_new_by_curve_name(NID_secp384r1); + const char *source = (NULL == curve_name) ? + "extract curve from certificate" : "use supplied curve"; + msg (D_TLS_DEBUG_LOW, + "Failed to %s (%s), using secp384r1 instead.", source, sname); + sname = OBJ_nid2sn(NID_secp384r1); + } + + if (!SSL_CTX_set_tmp_ecdh(ctx->ctx, ecdh)) + crypto_msg (M_FATAL, "SSL_CTX_set_tmp_ecdh: cannot add curve"); + + msg (D_TLS_DEBUG_LOW, "ECDH curve %s added", sname); + + EC_KEY_free(ecdh); +#else + msg (M_DEBUG, "Your OpenSSL library was built without elliptic curve support." + " Skipping ECDH parameter loading."); +#endif /* OPENSSL_NO_EC */ +} + int tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, const char *pkcs12_file_inline, @@ -501,7 +582,6 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, /* Load Private Key */ if (!SSL_CTX_use_PrivateKey (ctx->ctx, pkey)) crypto_msg (M_FATAL, "Cannot use private key"); - warn_if_group_others_accessible (pkcs12_file); /* Check Private Key */ if (!SSL_CTX_check_private_key (ctx->ctx)) @@ -552,7 +632,7 @@ tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) if (!SSL_CTX_use_CryptoAPI_certificate (ctx->ctx, cryptoapi_cert)) crypto_msg (M_FATAL, "Cannot load certificate \"%s\" from Microsoft Certificate Store", cryptoapi_cert); } -#endif /* WIN32 */ +#endif /* ENABLE_CRYPTOAPI */ static void tls_ctx_add_extra_certs (struct tls_root_ctx *ctx, BIO *bio) @@ -645,7 +725,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, const char *priv_key_file_inline ) { - int status; SSL_CTX *ssl_ctx = NULL; BIO *in = NULL; EVP_PKEY *pkey = NULL; @@ -678,7 +757,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, crypto_msg (M_WARN, "Cannot load private key file %s", priv_key_file); goto end; } - warn_if_group_others_accessible (priv_key_file); /* Check Private Key */ if (!SSL_CTX_check_private_key (ssl_ctx)) @@ -693,6 +771,64 @@ end: return ret; } +void +tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, + const char *crl_inline) +{ + X509_CRL *crl = NULL; + BIO *in = NULL; + + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx); + if (!store) + crypto_msg (M_FATAL, "Cannot get certificate store"); + + /* Always start with a cleared CRL list, for that we + * we need to manually find the CRL object from the stack + * and remove it */ + for (int i = 0; i < sk_X509_OBJECT_num(store->objs); i++) + { + X509_OBJECT* obj = sk_X509_OBJECT_value(store->objs, i); + ASSERT(obj); + if (obj->type == X509_LU_CRL) + { + sk_X509_OBJECT_delete(store->objs, i); + X509_OBJECT_free_contents(obj); + OPENSSL_free(obj); + } + } + + X509_STORE_set_flags (store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + + if (!strcmp (crl_file, INLINE_FILE_TAG) && crl_inline) + in = BIO_new_mem_buf ((char *)crl_inline, -1); + else + in = BIO_new_file (crl_file, "r"); + + if (in == NULL) + { + msg (M_WARN, "CRL: cannot read: %s", crl_file); + goto end; + } + + crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); + if (crl == NULL) + { + msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file); + goto end; + } + + if (!X509_STORE_add_crl(store, crl)) + { + msg (M_WARN, "CRL: cannot add %s to store", crl_file); + goto end; + } + +end: + X509_CRL_free(crl); + BIO_free(in); +} + + #ifdef MANAGMENT_EXTERNAL_KEY /* encrypt */ @@ -780,10 +916,11 @@ tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, X509 *cert = NULL; ASSERT (NULL != ctx); - ASSERT (NULL != cert); tls_ctx_load_cert_file_and_copy (ctx, cert_file, cert_file_inline, &cert); + ASSERT (NULL != cert); + /* allocate custom RSA method object */ ALLOC_OBJ_CLEAR (rsa_meth, RSA_METHOD); rsa_meth->name = "OpenVPN external private key RSA Method"; @@ -965,11 +1102,7 @@ tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, msg(M_WARN, "WARNING: experimental option --capath %s", ca_path); else crypto_msg (M_FATAL, "Cannot add lookup at --capath %s", ca_path); -#if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE_set_flags (store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); -#else - msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath"); -#endif } } @@ -1385,7 +1518,6 @@ show_available_tls_ciphers (const char *cipher_list) struct tls_root_ctx tls_ctx; SSL *ssl; const char *cipher_name; - const char *print_name; const tls_cipher_name_pair *pair; int priority = 0; @@ -1419,6 +1551,43 @@ show_available_tls_ciphers (const char *cipher_list) SSL_CTX_free (tls_ctx.ctx); } +/* + * Show the Elliptic curves that are available for us to use + * in the OpenSSL library. + */ +void +show_available_curves() +{ +#ifndef OPENSSL_NO_EC + EC_builtin_curve *curves = NULL; + size_t crv_len = 0; + size_t n = 0; + + crv_len = EC_get_builtin_curves(NULL, 0); + ALLOC_ARRAY(curves, EC_builtin_curve, crv_len); + if (EC_get_builtin_curves(curves, crv_len)) + { + printf ("Available Elliptic curves:\n"); + for (n = 0; n < crv_len; n++) + { + const char *sname; + sname = OBJ_nid2sn(curves[n].nid); + if (sname == NULL) sname = ""; + + printf("%s\n", sname); + } + } + else + { + crypto_msg (M_FATAL, "Cannot get list of builtin curves"); + } + free(curves); +#else + msg (M_WARN, "Your OpenSSL library was built without elliptic curve support. " + "No curves available."); +#endif +} + void get_highest_preference_tls_cipher (char *buf, int size) { @@ -1446,4 +1615,4 @@ get_ssl_library_version(void) return SSLeay_version(SSLEAY_VERSION); } -#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) */ +#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) */ diff --git a/src/openvpn/ssl_openssl.h b/src/openvpn/ssl_openssl.h index 73a6c49..97dc742 100644 --- a/src/openvpn/ssl_openssl.h +++ b/src/openvpn/ssl_openssl.h @@ -35,15 +35,14 @@ /** * SSL_OP_NO_TICKET tells OpenSSL to disable "stateless session resumption", * as this is something we do not want nor need, but could potentially be - * used for a future attack. For compatibility reasons, in the 2.3.x - * series, we keep building if the OpenSSL version is too old to support - * this. 2.4 requires it and will fail configure if not present. + * used for a future attack. For compatibility reasons we keep building if the + * OpenSSL version is too old (pre-0.9.8f) to support stateless session + * resumption (and the accompanying SSL_OP_NO_TICKET flag). */ #ifndef SSL_OP_NO_TICKET # define SSL_OP_NO_TICKET 0 #endif - /** * Structure that wraps the TLS context. Contents differ depending on the * SSL library used. diff --git a/src/openvpn/ssl_polarssl.c b/src/openvpn/ssl_polarssl.c deleted file mode 100644 index 1f58369..0000000 --- a/src/openvpn/ssl_polarssl.c +++ /dev/null @@ -1,1160 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * Copyright (C) 2006-2010, Brainspark B.V. - * - * 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 - */ - -/** - * @file Control Channel PolarSSL Backend - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) - -#include "errlevel.h" -#include "ssl_backend.h" -#include "base64.h" -#include "buffer.h" -#include "misc.h" -#include "manage.h" -#include "ssl_common.h" - -#include - -#include "ssl_verify_polarssl.h" -#include -#include -#include -#include -#include -#include - -void -tls_init_lib() -{ -} - -void -tls_free_lib() -{ -} - -void -tls_clear_error() -{ -} - -void -tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) -{ - ASSERT(NULL != ctx); - CLEAR(*ctx); - - ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context); - - ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_crt); - - ctx->endpoint = SSL_IS_SERVER; - ctx->initialised = true; -} - -void -tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags) -{ - ASSERT(NULL != ctx); - CLEAR(*ctx); - - ALLOC_OBJ_CLEAR(ctx->dhm_ctx, dhm_context); - ALLOC_OBJ_CLEAR(ctx->ca_chain, x509_crt); - - ctx->endpoint = SSL_IS_CLIENT; - ctx->initialised = true; -} - -void -tls_ctx_free(struct tls_root_ctx *ctx) -{ - if (ctx) - { - pk_free(ctx->priv_key); - if (ctx->priv_key) - free(ctx->priv_key); - - x509_crt_free(ctx->ca_chain); - if (ctx->ca_chain) - free(ctx->ca_chain); - - x509_crt_free(ctx->crt_chain); - if (ctx->crt_chain) - free(ctx->crt_chain); - - dhm_free(ctx->dhm_ctx); - if (ctx->dhm_ctx) - free(ctx->dhm_ctx); - -#if defined(ENABLE_PKCS11) - if (ctx->priv_key_pkcs11 != NULL) { - pkcs11_priv_key_free(ctx->priv_key_pkcs11); - free(ctx->priv_key_pkcs11); - } -#endif -#if defined(MANAGMENT_EXTERNAL_KEY) - if (ctx->external_key != NULL) - free(ctx->external_key); -#endif - - if (ctx->allowed_ciphers) - free(ctx->allowed_ciphers); - - CLEAR(*ctx); - - ctx->initialised = false; - - } -} - -bool -tls_ctx_initialised(struct tls_root_ctx *ctx) -{ - ASSERT(NULL != ctx); - return ctx->initialised; -} - -void -tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) -{ -} - -static const char * -tls_translate_cipher_name (const char * cipher_name) { - const tls_cipher_name_pair * pair = tls_get_cipher_name_pair(cipher_name, strlen(cipher_name)); - - if (NULL == pair) - { - // No translation found, return original - return cipher_name; - } - - if (0 != strcmp(cipher_name, pair->iana_name)) - { - // Deprecated name found, notify user - msg(M_WARN, "Deprecated cipher suite name '%s', please use IANA name '%s'", pair->openssl_name, pair->iana_name); - } - - return pair->iana_name; -} - -void -tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) -{ - char *tmp_ciphers, *tmp_ciphers_orig, *token; - int i, cipher_count; - int ciphers_len = strlen (ciphers); - - ASSERT (NULL != ctx); - ASSERT (0 != ciphers_len); - - /* Get number of ciphers */ - for (i = 0, cipher_count = 1; i < ciphers_len; i++) - if (ciphers[i] == ':') - cipher_count++; - - /* Allocate an array for them */ - ALLOC_ARRAY_CLEAR(ctx->allowed_ciphers, int, cipher_count+1) - - /* Parse allowed ciphers, getting IDs */ - i = 0; - tmp_ciphers_orig = tmp_ciphers = string_alloc (ciphers, NULL); - - token = strtok (tmp_ciphers, ":"); - while(token) - { - ctx->allowed_ciphers[i] = ssl_get_ciphersuite_id ( - tls_translate_cipher_name (token)); - if (0 != ctx->allowed_ciphers[i]) - i++; - token = strtok (NULL, ":"); - } - free(tmp_ciphers_orig); -} - -void -tls_ctx_check_cert_time (const struct tls_root_ctx *ctx) -{ - ASSERT (ctx); - if (ctx->crt_chain == NULL) - { - return; /* Nothing to check if there is no certificate */ - } - - if (x509_time_future (&ctx->crt_chain->valid_from)) - { - msg (M_WARN, "WARNING: Your certificate is not yet valid!"); - } - - if (x509_time_expired (&ctx->crt_chain->valid_to)) - { - msg (M_WARN, "WARNING: Your certificate has expired!"); - } -} - -void -tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file, - const char *dh_inline - ) -{ - if (!strcmp (dh_file, INLINE_FILE_TAG) && dh_inline) - { - if (!polar_ok(dhm_parse_dhm(ctx->dhm_ctx, - (const unsigned char *) dh_inline, strlen(dh_inline)))) - msg (M_FATAL, "Cannot read inline DH parameters"); - } -else - { - if (!polar_ok(dhm_parse_dhmfile(ctx->dhm_ctx, dh_file))) - msg (M_FATAL, "Cannot read DH parameters from file %s", dh_file); - } - - msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with " counter_format " bit key", - (counter_type) 8 * mpi_size(&ctx->dhm_ctx->P)); -} - -int -tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, - const char *pkcs12_file_inline, - bool load_ca_file - ) -{ - msg(M_FATAL, "PKCS #12 files not yet supported for PolarSSL."); - return 0; -} - -#ifdef ENABLE_CRYPTOAPI -void -tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) -{ - msg(M_FATAL, "Windows CryptoAPI not yet supported for PolarSSL."); -} -#endif /* WIN32 */ - -void -tls_ctx_load_cert_file (struct tls_root_ctx *ctx, const char *cert_file, - const char *cert_inline - ) -{ - ASSERT(NULL != ctx); - - if (!ctx->crt_chain) - { - ALLOC_OBJ_CLEAR(ctx->crt_chain, x509_crt); - } - - if (!strcmp (cert_file, INLINE_FILE_TAG) && cert_inline) - { - if (!polar_ok(x509_crt_parse(ctx->crt_chain, - (const unsigned char *) cert_inline, strlen(cert_inline)))) - msg (M_FATAL, "Cannot load inline certificate file"); - } - else - { - if (!polar_ok(x509_crt_parse_file(ctx->crt_chain, cert_file))) - { - msg (M_FATAL, "Cannot load certificate file %s", cert_file); - } - } -} - -int -tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, - const char *priv_key_inline - ) -{ - int status; - ASSERT(NULL != ctx); - - if (!ctx->priv_key) - { - ALLOC_OBJ_CLEAR(ctx->priv_key, pk_context); - } - - if (!strcmp (priv_key_file, INLINE_FILE_TAG) && priv_key_inline) - { - status = pk_parse_key(ctx->priv_key, - (const unsigned char *) priv_key_inline, strlen(priv_key_inline), - NULL, 0); - - if (POLARSSL_ERR_PK_PASSWORD_REQUIRED == status) - { - char passbuf[512] = {0}; - pem_password_callback(passbuf, 512, 0, NULL); - status = pk_parse_key(ctx->priv_key, - (const unsigned char *) priv_key_inline, strlen(priv_key_inline), - (unsigned char *) passbuf, strlen(passbuf)); - } - } - else - { - status = pk_parse_keyfile(ctx->priv_key, priv_key_file, NULL); - if (POLARSSL_ERR_PK_PASSWORD_REQUIRED == status) - { - char passbuf[512] = {0}; - pem_password_callback(passbuf, 512, 0, NULL); - status = pk_parse_keyfile(ctx->priv_key, priv_key_file, passbuf); - } - } - if (!polar_ok(status)) - { -#ifdef ENABLE_MANAGEMENT - if (management && (POLARSSL_ERR_PK_PASSWORD_MISMATCH == status)) - management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL); -#endif - msg (M_WARN, "Cannot load private key file %s", priv_key_file); - return 1; - } - - warn_if_group_others_accessible (priv_key_file); - - /* TODO: Check Private Key */ -#if 0 - if (!SSL_CTX_check_private_key (ctx)) - msg (M_SSLERR, "Private key does not match the certificate"); -#endif - return 0; -} - -#ifdef MANAGMENT_EXTERNAL_KEY - - -struct external_context { - size_t signature_length; -}; - -/** - * external_pkcs1_sign implements a PolarSSL rsa_sign_func callback, that uses - * the management interface to request an RSA signature for the supplied hash. - * - * @param ctx_voidptr Management external key context. - * @param f_rng (Unused) - * @param p_rng (Unused) - * @param mode RSA mode (should be RSA_PRIVATE). - * @param md_alg Message digest ('hash') algorithm type. - * @param hashlen Length of hash (overridden by length specified by md_alg - * if md_alg != POLARSSL_MD_NONE). - * @param hash The digest ('hash') to sign. Should have a size - * matching the length of md_alg (if != POLARSSL_MD_NONE), - * or hashlen otherwise. - * @param sig Buffer that returns the signature. Should be at least of - * size ctx->signature_length. - * - * @return 0 on success, non-zero polarssl error code on failure. - */ -static inline int external_pkcs1_sign( void *ctx_voidptr, - int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int mode, - md_type_t md_alg, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ) -{ - struct external_context * const ctx = ctx_voidptr; - char *in_b64 = NULL; - char *out_b64 = NULL; - int rv; - unsigned char *p = sig; - size_t asn_len = 0, oid_size = 0, sig_len = 0; - const char *oid = NULL; - - if( NULL == ctx ) - return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - - if( RSA_PRIVATE != mode ) - return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - - /* - * Support a wide range of hashes. TLSv1.1 and before only need SIG_RSA_RAW, - * but TLSv1.2 needs the full suite of hashes. - * - * This code has been taken from PolarSSL pkcs11_sign(), under the GPLv2.0+. - */ - if( md_alg != POLARSSL_MD_NONE ) - { - const md_info_t *md_info = md_info_from_type( md_alg ); - if( md_info == NULL ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - if (!polar_ok(oid_get_oid_by_md( md_alg, &oid, &oid_size ))) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - hashlen = md_get_size( md_info ); - asn_len = 10 + oid_size; - } - - sig_len = ctx->signature_length; - if ( (SIZE_MAX - hashlen) < asn_len || (hashlen + asn_len) > sig_len ) - return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - - if( md_alg != POLARSSL_MD_NONE ) - { - /* - * DigestInfo ::= SEQUENCE { - * digestAlgorithm DigestAlgorithmIdentifier, - * digest Digest } - * - * DigestAlgorithmIdentifier ::= AlgorithmIdentifier - * - * Digest ::= OCTET STRING - */ - *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; - *p++ = (unsigned char) ( 0x08 + oid_size + hashlen ); - *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; - *p++ = (unsigned char) ( 0x04 + oid_size ); - *p++ = ASN1_OID; - *p++ = oid_size & 0xFF; - memcpy( p, oid, oid_size ); - p += oid_size; - *p++ = ASN1_NULL; - *p++ = 0x00; - *p++ = ASN1_OCTET_STRING; - *p++ = hashlen; - - /* Determine added ASN length */ - asn_len = p - sig; - } - - /* Copy the hash to be signed */ - memcpy( p, hash, hashlen ); - - /* convert 'from' to base64 */ - if (openvpn_base64_encode (sig, asn_len + hashlen, &in_b64) <= 0) - { - rv = POLARSSL_ERR_RSA_BAD_INPUT_DATA; - goto done; - } - - /* call MI for signature */ - if (management) - out_b64 = management_query_rsa_sig (management, in_b64); - if (!out_b64) - { - rv = POLARSSL_ERR_RSA_PRIVATE_FAILED; - goto done; - } - - /* decode base64 signature to binary and verify length */ - if ( openvpn_base64_decode (out_b64, sig, ctx->signature_length) != - ctx->signature_length ) - { - rv = POLARSSL_ERR_RSA_PRIVATE_FAILED; - goto done; - } - - rv = 0; - -done: - if (in_b64) - free (in_b64); - if (out_b64) - free (out_b64); - return rv; -} - -static inline size_t external_key_len(void *vctx) -{ - struct external_context * const ctx = vctx; - - return ctx->signature_length; -} - -int -tls_ctx_use_external_private_key (struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline) -{ - ASSERT(NULL != ctx); - - tls_ctx_load_cert_file(ctx, cert_file, cert_file_inline); - - if (ctx->crt_chain == NULL) - return 0; - - ALLOC_OBJ_CLEAR (ctx->external_key, struct external_context); - ctx->external_key->signature_length = pk_get_len(&ctx->crt_chain->pk); - - ALLOC_OBJ_CLEAR (ctx->priv_key, pk_context); - if (!polar_ok (pk_init_ctx_rsa_alt(ctx->priv_key, ctx->external_key, - NULL, external_pkcs1_sign, external_key_len))) - return 0; - - return 1; -} -#endif - -void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, - const char *ca_inline, const char *ca_path, bool tls_server) -{ - if (ca_path) - msg(M_FATAL, "ERROR: PolarSSL cannot handle the capath directive"); - - if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_inline) - { - if (!polar_ok(x509_crt_parse(ctx->ca_chain, - (const unsigned char *) ca_inline, strlen(ca_inline)))) - msg (M_FATAL, "Cannot load inline CA certificates"); - } - else - { - /* Load CA file for verifying peer supplied certificate */ - if (!polar_ok(x509_crt_parse_file(ctx->ca_chain, ca_file))) - msg (M_FATAL, "Cannot load CA certificate file %s", ca_file); - } -} - -void -tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file, - const char *extra_certs_inline - ) -{ - ASSERT(NULL != ctx); - - if (!ctx->crt_chain) - { - ALLOC_OBJ_CLEAR (ctx->crt_chain, x509_crt); - } - - if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_inline) - { - if (!polar_ok(x509_crt_parse(ctx->crt_chain, - (const unsigned char *) extra_certs_inline, - strlen(extra_certs_inline)))) - msg (M_FATAL, "Cannot load inline extra-certs file"); - } - else - { - if (!polar_ok(x509_crt_parse_file(ctx->crt_chain, extra_certs_file))) - msg (M_FATAL, "Cannot load extra-certs file: %s", extra_certs_file); - } -} - -/* ************************************** - * - * Key-state specific functions - * - ***************************************/ - -/* - * "Endless buffer" - */ - -static inline void buf_free_entry(buffer_entry *entry) -{ - if (NULL != entry) - { - free(entry->data); - free(entry); - } -} - -static void buf_free_entries(endless_buffer *buf) -{ - while(buf->first_block) - { - buffer_entry *cur_block = buf->first_block; - buf->first_block = cur_block->next_block; - buf_free_entry(cur_block); - } - buf->last_block = NULL; -} - -static int endless_buf_read( void * ctx, unsigned char * out, size_t out_len ) -{ - endless_buffer *in = (endless_buffer *) ctx; - size_t read_len = 0; - - if (in->first_block == NULL) - return POLARSSL_ERR_NET_WANT_READ; - - while (in->first_block != NULL && read_len < out_len) - { - int block_len = in->first_block->length - in->data_start; - if (block_len <= out_len - read_len) - { - buffer_entry *cur_entry = in->first_block; - memcpy(out + read_len, cur_entry->data + in->data_start, - block_len); - - read_len += block_len; - - in->first_block = cur_entry->next_block; - in->data_start = 0; - - if (in->first_block == NULL) - in->last_block = NULL; - - buf_free_entry(cur_entry); - } - else - { - memcpy(out + read_len, in->first_block->data + in->data_start, - out_len - read_len); - in->data_start += out_len - read_len; - read_len = out_len; - } - } - - return read_len; -} - -static int endless_buf_write( void *ctx, const unsigned char *in, size_t len ) -{ - endless_buffer *out = (endless_buffer *) ctx; - buffer_entry *new_block = malloc(sizeof(buffer_entry)); - if (NULL == new_block) - return POLARSSL_ERR_NET_SEND_FAILED; - - new_block->data = malloc(len); - if (NULL == new_block->data) - { - free(new_block); - return POLARSSL_ERR_NET_SEND_FAILED; - } - - new_block->length = len; - new_block->next_block = NULL; - - memcpy(new_block->data, in, len); - - if (NULL == out->first_block) - out->first_block = new_block; - - if (NULL != out->last_block) - out->last_block->next_block = new_block; - - out->last_block = new_block; - - return len; -} - -static void my_debug( void *ctx, int level, const char *str ) -{ - int my_loglevel = (level < 3) ? D_TLS_DEBUG_MED : D_TLS_DEBUG; - msg (my_loglevel, "PolarSSL msg: %s", str); -} - -/* - * Further personalise the RNG using a hash of the public key - */ -void tls_ctx_personalise_random(struct tls_root_ctx *ctx) -{ - static char old_sha256_hash[32] = {0}; - unsigned char sha256_hash[32] = {0}; - ctr_drbg_context *cd_ctx = rand_ctx_get(); - - if (NULL != ctx->crt_chain) - { - x509_crt *cert = ctx->crt_chain; - - sha256(cert->tbs.p, cert->tbs.len, sha256_hash, false); - if ( 0 != memcmp(old_sha256_hash, sha256_hash, sizeof(sha256_hash))) - { - ctr_drbg_update(cd_ctx, sha256_hash, 32); - memcpy(old_sha256_hash, sha256_hash, sizeof(old_sha256_hash)); - } - } -} - -int -tls_version_max(void) -{ -#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_3) - return TLS_VER_1_2; -#elif defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_2) - return TLS_VER_1_1; -#else - return TLS_VER_1_0; -#endif -} - -/** - * Convert an OpenVPN tls-version variable to PolarSSl format (i.e. a major and - * minor ssl version number). - * - * @param tls_ver The tls-version variable to convert. - * @param major Returns the TLS major version in polarssl format. - * Must be a valid pointer. - * @param minor Returns the TLS minor version in polarssl format. - * Must be a valid pointer. - */ -static void tls_version_to_major_minor(int tls_ver, int *major, int *minor) { - ASSERT(major); - ASSERT(minor); - - switch (tls_ver) - { - case TLS_VER_1_0: - *major = SSL_MAJOR_VERSION_3; - *minor = SSL_MINOR_VERSION_1; - break; - case TLS_VER_1_1: - *major = SSL_MAJOR_VERSION_3; - *minor = SSL_MINOR_VERSION_2; - break; - case TLS_VER_1_2: - *major = SSL_MAJOR_VERSION_3; - *minor = SSL_MINOR_VERSION_3; - break; - default: - msg(M_FATAL, "%s: invalid TLS version %d", __func__, tls_ver); - break; - } -} - -void key_state_ssl_init(struct key_state_ssl *ks_ssl, - const struct tls_root_ctx *ssl_ctx, bool is_server, struct tls_session *session) -{ - ASSERT(NULL != ssl_ctx); - ASSERT(ks_ssl); - CLEAR(*ks_ssl); - - ALLOC_OBJ_CLEAR(ks_ssl->ctx, ssl_context); - if (polar_ok(ssl_init(ks_ssl->ctx))) - { - /* Initialise SSL context */ - debug_set_threshold(3); - ssl_set_dbg (ks_ssl->ctx, my_debug, NULL); - ssl_set_endpoint (ks_ssl->ctx, ssl_ctx->endpoint); - - ssl_set_rng (ks_ssl->ctx, ctr_drbg_random, rand_ctx_get()); - - if (ssl_ctx->allowed_ciphers) - ssl_set_ciphersuites (ks_ssl->ctx, ssl_ctx->allowed_ciphers); - - /* Disable record splitting (for now). OpenVPN assumes records are sent - * unfragmented, and changing that will require thorough review and - * testing. Since OpenVPN is not susceptible to BEAST, we can just - * disable record splitting as a quick fix. */ -#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING) - ssl_set_cbc_record_splitting (ks_ssl->ctx, SSL_CBC_RECORD_SPLITTING_DISABLED); -#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */ - - /* Initialise authentication information */ - if (is_server) - polar_ok (ssl_set_dh_param_ctx (ks_ssl->ctx, ssl_ctx->dhm_ctx)); - - polar_ok (ssl_set_own_cert (ks_ssl->ctx, ssl_ctx->crt_chain, - ssl_ctx->priv_key)); - - /* Initialise SSL verification */ -#if P2MP_SERVER - if (session->opt->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) - { - msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION " - "--client-cert-not-required may accept clients which do not present " - "a certificate"); - } - else -#endif - { - ssl_set_authmode (ks_ssl->ctx, SSL_VERIFY_REQUIRED); - ssl_set_verify (ks_ssl->ctx, verify_callback, session); - } - - /* TODO: PolarSSL does not currently support sending the CA chain to the client */ - ssl_set_ca_chain (ks_ssl->ctx, ssl_ctx->ca_chain, NULL, NULL ); - - /* Initialize minimum TLS version */ - { - const int tls_version_min = - (session->opt->ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & - SSLF_TLS_VERSION_MIN_MASK; - - /* default to TLS 1.0 */ - int major = SSL_MAJOR_VERSION_3; - int minor = SSL_MINOR_VERSION_1; - - if (tls_version_min > TLS_VER_UNSPEC) - tls_version_to_major_minor(tls_version_min, &major, &minor); - - ssl_set_min_version(ks_ssl->ctx, major, minor); - } - - /* Initialize maximum TLS version */ - { - const int tls_version_max = - (session->opt->ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & - SSLF_TLS_VERSION_MAX_MASK; - - if (tls_version_max > TLS_VER_UNSPEC) - { - int major, minor; - tls_version_to_major_minor(tls_version_max, &major, &minor); - ssl_set_max_version(ks_ssl->ctx, major, minor); - } - } - - /* Initialise BIOs */ - ALLOC_OBJ_CLEAR (ks_ssl->ct_in, endless_buffer); - ALLOC_OBJ_CLEAR (ks_ssl->ct_out, endless_buffer); - ssl_set_bio (ks_ssl->ctx, endless_buf_read, ks_ssl->ct_in, - endless_buf_write, ks_ssl->ct_out); - } -} - -void -key_state_ssl_free(struct key_state_ssl *ks_ssl) -{ - if (ks_ssl) { - if (ks_ssl->ctx) - { - ssl_free(ks_ssl->ctx); - free(ks_ssl->ctx); - } - if (ks_ssl->ct_in) { - buf_free_entries(ks_ssl->ct_in); - free(ks_ssl->ct_in); - } - if (ks_ssl->ct_out) { - buf_free_entries(ks_ssl->ct_out); - free(ks_ssl->ct_out); - } - CLEAR(*ks_ssl); - } -} - -int -key_state_write_plaintext (struct key_state_ssl *ks, struct buffer *buf) -{ - int retval = 0; - perf_push (PERF_BIO_WRITE_PLAINTEXT); - - ASSERT (NULL != ks); - ASSERT (buf); - ASSERT (buf->len >= 0); - - if (0 == buf->len) - { - perf_pop (); - return 0; - } - - retval = ssl_write(ks->ctx, BPTR(buf), buf->len); - - if (retval < 0) - { - perf_pop (); - if (POLARSSL_ERR_NET_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == retval) - return 0; - msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_plaintext error"); - return -1; - } - - if (retval != buf->len) - { - msg (D_TLS_ERRORS, - "TLS ERROR: write tls_write_plaintext incomplete %d/%d", - retval, buf->len); - perf_pop (); - return -1; - } - - /* successful write */ - dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_plaintext %d bytes", retval); - - memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */ - buf->len = 0; - - perf_pop (); - return 1; -} - -int -key_state_write_plaintext_const (struct key_state_ssl *ks, const uint8_t *data, int len) -{ - int retval = 0; - perf_push (PERF_BIO_WRITE_PLAINTEXT); - - ASSERT (NULL != ks); - ASSERT (len >= 0); - - if (0 == len) - { - perf_pop (); - return 0; - } - - ASSERT (data); - - retval = ssl_write(ks->ctx, data, len); - - if (retval < 0) - { - perf_pop (); - if (POLARSSL_ERR_NET_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == retval) - return 0; - polar_log_err (D_TLS_ERRORS, retval, - "TLS ERROR: write tls_write_plaintext_const error"); - return -1; - } - - if (retval != len) - { - msg (D_TLS_ERRORS, - "TLS ERROR: write tls_write_plaintext_const incomplete %d/%d", - retval, len); - perf_pop (); - return -1; - } - - /* successful write */ - dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_plaintext_const %d bytes", retval); - - perf_pop (); - return 1; -} - -int -key_state_read_ciphertext (struct key_state_ssl *ks, struct buffer *buf, - int maxlen) -{ - int retval = 0; - int len = 0; - - perf_push (PERF_BIO_READ_CIPHERTEXT); - - ASSERT (NULL != ks); - ASSERT (buf); - ASSERT (buf->len >= 0); - - if (buf->len) - { - perf_pop (); - return 0; - } - - len = buf_forward_capacity (buf); - if (maxlen < len) - len = maxlen; - - retval = endless_buf_read(ks->ct_out, BPTR(buf), len); - - /* Error during read, check for retry error */ - if (retval < 0) - { - perf_pop (); - if (POLARSSL_ERR_NET_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == retval) - return 0; - polar_log_err (D_TLS_ERRORS, retval, "TLS_ERROR: read tls_read_ciphertext error"); - buf->len = 0; - return -1; - } - /* Nothing read, try again */ - if (0 == retval) - { - buf->len = 0; - perf_pop (); - return 0; - } - - /* successful read */ - dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_ciphertext %d bytes", retval); - buf->len = retval; - perf_pop (); - return 1; -} - -int -key_state_write_ciphertext (struct key_state_ssl *ks, struct buffer *buf) -{ - int retval = 0; - perf_push (PERF_BIO_WRITE_CIPHERTEXT); - - ASSERT (NULL != ks); - ASSERT (buf); - ASSERT (buf->len >= 0); - - if (0 == buf->len) - { - perf_pop (); - return 0; - } - - retval = endless_buf_write(ks->ct_in, BPTR(buf), buf->len); - - if (retval < 0) - { - perf_pop (); - - if (POLARSSL_ERR_NET_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == retval) - return 0; - polar_log_err (D_TLS_ERRORS, retval, - "TLS ERROR: write tls_write_ciphertext error"); - return -1; - } - - if (retval != buf->len) - { - msg (D_TLS_ERRORS, "TLS ERROR: write tls_write_ciphertext incomplete %d/%d", - retval, buf->len); - perf_pop (); - return -1; - } - - /* successful write */ - dmsg (D_HANDSHAKE_VERBOSE, "write tls_write_ciphertext %d bytes", retval); - - memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */ - buf->len = 0; - - perf_pop (); - return 1; -} - -int -key_state_read_plaintext (struct key_state_ssl *ks, struct buffer *buf, - int maxlen) -{ - int retval = 0; - int len = 0; - - perf_push (PERF_BIO_READ_PLAINTEXT); - - ASSERT (NULL != ks); - ASSERT (buf); - ASSERT (buf->len >= 0); - - if (buf->len) - { - perf_pop (); - return 0; - } - - len = buf_forward_capacity (buf); - if (maxlen < len) - len = maxlen; - - retval = ssl_read(ks->ctx, BPTR(buf), len); - - /* Error during read, check for retry error */ - if (retval < 0) - { - if (POLARSSL_ERR_NET_WANT_WRITE == retval || POLARSSL_ERR_NET_WANT_READ == retval) - return 0; - polar_log_err (D_TLS_ERRORS, retval, "TLS_ERROR: read tls_read_plaintext error"); - buf->len = 0; - perf_pop (); - return -1; - } - /* Nothing read, try again */ - if (0 == retval) - { - buf->len = 0; - perf_pop (); - return 0; - } - - /* successful read */ - dmsg (D_HANDSHAKE_VERBOSE, "read tls_read_plaintext %d bytes", retval); - buf->len = retval; - - perf_pop (); - return 1; -} - -/* ************************************** - * - * Information functions - * - * Print information for the end user. - * - ***************************************/ -void -print_details (struct key_state_ssl * ks_ssl, const char *prefix) -{ - const x509_crt *cert; - char s1[256]; - char s2[256]; - - s1[0] = s2[0] = 0; - openvpn_snprintf (s1, sizeof (s1), "%s %s, cipher %s", - prefix, - ssl_get_version (ks_ssl->ctx), - ssl_get_ciphersuite(ks_ssl->ctx)); - - cert = ssl_get_peer_cert(ks_ssl->ctx); - if (cert != NULL) - { - openvpn_snprintf (s2, sizeof (s2), ", %zu bit key", pk_get_size(&cert->pk)); - } - - msg (D_HANDSHAKE, "%s%s", s1, s2); -} - -void -show_available_tls_ciphers (const char *cipher_list) -{ - struct tls_root_ctx tls_ctx; - const int *ciphers = ssl_list_ciphersuites(); - - if (cipher_list) { - tls_ctx_restrict_ciphers(&tls_ctx, cipher_list); - ciphers = tls_ctx.allowed_ciphers; - } - -#ifndef ENABLE_SMALL - printf ("Available TLS Ciphers,\n"); - printf ("listed in order of preference:\n\n"); -#endif - - while (*ciphers != 0) - { - printf ("%s\n", ssl_get_ciphersuite_name(*ciphers)); - ciphers++; - } - printf ("\n" SHOW_TLS_CIPHER_LIST_WARNING); -} - -void -get_highest_preference_tls_cipher (char *buf, int size) -{ - const char *cipher_name; - const int *ciphers = ssl_list_ciphersuites(); - if (*ciphers == 0) - msg (M_FATAL, "Cannot retrieve list of supported SSL ciphers."); - - cipher_name = ssl_get_ciphersuite_name(*ciphers); - strncpynt (buf, cipher_name, size); -} - -const char * -get_ssl_library_version(void) -{ - static char polar_version[30]; - unsigned int pv = version_get_number(); - sprintf( polar_version, "PolarSSL %d.%d.%d", - (pv>>24)&0xff, (pv>>16)&0xff, (pv>>8)&0xff ); - return polar_version; -} - -#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) */ diff --git a/src/openvpn/ssl_polarssl.h b/src/openvpn/ssl_polarssl.h deleted file mode 100644 index b80a509..0000000 --- a/src/openvpn/ssl_polarssl.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file Control Channel PolarSSL Backend - */ - -#ifndef SSL_POLARSSL_H_ -#define SSL_POLARSSL_H_ - -#include "syshead.h" - -#include -#include - -#if defined(ENABLE_PKCS11) -#include -#endif - -typedef struct _buffer_entry buffer_entry; - -struct _buffer_entry { - size_t length; - uint8_t *data; - buffer_entry *next_block; -}; - -typedef struct { - size_t data_start; - buffer_entry *first_block; - buffer_entry *last_block; -} endless_buffer; - -/** - * Structure that wraps the TLS context. Contents differ depending on the - * SSL library used. - * - * Either \c priv_key_pkcs11 or \c priv_key must be filled in. - */ -struct tls_root_ctx { - bool initialised; /**< True if the context has been initialised */ - - int endpoint; /**< Whether or not this is a server or a client */ - - dhm_context *dhm_ctx; /**< Diffie-Helmann-Merkle context */ - x509_crt *crt_chain; /**< Local Certificate chain */ - x509_crt *ca_chain; /**< CA chain for remote verification */ - pk_context *priv_key; /**< Local private key */ -#if defined(ENABLE_PKCS11) - pkcs11_context *priv_key_pkcs11; /**< PKCS11 private key */ -#endif -#ifdef MANAGMENT_EXTERNAL_KEY - struct external_context *external_key; /**< Management external key */ -#endif - int * allowed_ciphers; /**< List of allowed ciphers for this connection */ -}; - -struct key_state_ssl { - ssl_context *ctx; - endless_buffer *ct_in; - endless_buffer *ct_out; -}; - - -#endif /* SSL_POLARSSL_H_ */ diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c index ca69b41..a099776 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -35,10 +35,12 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#ifdef ENABLE_CRYPTO #include "misc.h" #include "manage.h" +#include "otime.h" +#include "base64.h" #include "ssl_verify.h" #include "ssl_verify_backend.h" @@ -191,40 +193,25 @@ tls_username (const struct tls_multi *multi, const bool null) } void -cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash) +cert_hash_remember (struct tls_session *session, const int error_depth, + const struct buffer *cert_hash) { if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH) { if (!session->cert_hash_set) - ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set); + { + ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set); + } if (!session->cert_hash_set->ch[error_depth]) - ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash); - { - struct cert_hash *ch = session->cert_hash_set->ch[error_depth]; - memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH); - } - } -} - -#if 0 -static void -cert_hash_print (const struct cert_hash_set *chs, int msglevel) -{ - struct gc_arena gc = gc_new (); - msg (msglevel, "CERT_HASH"); - if (chs) - { - int i; - for (i = 0; i < MAX_CERT_DEPTH; ++i) { - const struct cert_hash *ch = chs->ch[i]; - if (ch) - msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc)); + ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash); } + + struct cert_hash *ch = session->cert_hash_set->ch[error_depth]; + ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash)); + memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash)); } - gc_free (&gc); } -#endif void cert_hash_free (struct cert_hash_set *chs) @@ -238,7 +225,7 @@ cert_hash_free (struct cert_hash_set *chs) } } -static bool +bool cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2) { if (chs1 && chs2) @@ -251,7 +238,8 @@ cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set if (!ch1 && !ch2) continue; - else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH)) + else if (ch1 && ch2 && !memcmp (ch1->sha256_hash, ch2->sha256_hash, + sizeof(ch1->sha256_hash))) continue; else return false; @@ -278,7 +266,8 @@ cert_hash_copy (const struct cert_hash_set *chs) if (ch) { ALLOC_OBJ (dest->ch[i], struct cert_hash); - memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH); + memcpy (dest->ch[i]->sha256_hash, ch->sha256_hash, + sizeof(dest->ch[i]->sha256_hash)); } } } @@ -337,8 +326,6 @@ verify_peer_cert(const struct tls_options *opt, openvpn_x509_cert_t *peer_cert, } } -#if OPENSSL_VERSION_NUMBER >= 0x00907000L || ENABLE_CRYPTO_POLARSSL - /* verify certificate ku */ if (opt->remote_cert_ku[0] != 0) { @@ -367,8 +354,6 @@ verify_peer_cert(const struct tls_options *opt, openvpn_x509_cert_t *peer_cert, } } -#endif /* OPENSSL_VERSION_NUMBER */ - /* verify X509 name or username against --verify-x509-[user]name */ if (opt->verify_x509_type != VERIFY_X509_NONE) { @@ -397,22 +382,17 @@ verify_peer_cert(const struct tls_options *opt, openvpn_x509_cert_t *peer_cert, */ static void verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert_depth, - const char *subject, const char *common_name -#ifdef ENABLE_X509_TRACK - , const struct x509_track *x509_track -#endif - ) + const char *subject, const char *common_name, + const struct x509_track *x509_track) { char envname[64]; char *serial = NULL; struct gc_arena gc = gc_new (); /* Save X509 fields in environment */ -#ifdef ENABLE_X509_TRACK if (x509_track) x509_setenv_track (x509_track, es, cert_depth, peer_cert); else -#endif x509_setenv (es, cert_depth, peer_cert); /* export subject name string as environmental variable */ @@ -425,13 +405,19 @@ verify_cert_set_env(struct env_set *es, openvpn_x509_cert_t *peer_cert, int cert setenv_str (es, envname, common_name); #endif - /* export X509 cert SHA1 fingerprint */ + /* export X509 cert fingerprints */ { - unsigned char *sha1_hash = x509_get_sha1_hash(peer_cert, &gc); + struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc); + struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc); openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth); - setenv_str (es, envname, format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1, - ":", &gc)); + setenv_str (es, envname, + format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc)); + + openvpn_snprintf (envname, sizeof(envname), "tls_digest_sha256_%d", + cert_depth); + setenv_str (es, envname, + format_hex_ex(BPTR(&sha256), BLEN(&sha256), 0, 1, ":", &gc)); } /* export serial number as environmental variable */ @@ -530,7 +516,8 @@ verify_cert_call_command(const char *verify_command, struct env_set *es, } } - argv_printf (&argv, "%sc %d %s", verify_command, cert_depth, subject); + argv_parse_cmd (&argv, verify_command); + argv_printf_cat (&argv, "%d %s", cert_depth, subject); argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command"); ret = openvpn_run_script (&argv, es, 0, "--tls-verify script"); @@ -647,8 +634,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep /* verify level 1 cert, i.e. the CA that signed our leaf cert */ if (cert_depth == 1 && opt->verify_hash) { - unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc); - if (memcmp (sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH)) + struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc); + if (memcmp (BPTR (&sha1_hash), opt->verify_hash, BLEN(&sha1_hash))) { msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed"); goto cleanup; @@ -662,11 +649,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep session->verify_maxlevel = max_int (session->verify_maxlevel, cert_depth); /* export certificate values to the environment */ - verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name -#ifdef ENABLE_X509_TRACK - , opt->x509_track -#endif - ); + verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name, + opt->x509_track); /* export current untrusted IP */ setenv_untrusted (session); @@ -688,15 +672,18 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep if (opt->crl_file) { if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR) - { - if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert)) - goto cleanup; - } + { + if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert)) + goto cleanup; + } else - { - if (SUCCESS != x509_verify_crl(opt->crl_file, cert, subject)) - goto cleanup; - } + { + if (tls_verify_crl_missing (opt)) + { + msg (D_TLS_ERRORS, "VERIFY ERROR: CRL not loaded"); + goto cleanup; + } + } } msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", cert_depth, subject); @@ -1000,7 +987,8 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up setenv_untrusted (session); /* format command line */ - argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file); + argv_parse_cmd (&argv, session->opt->auth_user_pass_verify_script); + argv_printf_cat (&argv, "%s", tmp_file); /* call command */ ret = openvpn_run_script (&argv, session->opt->es, 0, @@ -1030,7 +1018,9 @@ static int verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username) { int retval = OPENVPN_PLUGIN_FUNC_ERROR; +#ifdef PLUGIN_DEF_AUTH struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ +#endif /* Is username defined? */ if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) @@ -1154,6 +1144,63 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, string_mod_remap_name (up->username, COMMON_NAME_CHAR_CLASS); string_mod (up->password, CC_PRINT, CC_CRLF, '_'); + /* If server is configured with --auth-gen-token and we have an + * authentication token for this client, this authentication + * round will be done internally using the token instead of + * calling any external authentication modules. + */ + if (session->opt->auth_token_generate && multi->auth_token_sent + && NULL != multi->auth_token) + { + unsigned int ssl_flags = session->opt->ssl_flags; + + /* Ensure that the username has not changed */ + if (!tls_lock_username(multi, up->username)) + { + ks->authenticated = false; + goto done; + } + + /* If auth-token lifetime has been enabled, + * ensure the token has not expired + */ + if (session->opt->auth_token_lifetime > 0 + && (multi->auth_token_tstamp + session->opt->auth_token_lifetime) < now) + { + msg (D_HANDSHAKE, "Auth-token for client expired\n"); + ks->authenticated = false; + goto done; + } + + /* The core authentication of the token itself */ + if (memcmp_constant_time(multi->auth_token, up->password, + strlen(multi->auth_token)) != 0) + { + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); + free (multi->auth_token); + multi->auth_token = NULL; + multi->auth_token_sent = false; + ks->authenticated = false; + tls_deauthenticate (multi); + + msg (D_TLS_ERRORS, "TLS Auth Error: Auth-token verification " + "failed for username '%s' %s", up->username, + (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); + } + else + { + ks->authenticated = true; + + if (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) + set_common_name (session, up->username); + msg (D_HANDSHAKE, "TLS: Username/auth-token authentication " + "succeeded for username '%s' %s", + up->username, + (ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); + } + goto done; + } + /* call plugin(s) and/or script */ #ifdef MANAGEMENT_DEF_AUTH if (man_def_auth == KMDA_DEF) @@ -1191,6 +1238,43 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, if (man_def_auth != KMDA_UNDEF) ks->auth_deferred = true; #endif + + if ((session->opt->auth_token_generate) && (NULL == multi->auth_token)) + { + /* Server is configured with --auth-gen-token but no token has yet + * been generated for this client. Generate one and save it. + */ + uint8_t tok[AUTH_TOKEN_SIZE]; + + if (!rand_bytes(tok, AUTH_TOKEN_SIZE)) + { + msg( M_FATAL, "Failed to get enough randomness for " + "authentication token"); + } + + /* The token should be longer than the input when + * being base64 encoded + */ + if( openvpn_base64_encode(tok, AUTH_TOKEN_SIZE, + &multi->auth_token) < AUTH_TOKEN_SIZE) + { + msg(D_TLS_ERRORS, "BASE64 encoding of token failed. " + "No auth-token will be activated now"); + if (multi->auth_token) + { + memset (multi->auth_token, 0, AUTH_TOKEN_SIZE); + free (multi->auth_token); + multi->auth_token = NULL; + } + } + else + { + multi->auth_token_tstamp = now; + dmsg (D_SHOW_KEYS, "Generated token for client: %s", + multi->auth_token); + } + } + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)) set_common_name (session, up->username); #ifdef ENABLE_DEF_AUTH @@ -1210,6 +1294,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer"); } + done: gc_free (&gc); } @@ -1270,4 +1355,4 @@ verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session) gc_free (&gc); } } -#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) */ +#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h index e0bcba4..98312fd 100644 --- a/src/openvpn/ssl_verify.h +++ b/src/openvpn/ssl_verify.h @@ -30,17 +30,18 @@ #ifndef SSL_VERIFY_H_ #define SSL_VERIFY_H_ +#ifdef ENABLE_CRYPTO + #include "syshead.h" #include "misc.h" -#include "manage.h" #include "ssl_common.h" /* Include OpenSSL-specific code */ #ifdef ENABLE_CRYPTO_OPENSSL #include "ssl_verify_openssl.h" #endif -#ifdef ENABLE_CRYPTO_POLARSSL -#include "ssl_verify_polarssl.h" +#ifdef ENABLE_CRYPTO_MBEDTLS +#include "ssl_verify_mbedtls.h" #endif #include "ssl_verify_backend.h" @@ -54,7 +55,7 @@ /** Structure containing the hash for a single certificate */ struct cert_hash { - unsigned char sha1_hash[SHA_DIGEST_LENGTH]; /**< The SHA1 hash for a certificate */ + unsigned char sha256_hash[256/8]; }; /** Structure containing the hashes for a full certificate chain */ @@ -66,8 +67,6 @@ struct cert_hash_set { #define VERIFY_X509_SUBJECT_DN 1 #define VERIFY_X509_SUBJECT_RDN 2 #define VERIFY_X509_SUBJECT_RDN_PREFIX 3 -#define TLS_REMOTE_SUBJECT_DN 1 + 0x100 -#define TLS_REMOTE_SUBJECT_RDN_PREFIX 3 + 0x100 #define TLS_AUTHENTICATION_SUCCEEDED 0 #define TLS_AUTHENTICATION_FAILED 1 @@ -136,6 +135,14 @@ const char *tls_common_name (const struct tls_multi* multi, const bool null); */ const char *tls_username (const struct tls_multi *multi, const bool null); +/** + * Compares certificates hashes, returns true if hashes are equal. + * + * @param chs1 cert 1 hash set + * @param chs2 cert 2 hash set + */ +bool cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2); + #ifdef ENABLE_PF /** @@ -165,25 +172,6 @@ tls_common_name_hash (const struct tls_multi *multi, const char **cn, uint32_t * #endif -/** - * Returns whether or not the server should check for username/password - * - * @param session The current TLS session - * - * @return true if username and password verification is enabled, - * false if not. - * - */ -static inline bool verify_user_pass_enabled(struct tls_session *session) -{ - return (session->opt->auth_user_pass_verify_script - || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) -#ifdef MANAGEMENT_DEF_AUTH - || management_enable_def_auth (management) -#endif - ); -} - /** * Verify the given username and password, using either an external script, a * plugin, or the management interface. @@ -211,8 +199,6 @@ void verify_user_pass(struct user_pass *up, struct tls_multi *multi, */ void verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session); -#ifdef ENABLE_X509_TRACK - struct x509_track { const struct x509_track *next; @@ -222,10 +208,6 @@ struct x509_track int nid; }; -void x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc); - -#endif - /* * Certificate checking for verify_nsCertType */ @@ -254,5 +236,6 @@ tls_client_reason (struct tls_multi *multi) #endif } -#endif /* SSL_VERIFY_H_ */ +#endif /* ENABLE_CRYPTO */ +#endif /* SSL_VERIFY_H_ */ diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h index 01e453e..de304b9 100644 --- a/src/openvpn/ssl_verify_backend.h +++ b/src/openvpn/ssl_verify_backend.h @@ -66,10 +66,10 @@ result_t verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int * * @param session TLS Session associated with this tunnel * @param cert_depth Depth of the current certificate - * @param sha1_hash Hash of the current certificate + * @param cert_hash Hash of the current certificate */ void cert_hash_remember (struct tls_session *session, const int cert_depth, - const unsigned char *sha1_hash); + const struct buffer *cert_hash); /* * Library-specific functions. @@ -87,14 +87,27 @@ void cert_hash_remember (struct tls_session *session, const int cert_depth, */ char *x509_get_subject (openvpn_x509_cert_t *cert, struct gc_arena *gc); -/* Retrieve the certificate's SHA1 hash. +/** + * Retrieve the certificate's SHA1 fingerprint. * - * @param cert Certificate to retrieve the hash from. + * @param cert Certificate to retrieve the fingerprint from. * @param gc Garbage collection arena to use when allocating string. * - * @return a string containing the SHA1 hash of the certificate + * @return a string containing the certificate fingerprint */ -unsigned char *x509_get_sha1_hash (openvpn_x509_cert_t *cert, struct gc_arena *gc); +struct buffer x509_get_sha1_fingerprint (openvpn_x509_cert_t *cert, + struct gc_arena *gc); + +/** + * Retrieve the certificate's SHA256 fingerprint. + * + * @param cert Certificate to retrieve the fingerprint from. + * @param gc Garbage collection arena to use when allocating string. + * + * @return a string containing the certificate fingerprint + */ +struct buffer x509_get_sha256_fingerprint (openvpn_x509_cert_t *cert, + struct gc_arena *gc); /* * Retrieve the certificate's username from the specified field. @@ -150,8 +163,6 @@ char *backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, */ void x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert); -#ifdef ENABLE_X509_TRACK - /* * Start tracking the given attribute. * @@ -189,8 +200,6 @@ void x509_track_add (const struct x509_track **ll_head, const char *name, void x509_setenv_track (const struct x509_track *xt, struct env_set *es, const int depth, openvpn_x509_cert_t *x509); -#endif - /* * Check X.509 Netscape certificate type field, if available. * @@ -204,8 +213,6 @@ void x509_setenv_track (const struct x509_track *xt, struct env_set *es, */ result_t x509_verify_ns_cert_type(const openvpn_x509_cert_t *cert, const int usage); -#if OPENSSL_VERSION_NUMBER >= 0x00907000L || ENABLE_CRYPTO_POLARSSL - /* * Verify X.509 key usage extension field. * @@ -234,8 +241,6 @@ result_t x509_verify_cert_ku (openvpn_x509_cert_t *x509, const unsigned * const */ result_t x509_verify_cert_eku (openvpn_x509_cert_t *x509, const char * const expected_oid); -#endif - /* * Store the given certificate in pem format in a temporary file in tmp_dir * @@ -247,18 +252,12 @@ result_t x509_verify_cert_eku (openvpn_x509_cert_t *x509, const char * const exp */ result_t x509_write_pem(FILE *peercert_file, openvpn_x509_cert_t *peercert); -/* - * Check the certificate against a CRL file. - * - * @param crl_file File name of the CRL file - * @param cert Certificate to verify - * @param subject Subject of the given certificate - * - * @return \c SUCCESS if the CRL was not signed by the issuer of the - * certificate or does not contain an entry for it. - * \c FAILURE otherwise. +/** + * Return true iff a CRL is configured, but is not loaded. This can be caused + * by e.g. a CRL parsing error, a missing CRL file or CRL file permission + * errors. (These conditions are checked upon startup, but the CRL might be + * updated and reloaded during runtime.) */ -result_t x509_verify_crl(const char *crl_file, openvpn_x509_cert_t *cert, - const char *subject); +bool tls_verify_crl_missing(const struct tls_options *opt); #endif /* SSL_VERIFY_BACKEND_H_ */ diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c new file mode 100644 index 0000000..332f04b --- /dev/null +++ b/src/openvpn/ssl_verify_mbedtls.c @@ -0,0 +1,511 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file Control Channel Verification Module mbed TLS backend + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) + +#include "crypto_mbedtls.h" +#include "ssl_verify.h" +#include +#include +#include +#include +#include + +#define MAX_SUBJECT_LENGTH 256 + +int +verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth, + uint32_t *flags) +{ + struct tls_session *session = (struct tls_session *) session_obj; + struct gc_arena gc = gc_new(); + + ASSERT (cert); + ASSERT (session); + + session->verified = false; + + /* Remember certificate hash */ + struct buffer cert_fingerprint = x509_get_sha256_fingerprint (cert, &gc); + cert_hash_remember (session, cert_depth, &cert_fingerprint); + + /* did peer present cert which was signed by our root cert? */ + if (*flags != 0) + { + int ret = 0; + char errstr[512] = { 0 }; + char *subject = x509_get_subject(cert, &gc); + + ret = mbedtls_x509_crt_verify_info (errstr, sizeof(errstr)-1, "", *flags); + if (ret <= 0 && !openvpn_snprintf(errstr, sizeof(errstr), + "Could not retrieve error string, flags=%"PRIx32, *flags)) + { + errstr[0] = '\0'; + } + else + { + chomp(errstr); + } + + if (subject) + { + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s: %s", + cert_depth, subject, errstr); + } + else + { + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, (could not extract X509 " + "subject string from certificate): %s", cert_depth, errstr); + } + + /* Leave flags set to non-zero to indicate that the cert is not ok */ + } + else if (SUCCESS != verify_cert(session, cert, cert_depth)) + { + *flags |= MBEDTLS_X509_BADCERT_OTHER; + } + + gc_free(&gc); + + /* + * PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors. + */ + return 0; +} + +#ifdef ENABLE_X509ALTUSERNAME +# warning "X509 alt user name not yet supported for mbed TLS" +#endif + +result_t +backend_x509_get_username (char *cn, int cn_len, + char *x509_username_field, mbedtls_x509_crt *cert) +{ + mbedtls_x509_name *name; + + ASSERT( cn != NULL ); + + name = &cert->subject; + + /* Find common name */ + while( name != NULL ) + { + if (0 == memcmp (name->oid.p, MBEDTLS_OID_AT_CN, + MBEDTLS_OID_SIZE (MBEDTLS_OID_AT_CN))) + break; + + name = name->next; + } + + /* Not found, return an error if this is the peer's certificate */ + if( name == NULL ) + return FAILURE; + + /* Found, extract CN */ + if (cn_len > name->val.len) + { + memcpy( cn, name->val.p, name->val.len ); + cn[name->val.len] = '\0'; + } + else + { + memcpy( cn, name->val.p, cn_len); + cn[cn_len-1] = '\0'; + } + + return SUCCESS; +} + +char * +backend_x509_get_serial (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + char *buf = NULL; + size_t buflen = 0; + mbedtls_mpi serial_mpi = { 0 }; + + /* Transform asn1 integer serial into mbed TLS MPI */ + mbedtls_mpi_init(&serial_mpi); + if (!mbed_ok(mbedtls_mpi_read_binary(&serial_mpi, cert->serial.p, + cert->serial.len))) + { + msg(M_WARN, "Failed to retrieve serial from certificate."); + goto end; + } + + /* Determine decimal representation length, allocate buffer */ + mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen); + buf = gc_malloc(buflen, true, gc); + + /* Write MPI serial as decimal string into buffer */ + if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen))) + { + msg(M_WARN, "Failed to write serial to string."); + buf = NULL; + goto end; + } + +end: + mbedtls_mpi_free(&serial_mpi); + return buf; +} + +char * +backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + char *buf = NULL; + size_t len = cert->serial.len * 3 + 1; + + buf = gc_malloc(len, true, gc); + + if(mbedtls_x509_serial_gets(buf, len-1, &cert->serial) < 0) + buf = NULL; + + return buf; +} + +static struct buffer +x509_get_fingerprint (const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert, + struct gc_arena *gc) +{ + const size_t md_size = mbedtls_md_get_size (md_info); + struct buffer fingerprint = alloc_buf_gc (md_size, gc); + mbedtls_md(md_info, cert->raw.p, cert->tbs.len, BPTR (&fingerprint)); + ASSERT (buf_inc_len(&fingerprint, md_size)); + return fingerprint; +} + +struct buffer +x509_get_sha1_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), + cert, gc); +} + +struct buffer +x509_get_sha256_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + cert, gc); +} + +char * +x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc) +{ + char tmp_subject[MAX_SUBJECT_LENGTH] = {0}; + char *subject = NULL; + + int ret = 0; + + ret = mbedtls_x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); + if (ret > 0) + { + /* Allocate the required space for the subject */ + subject = string_alloc(tmp_subject, gc); + } + + return subject; +} + +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 char * +asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc) +{ + size_t i; + char *val; + + for (i = 0; i < orig->len; ++i) + if (orig->p[i] == '\0') + return "ERROR: embedded null value"; + val = gc_malloc(orig->len+1, false, gc); + memcpy(val, orig->p, orig->len); + val[orig->len] = '\0'; + return val; +} + +static void +do_setenv_name(struct env_set *es, const struct x509_track *xt, + const mbedtls_x509_crt *cert, int depth, struct gc_arena *gc) +{ + const mbedtls_x509_name *xn; + for (xn = &cert->subject; xn != NULL; xn = xn->next) + { + const char *xn_short_name = NULL; + if (0 == mbedtls_oid_get_attr_short_name (&xn->oid, &xn_short_name) && + 0 == strcmp (xt->name, xn_short_name)) + { + char *val_str = asn1_buf_to_c_string (&xn->val, gc); + do_setenv_x509 (es, xt->name, val_str, depth); + } + } +} + +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->next = *ll_head; + *ll_head = xt; +} + +void +x509_setenv_track (const struct x509_track *xt, struct env_set *es, + const int depth, mbedtls_x509_crt *cert) +{ + struct gc_arena gc = gc_new(); + while (xt) + { + if (depth == 0 || (xt->flags & XT_FULL_CHAIN)) + { + if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256")) + { + /* Fingerprint is not part of X509 structure */ + struct buffer cert_hash; + char *fingerprint; + + if (0 == strcmp(xt->name, "SHA1")) + cert_hash = x509_get_sha1_fingerprint(cert, &gc); + else + cert_hash = x509_get_sha256_fingerprint(cert, &gc); + + fingerprint = format_hex_ex(BPTR(&cert_hash), + BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc); + do_setenv_x509(es, xt->name, fingerprint, depth); + } + else + { + do_setenv_name(es, xt, cert, depth, &gc); + } + } + xt = xt->next; + } + gc_free(&gc); +} + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + */ +void +x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert) +{ + int i; + unsigned char c; + const mbedtls_x509_name *name; + char s[128]; + + name = &cert->subject; + + memset( s, 0, sizeof( s ) ); + + while( name != NULL ) + { + char name_expand[64+8]; + const char *shortname; + + if( 0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname) ) + { + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_%s", + cert_depth, shortname); + } + else + { + openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?", + cert_depth); + } + + for( i = 0; i < name->val.len; i++ ) + { + if( i >= (int) sizeof( s ) - 1 ) + break; + + c = name->val.p[i]; + if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) + s[i] = '?'; + else s[i] = c; + } + s[i] = '\0'; + + /* Check both strings, set environment variable */ + string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); + string_mod ((char*)s, CC_PRINT, CC_CRLF, '_'); + setenv_str_incr (es, name_expand, (char*)s); + + name = name->next; + } +} + +result_t +x509_verify_ns_cert_type(const mbedtls_x509_crt *cert, const int usage) +{ + if (usage == NS_CERT_CHECK_NONE) + return SUCCESS; + if (usage == NS_CERT_CHECK_CLIENT) + return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE) + && (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT)) ? + SUCCESS : FAILURE; + if (usage == NS_CERT_CHECK_SERVER) + return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE) + && (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER)) ? + SUCCESS : FAILURE; + + return FAILURE; +} + +result_t +x509_verify_cert_ku (mbedtls_x509_crt *cert, const unsigned * const expected_ku, + int expected_len) +{ + result_t fFound = FAILURE; + + if(!(cert->ext_types & MBEDTLS_X509_EXT_KEY_USAGE)) + { + msg (D_HANDSHAKE, "Certificate does not have key usage extension"); + } + else + { + int i; + unsigned nku = cert->key_usage; + + msg (D_HANDSHAKE, "Validating certificate key usage"); + for (i=0; SUCCESS != fFound && iext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE)) + { + msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); + } + else + { + mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage); + + msg (D_HANDSHAKE, "Validating certificate extended key usage"); + while (oid_seq != NULL) + { + mbedtls_x509_buf *oid = &oid_seq->buf; + char oid_num_str[1024]; + const char *oid_str; + + if (0 == mbedtls_oid_get_extended_key_usage( oid, &oid_str )) + { + msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", + oid_str, expected_oid); + if (!strcmp (expected_oid, oid_str)) + { + fFound = SUCCESS; + break; + } + } + + if (0 < mbedtls_oid_get_numeric_string( oid_num_str, + sizeof (oid_num_str), oid)) + { + msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", + oid_num_str, expected_oid); + if (!strcmp (expected_oid, oid_num_str)) + { + fFound = SUCCESS; + break; + } + } + oid_seq = oid_seq->next; + } + } + + return fFound; +} + +result_t +x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert) +{ + msg (M_WARN, "mbed TLS does not support writing peer certificate in PEM format"); + return FAILURE; +} + +bool +tls_verify_crl_missing(const struct tls_options *opt) +{ + if (opt->crl_file && !(opt->ssl_flags & SSLF_CRL_VERIFY_DIR) + && (opt->ssl_ctx.crl == NULL || opt->ssl_ctx.crl->version == 0)) + { + return true; + } + return false; +} + +#endif /* #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/ssl_verify_mbedtls.h b/src/openvpn/ssl_verify_mbedtls.h new file mode 100644 index 0000000..e26d08f --- /dev/null +++ b/src/openvpn/ssl_verify_mbedtls.h @@ -0,0 +1,78 @@ +/* + * 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. + * Copyright (C) 2010 Fox Crypto B.V. + * + * 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 + */ + +/** + * @file Control Channel Verification Module mbed TLS backend + */ + +#ifndef SSL_VERIFY_MBEDTLS_H_ +#define SSL_VERIFY_MBEDTLS_H_ + +#include "syshead.h" +#include + +#ifndef __OPENVPN_X509_CERT_T_DECLARED +#define __OPENVPN_X509_CERT_T_DECLARED +typedef mbedtls_x509_crt openvpn_x509_cert_t; +#endif + +/** @name Function for authenticating a new connection from a remote OpenVPN peer + * @{ */ + +/** + * Verify that the remote OpenVPN peer's certificate allows setting up a + * VPN tunnel. + * @ingroup control_tls + * + * This callback function is called when a new TLS session is being setup to + * determine whether the remote OpenVPN peer's certificate is allowed to + * connect. It is called for once for every certificate in the chain. The + * callback functionality is configured in the \c init_ssl() function, which + * calls the mbed TLS library's \c ssl_set_verify_callback() function with \c + * verify_callback() as its callback argument. + * + * It checks *flags and registers the certificate hash. If these steps succeed, + * it calls the \c verify_cert() function, which performs OpenVPN-specific + * verification. + * + * @param session_obj - The OpenVPN \c tls_session associated with this object, + * as set during SSL session setup. + * @param cert - The certificate used by mbed TLS. + * @param cert_depth - The depth of the current certificate in the chain, with + * 0 being the actual certificate. + * @param flags - Whether the remote OpenVPN peer's certificate + * passed verification. A value of 0 means it + * verified successfully, any other value means it + * failed. \c verify_callback() is considered to have + * ok'ed this certificate if flags is 0 when it returns. + * + * @return The return value is 0 unless a fatal error occurred. + */ +int verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth, + uint32_t *flags); + +/** @} name Function for authenticating a new connection from a remote OpenVPN peer */ + +#endif /* SSL_VERIFY_MBEDTLS_H_ */ diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c index 4750f02..3d1c85e 100644 --- a/src/openvpn/ssl_verify_openssl.c +++ b/src/openvpn/ssl_verify_openssl.c @@ -35,11 +35,15 @@ #include "syshead.h" -#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#include "ssl_verify_openssl.h" + +#include "error.h" +#include "ssl_openssl.h" #include "ssl_verify.h" #include "ssl_verify_backend.h" -#include "ssl_openssl.h" + #include #include @@ -57,8 +61,8 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index); ASSERT (session); - cert_hash_remember (session, ctx->error_depth, - x509_get_sha1_hash(ctx->current_cert, &gc)); + struct buffer cert_hash = x509_get_sha256_fingerprint(ctx->current_cert, &gc); + cert_hash_remember (session, ctx->error_depth, &cert_hash); /* did peer present cert which was signed by our root cert? */ if (!preverify_ok) @@ -66,15 +70,28 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) /* get the X509 name */ char *subject = x509_get_subject(ctx->current_cert, &gc); - if (subject) + if (!subject) + { + subject = "(Failed to retrieve certificate subject)"; + } + + /* Log and ignore missing CRL errors */ + if (ctx->error == X509_V_ERR_UNABLE_TO_GET_CRL) { - /* Remote site specified a certificate, but it's not correct */ - msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", - ctx->error_depth, - X509_verify_cert_error_string (ctx->error), - subject); + msg (D_TLS_DEBUG_LOW, "VERIFY WARNING: depth=%d, %s: %s", + ctx->error_depth, + X509_verify_cert_error_string (ctx->error), + subject); + ret = 1; + goto cleanup; } + /* Remote site specified a certificate, but it's not correct */ + msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", + ctx->error_depth, + X509_verify_cert_error_string (ctx->error), + subject); + ERR_clear_error(); session->verified = false; @@ -134,8 +151,8 @@ bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) } break; default: - msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i", - name->type); + msg (D_TLS_DEBUG, "%s: ignoring general name field type %i", + __func__, name->type); break; } } @@ -165,8 +182,8 @@ extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int tmp = -1; X509_NAME_ENTRY *x509ne = 0; ASN1_STRING *asn1 = 0; - unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - int nid = OBJ_txt2nid((char *)field_name); + unsigned char *buf = NULL; + int nid = OBJ_txt2nid(field_name); ASSERT (size > 0); *out = '\0'; @@ -244,11 +261,21 @@ backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, struct gc_arena *gc) return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc); } -unsigned char * -x509_get_sha1_hash (X509 *cert, struct gc_arena *gc) +struct buffer +x509_get_sha1_fingerprint (X509 *cert, struct gc_arena *gc) +{ + struct buffer hash = alloc_buf_gc(sizeof(cert->sha1_hash), gc); + memcpy(BPTR(&hash), cert->sha1_hash, sizeof(cert->sha1_hash)); + ASSERT (buf_inc_len(&hash, sizeof (cert->sha1_hash))); + return hash; +} + +struct buffer +x509_get_sha256_fingerprint (X509 *cert, struct gc_arena *gc) { - unsigned char *hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); - memcpy(hash, cert->sha1_hash, SHA_DIGEST_LENGTH); + struct buffer hash = alloc_buf_gc((EVP_sha256())->md_size, gc); + X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL); + ASSERT (buf_inc_len(&hash, (EVP_sha256())->md_size)); return hash; } @@ -299,7 +326,26 @@ err: } -#ifdef ENABLE_X509_TRACK +/* + * x509-track implementation -- save X509 fields to environment, + * using the naming convention: + * + * X509_{cert_depth}_{name}={value} + * + * This function differs from x509_setenv 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. + * (4) This function can return the SHA1 fingerprint of a cert, e.g. + * x509-track "+SHA1" + * will return the SHA1 fingerprint for each certificate in the + * peer chain. + */ void x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc) @@ -342,60 +388,82 @@ do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth) void x509_setenv_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509) { + struct gc_arena gc = gc_new(); 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 + switch (xt->nid) { - 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); - } - } - } + case NID_sha1: + case NID_sha256: + { + struct buffer fp_buf; + char *fp_str = NULL; + + if (xt->nid == NID_sha1) + fp_buf = x509_get_sha1_fingerprint(x509, &gc); + else + fp_buf = x509_get_sha256_fingerprint(x509, &gc); + + fp_str = format_hex_ex(BPTR(&fp_buf), BLEN(&fp_buf), 0, + 1 | FHE_CAPS, ":", &gc); + do_setenv_x509(es, xt->name, fp_str, depth); + } + break; + default: + { + int 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; } + gc_free(&gc); } -#endif /* * Save X509 fields to environment, using the naming convention: @@ -444,7 +512,7 @@ x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *peer_cert) objbuf); string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); string_mod ((char*)buf, CC_PRINT, CC_CRLF, '_'); - setenv_str (es, name_expand, (char*)buf); + setenv_str_incr (es, name_expand, (char*)buf); free (name_expand); OPENSSL_free (buf); } @@ -465,8 +533,6 @@ x509_verify_ns_cert_type(const openvpn_x509_cert_t *peer_cert, const int usage) return FAILURE; } -#if OPENSSL_VERSION_NUMBER >= 0x00907000L - result_t x509_verify_cert_ku (X509 *x509, const unsigned * const expected_ku, int expected_len) @@ -572,61 +638,28 @@ x509_write_pem(FILE *peercert_file, X509 *peercert) return SUCCESS; } -#endif /* OPENSSL_VERSION_NUMBER */ - -/* - * check peer cert against CRL - */ -result_t -x509_verify_crl(const char *crl_file, X509 *peer_cert, const char *subject) +bool +tls_verify_crl_missing(const struct tls_options *opt) { - X509_CRL *crl=NULL; - X509_REVOKED *revoked; - BIO *in=NULL; - int n,i; - result_t retval = FAILURE; - struct gc_arena gc = gc_new(); - char *serial; - - in = BIO_new_file (crl_file, "r"); - - if (in == NULL) { - msg (M_WARN, "CRL: cannot read: %s", crl_file); - goto end; - } - crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL); - if (crl == NULL) { - msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file); - goto end; - } - - if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(peer_cert)) != 0) { - msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of " - "certificate %s", crl_file, subject); - retval = SUCCESS; - goto end; - } - - 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(peer_cert)) == 0) { - serial = backend_x509_get_serial_hex(peer_cert, &gc); - msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE")); - goto end; + if (!opt->crl_file || (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)) + { + return false; } - } - - retval = SUCCESS; - msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); -end: - gc_free(&gc); - BIO_free(in); - if (crl) - X509_CRL_free (crl); + X509_STORE *store = SSL_CTX_get_cert_store(opt->ssl_ctx.ctx); + if (!store) + crypto_msg (M_FATAL, "Cannot get certificate store"); - return retval; + for (int i = 0; i < sk_X509_OBJECT_num(store->objs); i++) + { + X509_OBJECT* obj = sk_X509_OBJECT_value(store->objs, i); + ASSERT(obj); + if (obj->type == X509_LU_CRL) + { + return false; + } + } + return true; } -#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) */ +#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) */ diff --git a/src/openvpn/ssl_verify_polarssl.c b/src/openvpn/ssl_verify_polarssl.c deleted file mode 100644 index 7ed87d6..0000000 --- a/src/openvpn/ssl_verify_polarssl.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file Control Channel Verification Module PolarSSL backend - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) - -#include "ssl_verify.h" -#include -#include -#include -#include - -#define MAX_SUBJECT_LENGTH 256 - -int -verify_callback (void *session_obj, x509_crt *cert, int cert_depth, - int *flags) -{ - struct tls_session *session = (struct tls_session *) session_obj; - struct gc_arena gc = gc_new(); - - ASSERT (cert); - ASSERT (session); - - session->verified = false; - - /* Remember certificate hash */ - cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc)); - - /* did peer present cert which was signed by our root cert? */ - if (*flags != 0) - { - char *subject = x509_get_subject(cert, &gc); - - if (subject) - msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, %s", cert_depth, *flags, subject); - else - msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, could not extract X509 " - "subject string from certificate", *flags, cert_depth); - - /* Leave flags set to non-zero to indicate that the cert is not ok */ - } - else if (SUCCESS != verify_cert(session, cert, cert_depth)) - { - *flags |= BADCERT_OTHER; - } - - gc_free(&gc); - - /* - * PolarSSL-1.2.0+ expects 0 on anything except fatal errors. - */ - return 0; -} - -#ifdef ENABLE_X509ALTUSERNAME -# warning "X509 alt user name not yet supported for PolarSSL" -#endif - -result_t -backend_x509_get_username (char *cn, int cn_len, - char *x509_username_field, x509_crt *cert) -{ - x509_name *name; - - ASSERT( cn != NULL ); - - name = &cert->subject; - - /* Find common name */ - while( name != NULL ) - { - if( memcmp( name->oid.p, OID_AT_CN, OID_SIZE(OID_AT_CN) ) == 0) - break; - - name = name->next; - } - - /* Not found, return an error if this is the peer's certificate */ - if( name == NULL ) - return FAILURE; - - /* Found, extract CN */ - if (cn_len > name->val.len) - memcpy( cn, name->val.p, name->val.len ); - else - { - memcpy( cn, name->val.p, cn_len); - cn[cn_len-1] = '\0'; - } - - return SUCCESS; -} - -char * -backend_x509_get_serial (openvpn_x509_cert_t *cert, struct gc_arena *gc) -{ - char *buf = NULL; - size_t buflen = 0; - mpi serial_mpi = { 0 }; - - /* Transform asn1 integer serial into PolarSSL MPI */ - mpi_init(&serial_mpi); - if (!polar_ok(mpi_read_binary(&serial_mpi, cert->serial.p, cert->serial.len))) - { - msg(M_WARN, "Failed to retrieve serial from certificate."); - return NULL; - } - - /* Determine decimal representation length, allocate buffer */ - mpi_write_string(&serial_mpi, 10, buf, &buflen); - buf = gc_malloc(buflen, true, gc); - - /* Write MPI serial as decimal string into buffer */ - if (!polar_ok(mpi_write_string(&serial_mpi, 10, buf, &buflen))) - { - msg(M_WARN, "Failed to write serial to string."); - return NULL; - } - - return buf; -} - -char * -backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, struct gc_arena *gc) -{ - char *buf = NULL; - size_t len = cert->serial.len * 3 + 1; - - buf = gc_malloc(len, true, gc); - - if(x509_serial_gets(buf, len-1, &cert->serial) < 0) - buf = NULL; - - return buf; -} - -unsigned char * -x509_get_sha1_hash (x509_crt *cert, struct gc_arena *gc) -{ - unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc); - sha1(cert->tbs.p, cert->tbs.len, sha1_hash); - return sha1_hash; -} - -char * -x509_get_subject(x509_crt *cert, struct gc_arena *gc) -{ - char tmp_subject[MAX_SUBJECT_LENGTH] = {0}; - char *subject = NULL; - - int ret = 0; - - ret = x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject ); - if (ret > 0) - { - /* Allocate the required space for the subject */ - subject = string_alloc(tmp_subject, gc); - } - - return subject; -} - -/* - * Save X509 fields to environment, using the naming convention: - * - * X509_{cert_depth}_{name}={value} - */ -void -x509_setenv (struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert) -{ - int i; - unsigned char c; - const x509_name *name; - char s[128]; - - name = &cert->subject; - - memset( s, 0, sizeof( s ) ); - - while( name != NULL ) - { - char name_expand[64+8]; - const char *shortname; - - if( 0 == oid_get_attr_short_name(&name->oid, &shortname) ) - { - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_%s", - cert_depth, shortname); - } - else - { - openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?", - cert_depth); - } - - for( i = 0; i < name->val.len; i++ ) - { - if( i >= (int) sizeof( s ) - 1 ) - break; - - c = name->val.p[i]; - if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) - s[i] = '?'; - else s[i] = c; - } - s[i] = '\0'; - - /* Check both strings, set environment variable */ - string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); - string_mod ((char*)s, CC_PRINT, CC_CRLF, '_'); - setenv_str (es, name_expand, (char*)s); - - name = name->next; - } -} - -result_t -x509_verify_ns_cert_type(const x509_crt *cert, const int usage) -{ - if (usage == NS_CERT_CHECK_NONE) - return SUCCESS; - if (usage == NS_CERT_CHECK_CLIENT) - return ((cert->ext_types & EXT_NS_CERT_TYPE) - && (cert->ns_cert_type & NS_CERT_TYPE_SSL_CLIENT)) ? SUCCESS : FAILURE; - if (usage == NS_CERT_CHECK_SERVER) - return ((cert->ext_types & EXT_NS_CERT_TYPE) - && (cert->ns_cert_type & NS_CERT_TYPE_SSL_SERVER)) ? SUCCESS : FAILURE; - - return FAILURE; -} - -result_t -x509_verify_cert_ku (x509_crt *cert, const unsigned * const expected_ku, - int expected_len) -{ - result_t fFound = FAILURE; - - if(!(cert->ext_types & EXT_KEY_USAGE)) - { - msg (D_HANDSHAKE, "Certificate does not have key usage extension"); - } - else - { - int i; - unsigned nku = cert->key_usage; - - msg (D_HANDSHAKE, "Validating certificate key usage"); - for (i=0; SUCCESS != fFound && iext_types & EXT_EXTENDED_KEY_USAGE)) - { - msg (D_HANDSHAKE, "Certificate does not have extended key usage extension"); - } - else - { - x509_sequence *oid_seq = &(cert->ext_key_usage); - - msg (D_HANDSHAKE, "Validating certificate extended key usage"); - while (oid_seq != NULL) - { - x509_buf *oid = &oid_seq->buf; - char oid_num_str[1024]; - const char *oid_str; - - if (0 == oid_get_extended_key_usage( oid, &oid_str )) - { - msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", - oid_str, expected_oid); - if (!strcmp (expected_oid, oid_str)) - { - fFound = SUCCESS; - break; - } - } - - if (0 < oid_get_numeric_string( oid_num_str, - sizeof (oid_num_str), oid)) - { - msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", - oid_num_str, expected_oid); - if (!strcmp (expected_oid, oid_num_str)) - { - fFound = SUCCESS; - break; - } - } - oid_seq = oid_seq->next; - } - } - - return fFound; -} - -result_t -x509_write_pem(FILE *peercert_file, x509_crt *peercert) -{ - msg (M_WARN, "PolarSSL does not support writing peer certificate in PEM format"); - return FAILURE; -} - -/* - * check peer cert against CRL - */ -result_t -x509_verify_crl(const char *crl_file, x509_crt *cert, const char *subject) -{ - result_t retval = FAILURE; - x509_crl crl = {0}; - struct gc_arena gc = gc_new(); - char *serial; - - if (!polar_ok(x509_crl_parse_file(&crl, crl_file))) - { - msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file); - goto end; - } - - if(cert->issuer_raw.len != crl.issuer_raw.len || - memcmp(crl.issuer_raw.p, cert->issuer_raw.p, crl.issuer_raw.len) != 0) - { - msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of " - "certificate %s", crl_file, subject); - retval = SUCCESS; - goto end; - } - - if (!polar_ok(x509_crt_revoked(cert, &crl))) - { - serial = backend_x509_get_serial_hex(cert, &gc); - msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE")); - goto end; - } - - retval = SUCCESS; - msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject); - -end: - gc_free(&gc); - x509_crl_free(&crl); - return retval; -} - -#endif /* #if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_POLARSSL) */ diff --git a/src/openvpn/ssl_verify_polarssl.h b/src/openvpn/ssl_verify_polarssl.h deleted file mode 100644 index b5157ed..0000000 --- a/src/openvpn/ssl_verify_polarssl.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - * Copyright (C) 2010 Fox Crypto B.V. - * - * 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 - */ - -/** - * @file Control Channel Verification Module PolarSSL backend - */ - -#ifndef SSL_VERIFY_POLARSSL_H_ -#define SSL_VERIFY_POLARSSL_H_ - -#include "syshead.h" -#include "misc.h" -#include "manage.h" -#include - -#ifndef __OPENVPN_X509_CERT_T_DECLARED -#define __OPENVPN_X509_CERT_T_DECLARED -typedef x509_crt openvpn_x509_cert_t; -#endif - -/** @name Function for authenticating a new connection from a remote OpenVPN peer - * @{ */ - -/** - * Verify that the remote OpenVPN peer's certificate allows setting up a - * VPN tunnel. - * @ingroup control_tls - * - * This callback function is called when a new TLS session is being setup to - * determine whether the remote OpenVPN peer's certificate is allowed to - * connect. It is called for once for every certificate in the chain. The - * callback functionality is configured in the \c init_ssl() function, which - * calls the PolarSSL library's \c ssl_set_verify_callback() function with \c - * verify_callback() as its callback argument. - * - * It checks *flags and registers the certificate hash. If these steps succeed, - * it calls the \c verify_cert() function, which performs OpenVPN-specific - * verification. - * - * @param session_obj - The OpenVPN \c tls_session associated with this object, - * as set during SSL session setup. - * @param cert - The certificate used by PolarSSL. - * @param cert_depth - The depth of the current certificate in the chain, with - * 0 being the actual certificate. - * @param flags - Whether the remote OpenVPN peer's certificate - * passed verification. A value of 0 means it - * verified successfully, any other value means it - * failed. \c verify_callback() is considered to have - * ok'ed this certificate if flags is 0 when it returns. - * - * @return The return value is 0 unless a fatal error occurred. - */ -int verify_callback (void *session_obj, x509_crt *cert, int cert_depth, - int *flags); - -/** @} name Function for authenticating a new connection from a remote OpenVPN peer */ - -#endif /* SSL_VERIFY_POLARSSL_H_ */ diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index 24926ae..8de7d87 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -37,7 +37,7 @@ # define unlikely(x) (x) #endif -#ifdef WIN32 +#ifdef _WIN32 #include #include #define sleep(x) Sleep((x)*1000) @@ -64,7 +64,7 @@ # include #endif -#ifndef WIN32 +#ifndef _WIN32 #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif @@ -217,7 +217,7 @@ #include #endif -#ifdef TARGET_LINUX +#if defined(TARGET_LINUX) || defined (TARGET_ANDROID) #ifdef HAVE_LINUX_IF_TUN_H #include @@ -358,9 +358,14 @@ #endif /* TARGET_DARWIN */ -#ifdef WIN32 -#include +#ifdef _WIN32 + // Missing declarations for MinGW 32. + // #if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 2 + typedef int MIB_TCP_STATE; + // #endif +#include #include +#include #include #include /* The following two headers are needed of PF_INET6 */ @@ -379,16 +384,13 @@ * Pedantic mode is meant to accomplish lint-style program checking, * not to build a working executable. */ -#ifdef __STRICT_ANSI__ -# define PEDANTIC 1 +#ifdef PEDANTIC # undef HAVE_CPP_VARARG_MACRO_GCC # undef HAVE_CPP_VARARG_MACRO_ISO # undef EMPTY_ARRAY_SIZE # define EMPTY_ARRAY_SIZE 1 # undef inline # define inline -#else -# define PEDANTIC 0 #endif /* @@ -403,17 +405,10 @@ /* * Do we have nanoseconds gettimeofday? */ -#if defined(HAVE_GETTIMEOFDAY) || defined(WIN32) +#if defined(HAVE_GETTIMEOFDAY) || defined(_WIN32) #define HAVE_GETTIMEOFDAY_NANOSECONDS 1 #endif -/* - * do we have the MIN() macro? - */ -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif - /* * Do we have the capability to report extended socket errors? */ @@ -441,6 +436,13 @@ #define SOL_IP IPPROTO_IP #endif +/* + * Define type sa_family_t if it isn't defined in the socket headers + */ +#ifndef HAVE_SA_FAMILY_T +typedef unsigned short sa_family_t; +#endif + /* * Disable ESEC */ @@ -468,26 +470,16 @@ /* * Directory separation char */ -#ifdef WIN32 +#ifdef _WIN32 #define OS_SPECIFIC_DIRSEP '\\' #else #define OS_SPECIFIC_DIRSEP '/' #endif -/* - * Define a boolean value based - * on Win32 status. - */ -#ifdef WIN32 -#define WIN32_0_1 1 -#else -#define WIN32_0_1 0 -#endif - /* * Our socket descriptor type. */ -#ifdef WIN32 +#ifdef _WIN32 #define SOCKET_UNDEFINED (INVALID_SOCKET) typedef SOCKET socket_descriptor_t; #else @@ -518,7 +510,7 @@ socket_defined (const socket_descriptor_t sd) * Do we have point-to-multipoint capability? */ -#if defined(ENABLE_CLIENT_SERVER) && defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) && defined(HAVE_GETTIMEOFDAY_NANOSECONDS) +#if defined(ENABLE_CLIENT_SERVER) && defined(ENABLE_CRYPTO) && defined(HAVE_GETTIMEOFDAY_NANOSECONDS) #define P2MP 1 #else #define P2MP 0 @@ -555,14 +547,14 @@ socket_defined (const socket_descriptor_t sd) /* * Enable external private key */ -#if defined(ENABLE_MANAGEMENT) && defined(ENABLE_SSL) +#if defined(ENABLE_MANAGEMENT) && defined(ENABLE_CRYPTO) #define MANAGMENT_EXTERNAL_KEY #endif -/* Enable PolarSSL RNG prediction resistance support */ -#ifdef ENABLE_CRYPTO_POLARSSL +/* Enable mbed TLS RNG prediction resistance support */ +#ifdef ENABLE_CRYPTO_MBEDTLS #define ENABLE_PREDICTION_RESISTANCE -#endif /* ENABLE_CRYPTO_POLARSSL */ +#endif /* ENABLE_CRYPTO_MBEDTLS */ /* * MANAGEMENT_IN_EXTRA allows the management interface to @@ -588,17 +580,12 @@ socket_defined (const socket_descriptor_t sd) /* * Do we support Unix domain sockets? */ -#if defined(PF_UNIX) && !defined(WIN32) +#if defined(PF_UNIX) && !defined(_WIN32) #define UNIX_SOCK_SUPPORT 1 #else #define UNIX_SOCK_SUPPORT 0 #endif -/* - * Compile the struct buffer_list code - */ -#define ENABLE_BUFFER_LIST - /* * Should we include OCC (options consistency check) code? */ @@ -609,7 +596,7 @@ socket_defined (const socket_descriptor_t sd) /* * Should we include NTLM proxy functionality */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_HTTP_PROXY) +#if defined(ENABLE_CRYPTO) #define NTLM 1 #else #define NTLM 0 @@ -618,33 +605,19 @@ socket_defined (const socket_descriptor_t sd) /* * Should we include proxy digest auth functionality */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_HTTP_PROXY) +#if defined(ENABLE_CRYPTO) #define PROXY_DIGEST_AUTH 1 #else #define PROXY_DIGEST_AUTH 0 #endif -/* - * Should we include code common to all proxy methods? - */ -#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS) -#define GENERAL_PROXY_SUPPORT -#endif - /* * Do we have CryptoAPI capability? */ -#if defined(WIN32) && defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(_WIN32) && defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) #define ENABLE_CRYPTOAPI #endif -/* - * Enable x509-track feature? - */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) && defined (ENABLE_CRYPTO_OPENSSL) -#define ENABLE_X509_TRACK -#endif - /* * Is poll available on this platform? */ @@ -669,15 +642,6 @@ socket_defined (const socket_descriptor_t sd) #define EPOLL 0 #endif -/* - * Should we include http proxy override functionality - */ -#if defined(ENABLE_MANAGEMENT) && defined(ENABLE_HTTP_PROXY) -#define HTTP_PROXY_OVERRIDE 1 -#else -#define HTTP_PROXY_OVERRIDE 0 -#endif - /* * Reduce sensitivity to system clock instability * and backtracks. @@ -719,14 +683,17 @@ socket_defined (const socket_descriptor_t sd) /* * Do we support pushing peer info? */ -#if defined(ENABLE_CRYPTO) && defined(ENABLE_SSL) +#if defined(ENABLE_CRYPTO) #define ENABLE_PUSH_PEER_INFO #endif /* - * Do we support internal client-side NAT? + * Compression support */ -#define ENABLE_CLIENT_NAT +#if defined(ENABLE_LZO) || defined(ENABLE_LZ4) || \ + defined(ENABLE_COMP_STUB) +#define USE_COMP +#endif /* * Enable --memstats option diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c new file mode 100644 index 0000000..d40532e --- /dev/null +++ b/src/openvpn/tls_crypt.c @@ -0,0 +1,254 @@ +/* + * 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) 2016 Fox Crypto B.V. + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#ifdef ENABLE_CRYPTO +#include "crypto.h" +#include "session_id.h" + +#include "tls_crypt.h" + +int tls_crypt_buf_overhead(void) +{ + return packet_id_size (true) + TLS_CRYPT_TAG_SIZE + TLS_CRYPT_BLOCK_SIZE; +} + +void +tls_crypt_init_key (struct key_ctx_bi *key, const char *key_file, + const char *key_inline, bool tls_server) { + const int key_direction = tls_server ? + KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; + + struct key_type kt; + kt.cipher = cipher_kt_get ("AES-256-CTR"); + kt.cipher_length = cipher_kt_key_size (kt.cipher); + kt.digest = md_kt_get ("SHA256"); + kt.hmac_length = md_kt_size (kt.digest); + + if (!kt.cipher) + { + msg (M_FATAL, "ERROR: --tls-crypt requires AES-256-CTR support."); + } + if (!kt.digest) + { + msg (M_FATAL, "ERROR: --tls-crypt requires HMAC-SHA-256 support."); + } + + crypto_read_openvpn_key (&kt, key, key_file, key_inline, key_direction, + "Control Channel Encryption", "tls-crypt"); +} + +void +tls_crypt_adjust_frame_parameters(struct frame *frame) +{ + frame_add_to_extra_frame (frame, tls_crypt_buf_overhead()); + + msg(D_MTU_DEBUG, "%s: Adjusting frame parameters for tls-crypt by %i bytes", + __func__, tls_crypt_buf_overhead()); +} + + +bool +tls_crypt_wrap (const struct buffer *src, struct buffer *dst, + struct crypto_options *opt) { + const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; + struct gc_arena gc; + + /* IV, packet-ID and implicit IV required for this mode. */ + ASSERT (ctx->cipher); + ASSERT (ctx->hmac); + ASSERT (packet_id_initialized(&opt->packet_id)); + ASSERT (hmac_ctx_size(ctx->hmac) == 256/8); + + gc_init (&gc); + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP FROM: %s", + format_hex (BPTR (src), BLEN (src), 80, &gc)); + + /* Get packet ID */ + { + struct packet_id_net pin; + packet_id_alloc_outgoing (&opt->packet_id.send, &pin, true); + packet_id_write (&pin, dst, true, false); + } + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP AD: %s", + format_hex (BPTR (dst), BLEN (dst), 0, &gc)); + + /* Buffer overflow check */ + if (!buf_safe (dst, BLEN (src) + TLS_CRYPT_BLOCK_SIZE + TLS_CRYPT_TAG_SIZE)) + { + msg (D_CRYPT_ERRORS, "TLS-CRYPT WRAP: buffer size error, " + "sc=%d so=%d sl=%d dc=%d do=%d dl=%d", src->capacity, src->offset, + src->len, dst->capacity, dst->offset, dst->len); + goto err; + } + + /* Calculate auth tag and synthetic IV */ + { + uint8_t *tag = NULL; + hmac_ctx_reset (ctx->hmac); + hmac_ctx_update (ctx->hmac, BPTR (dst), BLEN (dst)); + hmac_ctx_update (ctx->hmac, BPTR (src), BLEN (src)); + + ASSERT (tag = buf_write_alloc (dst, TLS_CRYPT_TAG_SIZE)); + hmac_ctx_final (ctx->hmac, tag); + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TAG: %s", + format_hex (tag, TLS_CRYPT_TAG_SIZE, 0, &gc)); + + /* Use the 128 most significant bits of the tag as IV */ + ASSERT (cipher_ctx_reset (ctx->cipher, tag)); + } + + /* Encrypt src */ + { + int outlen = 0; + ASSERT (cipher_ctx_update (ctx->cipher, BEND (dst), &outlen, + BPTR (src), BLEN(src))); + ASSERT (buf_inc_len (dst, outlen)); + ASSERT (cipher_ctx_final (ctx->cipher, BPTR (dst), &outlen)); + ASSERT (buf_inc_len (dst, outlen)); + } + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT WRAP TO: %s", + format_hex (BPTR (dst), BLEN (dst), 80, &gc)); + + gc_free (&gc); + return true; + +err: + crypto_clear_error(); + dst->len = 0; + gc_free (&gc); + return false; +} + +bool +tls_crypt_unwrap (const struct buffer *src, struct buffer *dst, + struct crypto_options *opt) +{ + static const char error_prefix[] = "tls-crypt unwrap error"; + const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; + struct gc_arena gc; + + gc_init (&gc); + + ASSERT (opt); + ASSERT (src->len > 0); + ASSERT (ctx->cipher); + ASSERT (packet_id_initialized (&opt->packet_id) || + (opt->flags & CO_IGNORE_PACKET_ID)); + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT UNWRAP FROM: %s", + format_hex (BPTR (src), BLEN (src), 80, &gc)); + + if (buf_len (src) < TLS_CRYPT_OFF_CT) + { + CRYPT_ERROR ("packet too short"); + } + + /* Decrypt cipher text */ + { + int outlen = 0; + + /* Buffer overflow check (should never fail) */ + if (!buf_safe (dst, BLEN (src) - TLS_CRYPT_OFF_CT + TLS_CRYPT_BLOCK_SIZE)) + { + CRYPT_ERROR ("potential buffer overflow"); + } + + if (!cipher_ctx_reset (ctx->cipher, BPTR (src) + TLS_CRYPT_OFF_TAG)) + { + CRYPT_ERROR ("cipher reset failed"); + } + if (!cipher_ctx_update (ctx->cipher, BPTR (dst), &outlen, + BPTR (src) + TLS_CRYPT_OFF_CT, BLEN (src) - TLS_CRYPT_OFF_CT)) + { + CRYPT_ERROR ("cipher update failed"); + } + ASSERT (buf_inc_len (dst, outlen)); + if (!cipher_ctx_final (ctx->cipher, BPTR(dst), &outlen)) + { + CRYPT_ERROR ("cipher final failed"); + } + ASSERT (buf_inc_len (dst, outlen)); + } + + /* Check authentication */ + { + const uint8_t *tag = BPTR (src) + TLS_CRYPT_OFF_TAG; + uint8_t tag_check[TLS_CRYPT_TAG_SIZE] = { 0 }; + + dmsg (D_PACKET_CONTENT, "TLS-CRYPT UNWRAP AD: %s", + format_hex (BPTR (src), TLS_CRYPT_OFF_TAG, 0, &gc)); + dmsg (D_PACKET_CONTENT, "TLS-CRYPT UNWRAP TO: %s", + format_hex (BPTR (dst), BLEN (dst), 80, &gc)); + + hmac_ctx_reset (ctx->hmac); + hmac_ctx_update (ctx->hmac, BPTR (src), TLS_CRYPT_OFF_TAG); + hmac_ctx_update (ctx->hmac, BPTR (dst), BLEN (dst)); + hmac_ctx_final (ctx->hmac, tag_check); + + if (memcmp_constant_time (tag, tag_check, sizeof(tag_check))) + { + dmsg (D_CRYPTO_DEBUG, "tag : %s", + format_hex (tag, sizeof(tag_check), 0, &gc)); + dmsg (D_CRYPTO_DEBUG, "tag_check: %s", + format_hex (tag_check, sizeof(tag_check), 0, &gc)); + CRYPT_ERROR ("packet authentication failed"); + } + } + + /* Check replay */ + if (!(opt->flags & CO_IGNORE_PACKET_ID)) + { + struct packet_id_net pin; + struct buffer tmp = *src; + ASSERT (buf_advance (&tmp, TLS_CRYPT_OFF_PID)); + ASSERT (packet_id_read (&pin, &tmp, true)); + if (!crypto_check_replay (opt, &pin, error_prefix, &gc)) + { + CRYPT_ERROR ("packet replay"); + } + } + + gc_free (&gc); + return true; + + error_exit: + crypto_clear_error(); + dst->len = 0; + gc_free (&gc); + return false; +} + +#endif /* EMABLE_CRYPTO */ diff --git a/src/openvpn/tls_crypt.h b/src/openvpn/tls_crypt.h new file mode 100644 index 0000000..d1962c9 --- /dev/null +++ b/src/openvpn/tls_crypt.h @@ -0,0 +1,144 @@ +/* + * 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) 2016 Fox Crypto B.V. + * + * 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 + */ + +/** + * @defgroup tls_crypt Control channel encryption (--tls-crypt) + * @ingroup control_tls + * @{ + * + * @par + * Control channel encryption uses a pre-shared static key (like the --tls-auth + * key) to encrypt control channel packets. + * + * @par + * Encrypting control channel packets has three main advantages: + * - It provides more privacy by hiding the certificate used for the TLS + * connection. + * - It is harder to identify OpenVPN traffic as such. + * - It provides "poor-man's" post-quantum security, against attackers who + * will never know the pre-shared key (i.e. no forward secrecy). + * + * @par Specification + * Control channel encryption is based on the SIV construction [0], to achieve + * nonce misuse-resistant authenticated encryption: + * + * @par + * \code{.unparsed} + * msg = control channel plaintext + * header = opcode (1 byte) || session_id (8 bytes) || packet_id (8 bytes) + * Ka = authentication key (256 bits) + * Ke = encryption key (256 bits) + * (Ka and Ke are pre-shared keys, like with --tls-auth) + * + * auth_tag = HMAC-SHA256(Ka, header || msg) + * IV = 128 most-significant bits of auth_tag + * ciph = AES256-CTR(Ke, IV, msg) + * + * output = Header || Tag || Ciph + * \endcode + * + * @par + * This boils down to the following on-the-wire packet format: + * + * @par + * \code{.unparsed} + * - opcode - || - session_id - || - packet_id - || auth_tag || * payload * + * \endcode + * + * @par + * Where + * - XXX - means authenticated, and + * * XXX * means authenticated and encrypted. + */ + +#ifndef TLSCRYPT_H +#define TLSCRYPT_H + +#include "buffer.h" +#include "crypto.h" +#include "session_id.h" + +#define TLS_CRYPT_TAG_SIZE (256/8) +#define TLS_CRYPT_PID_SIZE (sizeof (packet_id_type) + sizeof (net_time_t)) +#define TLS_CRYPT_BLOCK_SIZE (128/8) + +#define TLS_CRYPT_OFF_PID (1 + SID_SIZE) +#define TLS_CRYPT_OFF_TAG (TLS_CRYPT_OFF_PID + TLS_CRYPT_PID_SIZE) +#define TLS_CRYPT_OFF_CT (TLS_CRYPT_OFF_TAG + TLS_CRYPT_TAG_SIZE) + +/** + * Initialize a key_ctx_bi structure for use with --tls-crypt. + * + * @param key The key context to initialize + * @param key_file The file to read the key from (or the inline tag to + * indicate and inline key). + * @param key_inline Array containing (zero-terminated) inline key, or NULL + * if not used. + * @param tls_server Must be set to true is this is a TLS server instance. + */ +void tls_crypt_init_key (struct key_ctx_bi *key, const char *key_file, + const char *key_inline, bool tls_server); + +/** + * Returns the maximum overhead (in bytes) added to the destination buffer by + * tls_crypt_wrap(). + */ +int tls_crypt_buf_overhead(void); + +/** + * Adjust frame parameters for --tls-crypt overhead. + */ +void tls_crypt_adjust_frame_parameters(struct frame *frame); + +/** + * Wrap a control channel packet (both authenticates and encrypts the data). + * + * @param src Data to authenticate and encrypt. + * @param dst Any data present in this buffer is first authenticated, then + * the wrapped packet id and data from the src buffer are appended. + * Must have at least tls_crypt_buf_overhead()+BLEN(src) headroom. + * @param opt The crypto state for this --tls-crypt instance. + * + * @returns true iff wrapping succeeded. + */ +bool tls_crypt_wrap (const struct buffer *src, struct buffer *dst, + struct crypto_options *opt); + +/** + * Unwrap a control channel packet (decrypts, authenticates and performs + * replay checks). + * + * @param src Data to decrypt and authenticate. + * @param dst Returns the decrypted data, if unwrapping was successful. + * @param opt The crypto state for this --tls-crypt instance. + * + * @returns true iff unwrapping succeeded (data authenticated correctly and was + * no replay). + */ +bool tls_crypt_unwrap (const struct buffer *src, struct buffer *dst, + struct crypto_options *opt); + +/** @} */ + +#endif /* TLSCRYPT_H */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index b70410d..77ae72f 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -49,7 +49,13 @@ #include "memdbg.h" -#ifdef WIN32 +#ifdef _WIN32 +#include "openvpn-msg.h" +#endif + +#include + +#ifdef _WIN32 /* #define SIMULATE_DHCP_FAILED */ /* simulate bad DHCP negotiation */ @@ -62,12 +68,70 @@ static void netsh_ifconfig (const struct tuntap_options *to, const in_addr_t ip, const in_addr_t netmask, const unsigned int flags); -static void netsh_command (const struct argv *a, int n); +static void netsh_command (const struct argv *a, int n, int msglevel); static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc); static DWORD get_adapter_index_flexible (const char *name); +static bool +do_address_service (const bool add, const short family, const struct tuntap *tt) +{ + DWORD len; + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new (); + HANDLE pipe = tt->options.msg_channel; + + address_message_t addr = { + .header = { + (add ? msg_add_address : msg_del_address), + sizeof (address_message_t), + 0 }, + .family = family, + .iface = { .index = tt->adapter_index, .name = "" } + }; + + if (addr.iface.index == TUN_ADAPTER_INDEX_INVALID) + { + strncpy (addr.iface.name, tt->actual_name, sizeof (addr.iface.name)); + addr.iface.name[sizeof (addr.iface.name) - 1] = '\0'; + } + + if (addr.family == AF_INET) + { + addr.address.ipv4.s_addr = tt->local; + addr.prefix_len = 32; + } + else + { + addr.address.ipv6 = tt->local_ipv6; + addr.prefix_len = tt->netbits_ipv6; + } + + if (!WriteFile (pipe, &addr, sizeof (addr), &len, NULL) || + !ReadFile (pipe, &ack, sizeof (ack), &len, NULL)) + { + msg (M_WARN, "TUN: could not talk to service: %s [%lu]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + goto out; + } + + if (ack.error_number != NO_ERROR) + { + msg (M_WARN, "TUN: %s address failed using service: %s [status=%u if_index=%lu]", + (add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc), + ack.error_number, addr.iface.index); + goto out; + } + + ret = true; + +out: + gc_free (&gc); + return ret; +} + #endif #ifdef TARGET_SOLARIS @@ -134,7 +198,7 @@ guess_tuntap_dev (const char *dev, const char *dev_node, struct gc_arena *gc) { -#ifdef WIN32 +#ifdef _WIN32 const int dt = dev_type_enum (dev, dev_type); if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP) { @@ -357,7 +421,7 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_READ) ? "R" : "r"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->reads)); #endif @@ -366,7 +430,7 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->writes)); #endif @@ -455,8 +519,8 @@ init_tun (const char *dev, /* --dev option */ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */ int ifconfig_ipv6_netbits_parm, const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */ - in_addr_t local_public, - in_addr_t remote_public, + struct addrinfo *local_public, + struct addrinfo *remote_public, const bool strict_warn, struct env_set *es) { @@ -507,6 +571,7 @@ init_tun (const char *dev, /* --dev option */ */ if (strict_warn) { + struct addrinfo *curele; ifconfig_sanity_check (tt->type == DEV_TYPE_TUN, tt->remote_netmask, tt->topology); /* @@ -514,17 +579,23 @@ init_tun (const char *dev, /* --dev option */ * make sure they do not clash with our virtual subnet. */ - check_addr_clash ("local", + for(curele=local_public;curele;curele=curele->ai_next) { + if(curele->ai_family == AF_INET) + check_addr_clash ("local", tt->type, - local_public, + ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr, tt->local, tt->remote_netmask); + } - check_addr_clash ("remote", - tt->type, - remote_public, - tt->local, - tt->remote_netmask); + for (curele=remote_public;curele;curele=curele->ai_next) { + if (curele->ai_family == AF_INET) + check_addr_clash ("remote", + tt->type, + ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr, + tt->local, + tt->remote_netmask); + } if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)) check_subnet_conflict (tt->local, tt->remote_netmask, "TUN/TAP adapter"); @@ -540,6 +611,21 @@ init_tun (const char *dev, /* --dev option */ tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask); } +#ifdef _WIN32 + /* + * Make sure that both ifconfig addresses are part of the + * same .252 subnet. + */ + if (tun) + { + verify_255_255_255_252 (tt->local, tt->remote_netmask); + tt->adapter_netmask = ~3; + } + else + { + tt->adapter_netmask = tt->remote_netmask; + } +#endif tt->did_ifconfig_setup = true; } @@ -579,7 +665,7 @@ init_tun_post (struct tuntap *tt, const struct tuntap_options *options) { tt->options = *options; -#ifdef WIN32 +#ifdef _WIN32 overlapped_io_init (&tt->reads, frame, FALSE, true); overlapped_io_init (&tt->writes, frame, TRUE, true); tt->rw_handle.read = tt->reads.overlapped.hEvent; @@ -588,7 +674,7 @@ init_tun_post (struct tuntap *tt, #endif } -#if defined(WIN32) || \ +#if defined(_WIN32) || \ defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD) /* some of the platforms will auto-add a "network route" pointing @@ -601,12 +687,12 @@ void add_route_connected_v6_net(struct tuntap * tt, { struct route_ipv6 r6; - r6.defined = true; + CLEAR(r6); r6.network = tt->local_ipv6; r6.netbits = tt->netbits_ipv6; r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ - r6.metric_defined = true; + r6.flags = RT_DEFINED | RT_METRIC_DEFINED; add_route_ipv6 (&r6, tt, 0, es); } @@ -615,17 +701,18 @@ void delete_route_connected_v6_net(struct tuntap * tt, { struct route_ipv6 r6; - r6.defined = true; + CLEAR(r6); r6.network = tt->local_ipv6; r6.netbits = tt->netbits_ipv6; r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ - r6.metric_defined = true; + r6.flags = RT_DEFINED | RT_ADDED | RT_METRIC_DEFINED; delete_route_ipv6 (&r6, tt, 0, es); } #endif -#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) +#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||\ + defined(TARGET_OPENBSD) /* we can't use true subnet mode on tun on all platforms, as that * conflicts with IPv6 (wants to use ND then, which we don't do), * but the OSes want "a remote address that is different from ours" @@ -635,8 +722,8 @@ void delete_route_connected_v6_net(struct tuntap * tt, * is still point to point and no layer 2 resolution is done... */ -const char * -create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) +in_addr_t +create_arbitrary_remote( struct tuntap *tt ) { in_addr_t remote; @@ -644,7 +731,7 @@ create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) if ( remote == tt->local ) remote ++; - return print_in_addr_t (remote, 0, gc); + return remote; } #endif @@ -664,14 +751,11 @@ do_ifconfig (struct tuntap *tt, const char *ifconfig_remote_netmask = NULL; const char *ifconfig_broadcast = NULL; const char *ifconfig_ipv6_local = NULL; - const char *ifconfig_ipv6_remote = NULL; bool do_ipv6 = false; - struct argv argv; - - argv_init (&argv); + struct argv argv = argv_new (); - msg( M_INFO, "do_ifconfig, tt->ipv6=%d, tt->did_ifconfig_ipv6_setup=%d", - tt->ipv6, tt->did_ifconfig_ipv6_setup ); + msg( M_DEBUG, "do_ifconfig, tt->did_ifconfig_ipv6_setup=%d", + tt->did_ifconfig_ipv6_setup ); /* * We only handle TUN/TAP devices here, not --dev null devices. @@ -684,10 +768,9 @@ do_ifconfig (struct tuntap *tt, ifconfig_local = print_in_addr_t (tt->local, 0, &gc); ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if (tt->did_ifconfig_ipv6_setup ) { ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); - ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); do_ipv6 = true; } @@ -703,8 +786,10 @@ do_ifconfig (struct tuntap *tt, management_set_state (management, OPENVPN_STATE_ASSIGN_IP, NULL, - tt->local, - 0); + &tt->local, + &tt->local_ipv6, + NULL, + NULL); } #endif @@ -743,7 +828,7 @@ do_ifconfig (struct tuntap *tt, iproute_path, actual, ifconfig_local, - count_netmask_bits(ifconfig_remote_netmask), + netmask_to_netbits2(tt->remote_netmask), ifconfig_broadcast ); argv_msg (M_INFO, &argv); @@ -799,8 +884,35 @@ do_ifconfig (struct tuntap *tt, tt->did_ifconfig = true; #endif /*ENABLE_IPROUTE*/ -#elif defined(TARGET_SOLARIS) +#elif defined(TARGET_ANDROID) + + if (do_ipv6) { + struct buffer out6 = alloc_buf_gc (64, &gc); + buf_printf (&out6, "%s/%d", ifconfig_ipv6_local,tt->netbits_ipv6); + management_android_control(management, "IFCONFIG6",buf_bptr(&out6)); + } + + struct buffer out = alloc_buf_gc (64, &gc); + + char* top; + switch(tt->topology) { + case TOP_NET30: + top="net30"; + break; + case TOP_P2P: + top="p2p"; + break; + case TOP_SUBNET: + top="subnet"; + break; + default: + top="undef"; + } + + buf_printf (&out, "%s %s %d %s", ifconfig_local, ifconfig_remote_netmask, tun_mtu, top); + management_android_control (management, "IFCONFIG", buf_bptr(&out)); +#elif defined(TARGET_SOLARIS) /* Solaris 2.6 (and 7?) cannot set all parameters in one go... * example: * ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up @@ -862,6 +974,9 @@ do_ifconfig (struct tuntap *tt, if ( tt->type == DEV_TYPE_TUN ) { + const char *ifconfig_ipv6_remote = + print_in6_addr (tt->remote_ipv6, 0, &gc); + argv_printf (&argv, "%s %s inet6 plumb %s/%d %s up", IFCONFIG_PATH, @@ -916,6 +1031,8 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_OPENBSD) + in_addr_t remote_end; /* for "virtual" subnet topology */ + /* * On OpenBSD, tun interfaces are persistent if created with * "ifconfig tunX create", and auto-destroyed if created by @@ -935,12 +1052,13 @@ do_ifconfig (struct tuntap *tt, else if ( tt->topology == TOP_SUBNET ) { + remote_end = create_arbitrary_remote( tt ); argv_printf (&argv, "%s %s %s %s mtu %d netmask %s up -link0", IFCONFIG_PATH, actual, ifconfig_local, - ifconfig_local, + print_in_addr_t (remote_end, 0, &gc), tun_mtu, ifconfig_remote_netmask ); @@ -957,6 +1075,19 @@ do_ifconfig (struct tuntap *tt, ); argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed"); + + /* Add a network route for the local tun interface */ + if (!tun && tt->topology == TOP_SUBNET) + { + struct route_ipv4 r; + CLEAR (r); + r.flags = RT_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = remote_end; + add_route (&r, tt, 0, NULL, es); + } + if ( do_ipv6 ) { argv_printf (&argv, @@ -976,13 +1107,6 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_NETBSD) -/* whether or not NetBSD can do IPv6 can be seen by the availability of - * the TUNSIFHEAD ioctl() - see next TARGET_NETBSD block for more details - */ -#ifdef TUNSIFHEAD -# define NETBSD_MULTI_AF -#endif - if (tun) argv_printf (&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", @@ -1025,7 +1149,6 @@ do_ifconfig (struct tuntap *tt, if ( do_ipv6 ) { -#ifdef NETBSD_MULTI_AF argv_printf (&argv, "%s %s inet6 %s/%d", IFCONFIG_PATH, @@ -1038,10 +1161,6 @@ do_ifconfig (struct tuntap *tt, /* and, hooray, we explicitely need to add a route... */ add_route_connected_v6_net(tt, es); -#else - msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" ); - tt->ipv6 = false; -#endif } tt->did_ifconfig = true; @@ -1126,6 +1245,8 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) + in_addr_t remote_end; /* for "virtual" subnet topology */ + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ if (tun) argv_printf (&argv, @@ -1138,12 +1259,13 @@ do_ifconfig (struct tuntap *tt, ); else if ( tt->topology == TOP_SUBNET ) { + remote_end = create_arbitrary_remote( tt ); argv_printf (&argv, "%s %s %s %s mtu %d netmask %s up", IFCONFIG_PATH, actual, ifconfig_local, - create_arbitrary_remote( tt, &gc ), + print_in_addr_t (remote_end, 0, &gc), tun_mtu, ifconfig_remote_netmask ); @@ -1170,7 +1292,7 @@ do_ifconfig (struct tuntap *tt, r.flags = RT_DEFINED; r.network = tt->local & tt->remote_netmask; r.netmask = tt->remote_netmask; - r.gateway = tt->local; + r.gateway = remote_end; add_route (&r, tt, 0, NULL, es); } @@ -1187,21 +1309,46 @@ do_ifconfig (struct tuntap *tt, openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed"); } -#elif defined (WIN32) +#elif defined(TARGET_AIX) { - /* - * Make sure that both ifconfig addresses are part of the - * same .252 subnet. - */ + /* AIX ifconfig will complain if it can't find ODM path in env */ + struct env_set *aix_es = env_set_create (NULL); + env_set_add( aix_es, "ODMDIR=/etc/objrepos" ); + if (tun) + msg(M_FATAL, "no tun support on AIX (canthappen)"); + + /* example: ifconfig tap0 172.30.1.1 netmask 255.255.254.0 up */ + argv_printf (&argv, + "%s %s %s netmask %s mtu %d up", + IFCONFIG_PATH, + actual, + ifconfig_local, + ifconfig_remote_netmask, + tun_mtu + ); + + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, aix_es, S_FATAL, "AIX ifconfig failed"); + tt->did_ifconfig = true; + + if ( do_ipv6 ) { - verify_255_255_255_252 (tt->local, tt->remote_netmask); - tt->adapter_netmask = ~3; - } - else - { - tt->adapter_netmask = tt->remote_netmask; + argv_printf (&argv, + "%s %s inet6 %s/%d", + IFCONFIG_PATH, + actual, + ifconfig_ipv6_local, + tt->netbits_ipv6 + ); + argv_msg (M_INFO, &argv); + openvpn_execve_check (&argv, aix_es, S_FATAL, "AIX ifconfig inet6 failed"); } + env_set_destroy (aix_es); + } +#elif defined (_WIN32) + { + ASSERT (actual != NULL); switch (tt->options.ip_win32_type) { @@ -1212,9 +1359,6 @@ do_ifconfig (struct tuntap *tt, print_in_addr_t (tt->adapter_netmask, 0, &gc)); break; case IPW32_SET_NETSH: - if (!strcmp (actual, "NULL")) - msg (M_FATAL, "Error: When using --ip-win32 netsh, if you have more than one TAP-Windows adapter, you must also specify --dev-node"); - netsh_ifconfig (&tt->options, actual, tt->local, @@ -1226,39 +1370,28 @@ do_ifconfig (struct tuntap *tt, tt->did_ifconfig = true; } - /* IPv6 always uses "netsh" interface */ if ( do_ipv6 ) { - char * saved_actual; - char iface[64]; - DWORD idx; - - if (!strcmp (actual, "NULL")) - msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Windows adapter, you must also specify --dev-node"); - - idx = get_adapter_index_flexible(actual); - openvpn_snprintf(iface, sizeof(iface), "interface=%lu", idx); - - /* example: netsh interface ipv6 set address interface=42 2001:608:8003::d store=active */ - argv_printf (&argv, - "%s%sc interface ipv6 set address %s %s store=active", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - win32_version_info() == WIN_XP ? actual : iface, - ifconfig_ipv6_local); - netsh_command (&argv, 4); + if (tt->options.msg_channel) + { + do_address_service (true, AF_INET6, tt); + } + else + { + /* example: netsh interface ipv6 set address interface=42 2001:608:8003::d store=active */ + char iface[64]; + openvpn_snprintf(iface, sizeof(iface), "interface=%lu", tt->adapter_index ); + argv_printf (&argv, + "%s%sc interface ipv6 set address %s %s store=active", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + iface, + ifconfig_ipv6_local ); + netsh_command (&argv, 4, M_FATAL); + } /* explicit route needed */ - /* on windows, OpenVPN does ifconfig first, open_tun later, so - * tt->actual_name might not yet be initialized, but routing code - * needs to know interface name - point to "actual", restore later - */ - saved_actual = tt->actual_name; - tt->actual_name = (char*) actual; - /* we use adapter_index in add_route_ipv6 */ - tt->adapter_index = idx; add_route_connected_v6_net(tt, es); - tt->actual_name = saved_actual; } #else msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); @@ -1272,7 +1405,7 @@ static void clear_tuntap (struct tuntap *tuntap) { CLEAR (*tuntap); -#ifdef WIN32 +#ifdef _WIN32 tuntap->hand = NULL; #else tuntap->fd = -1; @@ -1280,7 +1413,6 @@ clear_tuntap (struct tuntap *tuntap) #ifdef TARGET_SOLARIS tuntap->ip_fd = -1; #endif - tuntap->ipv6 = false; } static void @@ -1333,7 +1465,7 @@ write_tun_header (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -1370,20 +1502,15 @@ read_tun_header (struct tuntap* tt, uint8_t *buf, int len) #endif -#ifndef WIN32 +#if !(defined(_WIN32) || defined(TARGET_LINUX)) static void open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, - bool ipv6_explicitly_supported, bool dynamic, - struct tuntap *tt) + bool dynamic, struct tuntap *tt) { char tunname[256]; char dynamic_name[256]; bool dynamic_opened = false; - - if ( tt->ipv6 && ! ipv6_explicitly_supported ) - msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS"); - if (tt->type == DEV_TYPE_NULL) { open_null (tt); @@ -1477,7 +1604,9 @@ open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, tt->actual_name = string_alloc (dynamic_opened ? dynamic_name : dev, NULL); } } +#endif /* !_WIN32 && !TARGET_LINUX */ +#if !defined(_WIN32) static void close_tun_generic (struct tuntap *tt) { @@ -1487,12 +1616,83 @@ close_tun_generic (struct tuntap *tt) free (tt->actual_name); clear_tuntap (tt); } +#endif /* !_WIN32 */ -#endif +#if defined (TARGET_ANDROID) +void +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ +#define ANDROID_TUNNAME "vpnservice-tun" + int i; + struct user_pass up; + struct gc_arena gc = gc_new (); + bool opentun; -#if defined(TARGET_LINUX) + int oldtunfd = tt->fd; + + for (i = 0; i < tt->options.dns_len; ++i) { + management_android_control (management, "DNSSERVER", + print_in_addr_t(tt->options.dns[i], 0, &gc)); + } -#ifdef HAVE_LINUX_IF_TUN_H /* New driver support */ + if(tt->options.domain) + management_android_control (management, "DNSDOMAIN", tt->options.domain); + + int android_method = managment_android_persisttun_action (management); + + /* Android 4.4 workaround */ + if (oldtunfd >=0 && android_method == ANDROID_OPEN_AFTER_CLOSE) + { + close(oldtunfd); + openvpn_sleep(2); + } + + if (oldtunfd >=0 && android_method == ANDROID_KEEP_OLD_TUN) { + /* keep the old fd */ + opentun = true; + } else { + opentun = management_android_control (management, "OPENTUN", dev); + /* Pick up the fd from management interface after calling the + * OPENTUN command */ + tt->fd = management->connection.lastfdreceived; + management->connection.lastfdreceived=-1; + } + + if (oldtunfd>=0 && android_method == ANDROID_OPEN_BEFORE_CLOSE) + close(oldtunfd); + + /* Set the actual name to a dummy name */ + tt->actual_name = string_alloc (ANDROID_TUNNAME, NULL); + + if ((tt->fd < 0) || !opentun) + msg (M_ERR, "ERROR: Cannot open TUN"); + + gc_free (&gc); +} + +void +close_tun (struct tuntap *tt) +{ + if (tt) + { + close_tun_generic (tt); + free (tt); + } +} + +int +write_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return write (tt->fd, buf, len); +} + +int +read_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return read (tt->fd, buf, len); +} + +#elif defined(TARGET_LINUX) #ifndef HAVE_LINUX_SOCKIOS_H #error header file linux/sockios.h required @@ -1533,8 +1733,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu * Process --tun-ipv6 */ CLEAR (ifr); - if (!tt->ipv6) - ifr.ifr_flags = IFF_NO_PI; + ifr.ifr_flags = IFF_NO_PI; #if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN) ifr.ifr_flags |= IFF_ONE_QUEUE; @@ -1615,32 +1814,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu ASSERT (0); } -#endif - -#else - -void -open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) -{ - open_tun_generic (dev, dev_type, dev_node, false, true, tt); -} - -#endif /* HAVE_LINUX_IF_TUN_H */ +#endif /* !PENDANTIC */ #ifdef ENABLE_FEATURE_TUN_PERSIST -/* - * This can be removed in future - * when all systems will use newer - * linux-headers - */ -#ifndef TUNSETOWNER -#define TUNSETOWNER _IOW('T', 204, int) -#endif -#ifndef TUNSETGROUP -#define TUNSETGROUP _IOW('T', 206, int) -#endif - void tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options) { @@ -1686,9 +1863,8 @@ close_tun (struct tuntap *tt) { if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig) { - struct argv argv; + struct argv argv = argv_new (); struct gc_arena gc = gc_new (); - argv_init (&argv); #ifdef ENABLE_IPROUTE if (is_tun_p2p (tt)) @@ -1708,7 +1884,7 @@ close_tun (struct tuntap *tt) iproute_path, tt->actual_name, print_in_addr_t (tt->local, 0, &gc), - count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc)) + netmask_to_netbits2(tt->remote_netmask) ); } #else @@ -1722,7 +1898,7 @@ close_tun (struct tuntap *tt) argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed"); - if (tt->ipv6 && tt->did_ifconfig_ipv6_setup) + if (tt->did_ifconfig_ipv6_setup) { const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); @@ -1759,53 +1935,13 @@ close_tun (struct tuntap *tt) int write_tun (struct tuntap* tt, uint8_t *buf, int len) { - if (tt->ipv6) - { - struct tun_pi pi; - struct iphdr *iph; - struct iovec vect[2]; - int ret; - - iph = (struct iphdr *)buf; - - pi.flags = 0; - - if(iph->version == 6) - pi.proto = htons(OPENVPN_ETH_P_IPV6); - else - pi.proto = htons(OPENVPN_ETH_P_IPV4); - - vect[0].iov_len = sizeof(pi); - vect[0].iov_base = π - vect[1].iov_len = len; - vect[1].iov_base = buf; - - ret = writev(tt->fd, vect, 2); - return(ret - sizeof(pi)); - } - else - return write (tt->fd, buf, len); + return write (tt->fd, buf, len); } int read_tun (struct tuntap* tt, uint8_t *buf, int len) { - if (tt->ipv6) - { - struct iovec vect[2]; - struct tun_pi pi; - int ret; - - vect[0].iov_len = sizeof(pi); - vect[0].iov_base = π - vect[1].iov_len = len; - vect[1].iov_base = buf; - - ret = readv(tt->fd, vect, 2); - return(ret - sizeof(pi)); - } - else - return read (tt->fd, buf, len); + return read (tt->fd, buf, len); } #elif defined(TARGET_SOLARIS) @@ -2009,10 +2145,9 @@ solaris_close_tun (struct tuntap *tt) if (tt) { /* IPv6 interfaces need to be 'manually' de-configured */ - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if ( tt->did_ifconfig_ipv6_setup ) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); argv_printf( &argv, "%s %s inet6 unplumb", IFCONFIG_PATH, tt->actual_name ); argv_msg (M_INFO, &argv); @@ -2075,8 +2210,7 @@ static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6 ) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); if (unplumb_inet6) { @@ -2123,7 +2257,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); /* Enable multicast on the interface */ if (tt->fd >= 0) @@ -2168,12 +2302,11 @@ close_tun (struct tuntap* tt) else if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2217,11 +2350,7 @@ read_tun (struct tuntap *tt, uint8_t *buf, int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { -#ifdef NETBSD_MULTI_AF - open_tun_generic (dev, dev_type, dev_node, true, true, tt); -#else - open_tun_generic (dev, dev_type, dev_node, false, true, tt); -#endif + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0) { @@ -2230,7 +2359,6 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu i = 0; ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */ -#ifdef NETBSD_MULTI_AF if ( tt->type == DEV_TYPE_TUN ) { i = 1; @@ -2239,7 +2367,6 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno)); } } -#endif } } @@ -2260,12 +2387,11 @@ close_tun (struct tuntap *tt) else if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2278,8 +2404,6 @@ close_tun (struct tuntap *tt) } } -#ifdef NETBSD_MULTI_AF - static inline int netbsd_modify_read_write_return (int len) { @@ -2300,7 +2424,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct openvpn_iphdr *) buf; - if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6) + if (OPENVPN_IPH_GET_VER(iph->version_len) == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2335,21 +2459,6 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) return read (tt->fd, buf, len); } -#else /* not NETBSD_MULTI_AF -> older code, IPv4 only */ - -int -write_tun (struct tuntap* tt, uint8_t *buf, int len) -{ - return write (tt->fd, buf, len); -} - -int -read_tun (struct tuntap* tt, uint8_t *buf, int len) -{ - return read (tt->fd, buf, len); -} -#endif /* NETBSD_MULTI_AF */ - #elif defined(TARGET_FREEBSD) static inline int @@ -2364,7 +2473,7 @@ freebsd_modify_read_write_return (int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN) { @@ -2397,12 +2506,11 @@ close_tun (struct tuntap *tt) } else if (tt) /* close and destroy */ { - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2426,7 +2534,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2475,7 +2583,7 @@ dragonfly_modify_read_write_return (int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0) { @@ -2509,7 +2617,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2702,7 +2810,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu { /* No explicit utun and utun failed, try the generic way) */ msg (M_INFO, "Failed to open utun device. Falling back to /dev/tun device"); - open_tun_generic (dev, dev_type, NULL, true, true, tt); + open_tun_generic (dev, dev_type, NULL, true, tt); } else { @@ -2723,7 +2831,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu if (dev_node && strcmp (dev_node, "tun")==0) dev_node=NULL; - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); } } @@ -2733,10 +2841,9 @@ close_tun (struct tuntap* tt) if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if (tt->did_ifconfig_ipv6_setup ) { const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); @@ -2776,7 +2883,137 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) return read (tt->fd, buf, len); } -#elif defined(WIN32) +#elif defined(TARGET_AIX) + +void +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + char tunname[256]; + char dynamic_name[20]; + const char *p; + + if (tt->type == DEV_TYPE_NULL) + { + open_null (tt); + return; + } + + if ( tt->type == DEV_TYPE_TUN) + { + msg(M_FATAL, "no support for 'tun' devices on AIX" ); + } + + if ( strncmp( dev, "tap", 3 ) != 0 || dev_node ) + { + msg(M_FATAL, "'--dev %s' and/or '--dev-node' not supported on AIX, use '--dev tap0', 'tap1', etc.", dev ); + } + + if ( strcmp( dev, "tap" ) == 0 ) /* find first free tap dev */ + { /* (= no /dev/tapN node) */ + int i; + for (i=0; i<99; i++ ) + { + openvpn_snprintf (tunname, sizeof (tunname), "/dev/tap%d", i); + if ( access( tunname, F_OK ) < 0 && errno == ENOENT ) + { break; } + } + if ( i >= 99 ) + msg( M_FATAL, "cannot find unused tap device" ); + + openvpn_snprintf( dynamic_name, sizeof(dynamic_name), "tap%d", i ); + dev = dynamic_name; + } + else /* name given, sanity check */ + { + /* ensure that dev name is "tap+" *only* */ + p = &dev[3]; + while( isdigit(*p) ) p++; + if ( *p != '\0' ) + msg( M_FATAL, "TAP device name must be '--dev tapNNNN'" ); + + openvpn_snprintf (tunname, sizeof (tunname), "/dev/%s", dev); + } + + /* pre-existing device? + */ + if ( access( tunname, F_OK ) < 0 && errno == ENOENT ) + { + + /* tunnel device must be created with 'ifconfig tapN create' + */ + struct argv argv = argv_new (); + struct env_set *es = env_set_create (NULL); + argv_printf (&argv, "%s %s create", IFCONFIG_PATH, dev); + argv_msg (M_INFO, &argv); + env_set_add( es, "ODMDIR=/etc/objrepos" ); + openvpn_execve_check (&argv, es, S_FATAL, "AIX 'create tun interface' failed"); + env_set_destroy (es); + } + else + { + /* we didn't make it, we're not going to break it */ + tt->persistent_if = TRUE; + } + + if ((tt->fd = open (tunname, O_RDWR)) < 0) + { + msg (M_ERR, "Cannot open TAP device '%s'", tunname); + } + + set_nonblock (tt->fd); + set_cloexec (tt->fd); /* don't pass fd to scripts */ + msg (M_INFO, "TUN/TAP device %s opened", tunname); + + /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */ + tt->actual_name = string_alloc(dev, NULL); +} + +/* tap devices need to be manually destroyed on AIX + */ +void +close_tun (struct tuntap* tt) +{ + struct gc_arena gc = gc_new (); + struct argv argv = argv_new (); + struct env_set *es = env_set_create (NULL); + + if (!tt) return; + + /* persistent devices need IP address unconfig, others need destroyal + */ + if (tt->persistent_if) + { + argv_printf (&argv, "%s %s 0.0.0.0 down", + IFCONFIG_PATH, tt->actual_name); + } + else + { + argv_printf (&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); + } + + close_tun_generic (tt); + argv_msg (M_INFO, &argv); + env_set_add( es, "ODMDIR=/etc/objrepos" ); + openvpn_execve_check (&argv, es, 0, "AIX 'destroy tap interface' failed (non-critical)"); + + free(tt); + env_set_destroy (es); +} + +int +write_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return write (tt->fd, buf, len); +} + +int +read_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return read (tt->fd, buf, len); +} + +#elif defined(_WIN32) int tun_read_queue (struct tuntap *tt, int maxsize) @@ -4246,7 +4483,7 @@ dhcp_renew (const struct tuntap *tt) */ static void -netsh_command (const struct argv *a, int n) +netsh_command (const struct argv *a, int n, int msglevel) { int i; for (i = 0; i < n; ++i) @@ -4261,21 +4498,19 @@ netsh_command (const struct argv *a, int n) return; openvpn_sleep (4); } - msg (M_FATAL, "NETSH: command failed"); + msg (msglevel, "NETSH: command failed"); } void ipconfig_register_dns (const struct env_set *es) { - struct argv argv; + struct argv argv = argv_new (); bool status; const char err[] = "ERROR: Windows ipconfig command failed"; msg (D_TUNTAP_INFO, "Start net commands..."); netcmd_semaphore_lock (); - argv_init (&argv); - argv_printf (&argv, "%s%sc stop dnscache", get_win_sys_path(), WIN_NET_PATH_SUFFIX); @@ -4411,7 +4646,7 @@ netsh_ifconfig_options (const char *type, NETSH_PATH_SUFFIX, type, flex_name); - netsh_command (&argv, 2); + netsh_command (&argv, 2, M_FATAL); } /* add new DNS/WINS settings to TAP interface */ @@ -4432,7 +4667,7 @@ netsh_ifconfig_options (const char *type, type, flex_name, print_in_addr_t (addr_list[i], 0, &gc)); - netsh_command (&argv, 2); + netsh_command (&argv, 2, M_FATAL); ++count; } @@ -4507,7 +4742,7 @@ netsh_ifconfig (const struct tuntap_options *to, print_in_addr_t (ip, 0, &gc), print_in_addr_t (netmask, 0, &gc)); - netsh_command (&argv, 4); + netsh_command (&argv, 4, M_FATAL); } } @@ -4543,8 +4778,7 @@ static void netsh_enable_dhcp (const struct tuntap_options *to, const char *actual_name) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); /* example: netsh interface ip set address my-tap dhcp */ argv_printf (&argv, @@ -4553,7 +4787,7 @@ netsh_enable_dhcp (const struct tuntap_options *to, NETSH_PATH_SUFFIX, actual_name); - netsh_command (&argv, 4); + netsh_command (&argv, 4, M_FATAL); argv_reset (&argv); } @@ -4750,10 +4984,43 @@ fork_dhcp_action (struct tuntap *tt) } } +static void +register_dns_service (const struct tuntap *tt) +{ + DWORD len; + HANDLE msg_channel = tt->options.msg_channel; + ack_message_t ack; + struct gc_arena gc = gc_new (); + + message_header_t rdns = { msg_register_dns, sizeof(message_header_t), 0 }; + + if (!WriteFile (msg_channel, &rdns, sizeof (rdns), &len, NULL) || + !ReadFile (msg_channel, &ack, sizeof (ack), &len, NULL)) + { + msg (M_WARN, "Register_dns: could not talk to service: %s [status=0x%lx]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + } + + else if (ack.error_number != NO_ERROR) + { + msg (M_WARN, "Register_dns failed using service: %s [status=0x%x]", + strerror_win32 (ack.error_number, &gc), ack.error_number); + } + + else + msg (M_INFO, "Register_dns request sent to the service"); + + gc_free (&gc); +} + void fork_register_dns_action (struct tuntap *tt) { - if (tt && tt->options.register_dns) + if (tt && tt->options.register_dns && tt->options.msg_channel) + { + register_dns_service (tt); + } + else if (tt && tt->options.register_dns) { struct gc_arena gc = gc_new (); struct buffer cmd = alloc_buf_gc (256, &gc); @@ -4798,7 +5065,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /*netcmd_semaphore_lock ();*/ - msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 ); + msg( M_INFO, "open_tun"); if (tt->type == DEV_TYPE_NULL) { @@ -4924,11 +5191,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /* usage of numeric constants is ugly, but this is really tied to * *this* version of the driver */ - if ( tt->ipv6 && tt->type == DEV_TYPE_TUN && + if (tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] < 8) { - msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6", (int) info[0], (int) info[1] ); - tt->ipv6 = false; + msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will not work. Upgrade your Tap-Win32 driver.", (int) info[0], (int) info[1] ); } /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy @@ -4936,7 +5202,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu if ( tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] == 8) { - msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode", (int) info[0], (int) info[1] ); + msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32 driver.", (int) info[0], (int) info[1] ); } } @@ -5122,13 +5388,35 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /* flush arp cache */ if (index != TUN_ADAPTER_INDEX_INVALID) { - DWORD status; + DWORD status = -1; + + if (tt->options.msg_channel) + { + ack_message_t ack; + flush_neighbors_message_t msg = { + .header = { + msg_flush_neighbors, + sizeof (flush_neighbors_message_t), + 0 }, + .family = AF_INET, + .iface = { .index = index, .name = "" } + }; + + if (!WriteFile (tt->options.msg_channel, &msg, sizeof (msg), &len, NULL) || + !ReadFile (tt->options.msg_channel, &ack, sizeof (ack), &len, NULL)) + msg (M_WARN, "TUN: could not talk to service: %s [%lu]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + + status = ack.error_number; + } + else + status = FlushIpNetTable (index); - if ((status = FlushIpNetTable (index)) == NO_ERROR) + if (status == NO_ERROR) msg (M_INFO, "Successful ARP Flush on interface [%u] %s", (unsigned int)index, device_guid); - else + else if (status != -1) msg (D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", (unsigned int)index, device_guid, @@ -5247,30 +5535,36 @@ close_tun (struct tuntap *tt) if (tt) { - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if ( tt->did_ifconfig_ipv6_setup ) { - const char *ifconfig_ipv6_local; - struct argv argv; - argv_init (&argv); - - /* remove route pointing to interface */ - delete_route_connected_v6_net(tt, NULL); - - /* "store=active" is needed in Windows 8(.1) to delete the - * address we added (pointed out by Cedric Tabary). - */ - - /* netsh interface ipv6 delete address \"%s\" %s */ - ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); - argv_printf (&argv, - "%s%sc interface ipv6 delete address %s %s store=active", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - tt->actual_name, - ifconfig_ipv6_local ); - - netsh_command (&argv, 1); - argv_reset (&argv); + if (tt->options.msg_channel) + { + do_address_service (false, AF_INET6, tt); + } + else + { + const char *ifconfig_ipv6_local; + struct argv argv = argv_new (); + + /* remove route pointing to interface */ + delete_route_connected_v6_net(tt, NULL); + + /* "store=active" is needed in Windows 8(.1) to delete the + * address we added (pointed out by Cedric Tabary). + */ + + /* netsh interface ipv6 delete address \"%s\" %s */ + ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); + argv_printf (&argv, + "%s%sc interface ipv6 delete address %s %s store=active", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + tt->actual_name, + ifconfig_ipv6_local); + + netsh_command (&argv, 1, M_WARN); + argv_reset (&argv); + } } #if 1 if (tt->ipapi_context_defined) @@ -5377,7 +5671,7 @@ ipset2ascii_all (struct gc_arena *gc) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, false, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); } void diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 7089f7c..dedd915 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -25,7 +25,7 @@ #ifndef TUN_H #define TUN_H -#ifdef WIN32 +#ifdef _WIN32 #include #include #endif @@ -38,7 +38,7 @@ #include "proto.h" #include "misc.h" -#ifdef WIN32 +#if defined(_WIN32) || defined(TARGET_ANDROID) #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1) @@ -58,6 +58,10 @@ struct tuntap_options { # define IPW32_SET_N 5 int ip_win32_type; +#ifdef _WIN32 + HANDLE msg_channel; +#endif + /* --ip-win32 dynamic options */ bool dhcp_masq_custom_offset; int dhcp_masq_offset; @@ -135,8 +139,6 @@ struct tuntap bool did_ifconfig_ipv6_setup; bool did_ifconfig; - bool ipv6; - bool persistent_if; /* if existed before, keep on program end */ struct tuntap_options options; /* options set on command line */ @@ -155,7 +157,7 @@ struct tuntap struct in6_addr remote_ipv6; int netbits_ipv6; -#ifdef WIN32 +#ifdef _WIN32 HANDLE hand; struct overlapped_io reads; struct overlapped_io writes; @@ -195,7 +197,7 @@ struct tuntap static inline bool tuntap_defined (const struct tuntap *tt) { -#ifdef WIN32 +#ifdef _WIN32 return tt && tt->hand != NULL; #else return tt && tt->fd >= 0; @@ -232,8 +234,8 @@ struct tuntap *init_tun (const char *dev, /* --dev option */ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 / IPv6 */ int ifconfig_ipv6_netbits_parm, /* --ifconfig parm 1 / bits */ const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 / IPv6 */ - in_addr_t local_public, - in_addr_t remote_public, + struct addrinfo *local_public, + struct addrinfo *remote_public, const bool strict_warn, struct env_set *es); @@ -296,14 +298,31 @@ ifconfig_order(void) return IFCONFIG_AFTER_TUN_OPEN; #elif defined(TARGET_NETBSD) return IFCONFIG_AFTER_TUN_OPEN; -#elif defined(WIN32) +#elif defined(_WIN32) + return IFCONFIG_AFTER_TUN_OPEN; +#elif defined(TARGET_ANDROID) return IFCONFIG_BEFORE_TUN_OPEN; #else return IFCONFIG_DEFAULT; #endif } -#ifdef WIN32 +#define ROUTE_BEFORE_TUN 0 +#define ROUTE_AFTER_TUN 1 +#define ROUTE_ORDER_DEFAULT ROUTE_AFTER_TUN + +static inline int +route_order(void) +{ +#if defined(TARGET_ANDROID) + return ROUTE_BEFORE_TUN; +#else + return ROUTE_ORDER_DEFAULT; +#endif +} + + +#ifdef _WIN32 #define TUN_PASS_BUFFER @@ -457,7 +476,7 @@ tun_standby (struct tuntap *tt) static inline event_t tun_event_handle (const struct tuntap *tt) { -#ifdef WIN32 +#ifdef _WIN32 return &tt->rw_handle; #else return tt->fd; @@ -480,7 +499,7 @@ tun_set (struct tuntap *tt, if (persistent) *persistent = rwflags; } -#ifdef WIN32 +#ifdef _WIN32 if (rwflags & EVENT_READ) tun_read_queue (tt, 0); #endif diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index e17cca1..00bc7ac 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -35,7 +35,7 @@ #include "syshead.h" -#ifdef WIN32 +#ifdef _WIN32 #include "buffer.h" #include "error.h" @@ -43,34 +43,20 @@ #include "sig.h" #include "win32.h" #include "misc.h" +#include "openvpn-msg.h" #include "memdbg.h" -#include "win32_wfp.h" - #ifdef HAVE_VERSIONHELPERS_H #include #else #include "compat-versionhelpers.h" #endif -/* WFP function pointers. Initialized in win_wfp_init_funcs() */ -func_ConvertInterfaceIndexToLuid ConvertInterfaceIndexToLuid = NULL; -func_FwpmEngineOpen0 FwpmEngineOpen0 = NULL; -func_FwpmEngineClose0 FwpmEngineClose0 = NULL; -func_FwpmFilterAdd0 FwpmFilterAdd0 = NULL; -func_FwpmSubLayerAdd0 FwpmSubLayerAdd0 = NULL; -func_FwpmSubLayerDeleteByKey0 FwpmSubLayerDeleteByKey0 = NULL; -func_FwpmFreeMemory0 FwpmFreeMemory0 = NULL; -func_FwpmGetAppIdFromFileName0 FwpmGetAppIdFromFileName0 = NULL; - -/* - * WFP firewall name. - */ -WCHAR * FIREWALL_NAME = L"OpenVPN"; /* GLOBAL */ +#include "block_dns.h" /* - * WFP handle and GUID. + * WFP handle */ static HANDLE m_hEngineHandle = NULL; /* GLOBAL */ @@ -119,7 +105,6 @@ init_win32 (void) } window_title_clear (&window_title); win32_signal_clear (&win32_signal); - netcmd_semaphore_init (); } void @@ -598,7 +583,7 @@ win32_signal_get (struct win32_signal *ws) if (ret) { siginfo_static.signal_received = ret; - siginfo_static.hard = true; + siginfo_static.source = SIG_SOURCE_HARD; } } return ret; @@ -765,6 +750,10 @@ void netcmd_semaphore_lock (void) { const int timeout_seconds = 600; + + if (!netcmd_semaphore.hand) + netcmd_semaphore_init (); + if (!semaphore_lock (&netcmd_semaphore, timeout_seconds * 1000)) msg (M_FATAL, "Cannot lock net command semaphore"); } @@ -773,6 +762,8 @@ void netcmd_semaphore_release (void) { semaphore_release (&netcmd_semaphore); + /* netcmd_semaphore has max count of 1 - safe to close after release */ + semaphore_close (&netcmd_semaphore); } /* @@ -1106,211 +1097,109 @@ win_get_tempdir() return tmpdir; } -bool -win_wfp_init_funcs () +static bool +win_block_dns_service (bool add, int index, const HANDLE pipe) { - /* Initialize all WFP-related function pointers */ - HMODULE iphlpapiHandle; - HMODULE fwpuclntHandle; - - iphlpapiHandle = LoadLibrary("iphlpapi.dll"); - if (iphlpapiHandle == NULL) - { - msg (M_NONFATAL, "Can't load iphlpapi.dll"); - return false; - } + DWORD len; + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new (); - fwpuclntHandle = LoadLibrary("fwpuclnt.dll"); - if (fwpuclntHandle == NULL) - { - msg (M_NONFATAL, "Can't load fwpuclnt.dll"); - return false; - } - - ConvertInterfaceIndexToLuid = (func_ConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHandle, "ConvertInterfaceIndexToLuid"); - FwpmFilterAdd0 = (func_FwpmFilterAdd0)GetProcAddress(fwpuclntHandle, "FwpmFilterAdd0"); - FwpmEngineOpen0 = (func_FwpmEngineOpen0)GetProcAddress(fwpuclntHandle, "FwpmEngineOpen0"); - FwpmEngineClose0 = (func_FwpmEngineClose0)GetProcAddress(fwpuclntHandle, "FwpmEngineClose0"); - FwpmSubLayerAdd0 = (func_FwpmSubLayerAdd0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerAdd0"); - FwpmSubLayerDeleteByKey0 = (func_FwpmSubLayerDeleteByKey0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerDeleteByKey0"); - FwpmFreeMemory0 = (func_FwpmFreeMemory0)GetProcAddress(fwpuclntHandle, "FwpmFreeMemory0"); - FwpmGetAppIdFromFileName0 = (func_FwpmGetAppIdFromFileName0)GetProcAddress(fwpuclntHandle, "FwpmGetAppIdFromFileName0"); - - if (!ConvertInterfaceIndexToLuid || - !FwpmFilterAdd0 || - !FwpmEngineOpen0 || - !FwpmEngineClose0 || - !FwpmSubLayerAdd0 || - !FwpmSubLayerDeleteByKey0 || - !FwpmFreeMemory0 || - !FwpmGetAppIdFromFileName0) - { - msg (M_NONFATAL, "Can't get address for all WFP-related procedures."); - return false; - } + block_dns_message_t data = { + .header = { + (add ? msg_add_block_dns : msg_del_block_dns), + sizeof (block_dns_message_t), + 0 }, + .iface = { .index = index, .name = "" } + }; - return true; -} + if (!WriteFile (pipe, &data, sizeof (data), &len, NULL) || + !ReadFile (pipe, &ack, sizeof (ack), &len, NULL)) + { + msg (M_WARN, "Block_DNS: could not talk to service: %s [%lu]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + goto out; + } -bool -win_wfp_add_filter (HANDLE engineHandle, - const FWPM_FILTER0 *filter, - PSECURITY_DESCRIPTOR sd, - UINT64 *id) -{ - if (FwpmFilterAdd0(engineHandle, filter, sd, id) != ERROR_SUCCESS) + if (ack.error_number != NO_ERROR) { - msg (M_NONFATAL, "Can't add WFP filter"); - return false; + msg (M_WARN, "Block_DNS: %s block dns filters using service failed: %s [status=0x%x if_index=%d]", + (add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc), + ack.error_number, data.iface.index); + goto out; } - return true; + + ret = true; + msg (M_INFO, "%s outside dns using service succeeded.", (add ? "Blocking" : "Unblocking")); +out: + gc_free (&gc); + return ret; } -bool -win_wfp_block_dns (const NET_IFINDEX index) +static void +block_dns_msg_handler (DWORD err, const char *msg) { - FWPM_SESSION0 session = {0}; - FWPM_SUBLAYER0 SubLayer = {0}; - NET_LUID tapluid; - UINT64 filterid; - WCHAR openvpnpath[MAX_PATH]; - FWP_BYTE_BLOB *openvpnblob = NULL; - FWPM_FILTER0 Filter = {0}; - FWPM_FILTER_CONDITION0 Condition[2] = {0}; - - /* Add temporary filters which don't survive reboots or crashes. */ - session.flags = FWPM_SESSION_FLAG_DYNAMIC; - - dmsg (D_LOW, "Opening WFP engine"); + struct gc_arena gc = gc_new (); - if (FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &m_hEngineHandle) != ERROR_SUCCESS) + if (err == 0) { - msg (M_NONFATAL, "Can't open WFP engine"); - return false; + msg (M_INFO, "%s", msg); + } + else + { + msg (M_WARN, "Error in add_block_dns_filters(): %s : %s [status=0x%lx]", + msg, strerror_win32 (err, &gc), err); } - if (UuidCreate(&SubLayer.subLayerKey) != NO_ERROR) - return false; + gc_free (&gc); +} - /* Populate packet filter layer information. */ - SubLayer.displayData.name = FIREWALL_NAME; - SubLayer.displayData.description = FIREWALL_NAME; - SubLayer.flags = 0; - SubLayer.weight = 0x100; +bool +win_wfp_block_dns (const NET_IFINDEX index, const HANDLE msg_channel) +{ + WCHAR openvpnpath[MAX_PATH]; + bool ret = false; + DWORD status; - /* Add packet filter to our interface. */ - dmsg (D_LOW, "Adding WFP sublayer"); - if (FwpmSubLayerAdd0(m_hEngineHandle, &SubLayer, NULL) != ERROR_SUCCESS) + if (msg_channel) { - msg (M_NONFATAL, "Can't add WFP sublayer"); - return false; + dmsg (D_LOW, "Using service to add block dns filters"); + ret = win_block_dns_service (true, index, msg_channel); + goto out; } - dmsg (D_LOW, "Blocking DNS using WFP"); - if (ConvertInterfaceIndexToLuid(index, &tapluid) != NO_ERROR) + status = GetModuleFileNameW (NULL, openvpnpath, sizeof(openvpnpath)); + if (status == 0 || status == sizeof(openvpnpath)) { - msg (M_NONFATAL, "Can't convert interface index to LUID"); - return false; + msg (M_WARN|M_ERRNO, "block_dns: cannot get executable path"); + goto out; } - dmsg (D_LOW, "Tap Luid: %I64d", tapluid.Value); - - /* Get OpenVPN path. */ - GetModuleFileNameW(NULL, openvpnpath, MAX_PATH); - - if (FwpmGetAppIdFromFileName0(openvpnpath, &openvpnblob) != ERROR_SUCCESS) - return false; - - /* Prepare filter. */ - Filter.subLayerKey = SubLayer.subLayerKey; - Filter.displayData.name = FIREWALL_NAME; - Filter.weight.type = FWP_UINT8; - Filter.weight.uint8 = 0xF; - Filter.filterCondition = Condition; - Filter.numFilterConditions = 2; - - /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - Filter.action.type = FWP_ACTION_PERMIT; - - Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; - Condition[0].matchType = FWP_MATCH_EQUAL; - Condition[0].conditionValue.type = FWP_UINT16; - Condition[0].conditionValue.uint16 = 53; - - Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID; - Condition[1].matchType = FWP_MATCH_EQUAL; - Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE; - Condition[1].conditionValue.byteBlob = openvpnblob; - - /* Add filter condition to our interface. */ - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Permit OpenVPN IPv4 DNS) added with ID=%I64d", filterid); - - /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; - - /* Add filter condition to our interface. */ - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Permit OpenVPN IPv6 DNS) added with ID=%I64d", filterid); - - /* Third filter. Block all IPv4 DNS queries. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - Filter.action.type = FWP_ACTION_BLOCK; - Filter.weight.type = FWP_EMPTY; - Filter.numFilterConditions = 1; - - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Block IPv4 DNS) added with ID=%I64d", filterid); - - /* Forth filter. Block all IPv6 DNS queries. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; - - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Block IPv6 DNS) added with ID=%I64d", filterid); - - /* Fifth filter. Permit IPv4 DNS queries from TAP. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - Filter.action.type = FWP_ACTION_PERMIT; - Filter.numFilterConditions = 2; - - Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE; - Condition[1].matchType = FWP_MATCH_EQUAL; - Condition[1].conditionValue.type = FWP_UINT64; - Condition[1].conditionValue.uint64 = &tapluid.Value; - - /* Add filter condition to our interface. */ - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Permit IPv4 DNS queries from TAP) added with ID=%I64d", filterid); - - /* Sixth filter. Permit IPv6 DNS queries from TAP. */ - Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; - - /* Add filter condition to our interface. */ - if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) - goto err; - dmsg (D_LOW, "Filter (Permit IPv6 DNS queries from TAP) added with ID=%I64d", filterid); - - FwpmFreeMemory0((void **)&openvpnblob); - return true; - err: - FwpmFreeMemory0((void **)&openvpnblob); - return false; + status = add_block_dns_filters (&m_hEngineHandle, index, openvpnpath, + block_dns_msg_handler); + ret = (status == 0); + +out: + + return ret; } bool -win_wfp_uninit() +win_wfp_uninit(const HANDLE msg_channel) { dmsg (D_LOW, "Uninitializing WFP"); - if (m_hEngineHandle) { - FwpmEngineClose0(m_hEngineHandle); + + if (msg_channel) + { + msg (D_LOW, "Using service to delete block dns filters"); + win_block_dns_service (false, -1, msg_channel); + } + else + { + delete_block_dns_filters (m_hEngineHandle); m_hEngineHandle = NULL; - } + } + return true; } diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index 0990182..11e42f4 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -22,7 +22,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef WIN32 +#ifdef _WIN32 #ifndef OPENVPN_WIN32_H #define OPENVPN_WIN32_H @@ -271,9 +271,8 @@ const char *win_get_tempdir(); /* Convert a string from UTF-8 to UCS-2 */ WCHAR *wide_string (const char* utf8, struct gc_arena *gc); -bool win_wfp_init_funcs(); -bool win_wfp_block_dns(const NET_IFINDEX index); -bool win_wfp_uninit(); +bool win_wfp_block_dns(const NET_IFINDEX index, const HANDLE msg_channel); +bool win_wfp_uninit(const HANDLE msg_channel); #define WIN_XP 0 #define WIN_VISTA 1 diff --git a/src/openvpn/win32_wfp.h b/src/openvpn/win32_wfp.h deleted file mode 100644 index 7559e5b..0000000 --- a/src/openvpn/win32_wfp.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - This Software is provided under the Zope Public License (ZPL) Version 2.1. - - Copyright (c) 2009, 2010 by the mingw-w64 project - - See the AUTHORS file for the list of contributors to the mingw-w64 project. - - This license has been certified as open source. It has also been designated - as GPL compatible by the Free Software Foundation (FSF). - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions in source code must retain the accompanying copyright - notice, this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the accompanying - copyright notice, this list of conditions, and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - 3. Names of the copyright holders must not be used to endorse or promote - products derived from this software without prior written permission - from the copyright holders. - 4. The right to distribute this software or to use it for any purpose does - not give you the right to use Servicemarks (sm) or Trademarks (tm) of - the copyright holders. Use of them is covered by separate agreement - with the copyright holders. - 5. If any files are modified, you must cause the modified files to carry - prominent notices stating that you changed the files and the date of - any change. - - Disclaimer - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED - OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* - * Windows Filtering Platform (WFP) related prototypes, mostly stripped out of - * mingw-w64. - */ - -/* - * WFP-related defines and GUIDs. - */ - -#ifndef WIN32_WFP_H -#define WIN32_WFP_H - -#include -#include -#include -#include - -#ifndef FWPM_SESSION_FLAG_DYNAMIC -#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001 -#endif - -// c38d57d1-05a7-4c33-904f-7fbceee60e82 -DEFINE_GUID( - FWPM_LAYER_ALE_AUTH_CONNECT_V4, - 0xc38d57d1, - 0x05a7, - 0x4c33, - 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82 -); - -// 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 -DEFINE_GUID( - FWPM_LAYER_ALE_AUTH_CONNECT_V6, - 0x4a72393b, - 0x319f, - 0x44bc, - 0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4 -); - -// d78e1e87-8644-4ea5-9437-d809ecefc971 -DEFINE_GUID( - FWPM_CONDITION_ALE_APP_ID, - 0xd78e1e87, - 0x8644, - 0x4ea5, - 0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71 -); - -// c35a604d-d22b-4e1a-91b4-68f674ee674b -DEFINE_GUID( - FWPM_CONDITION_IP_REMOTE_PORT, - 0xc35a604d, - 0xd22b, - 0x4e1a, - 0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b -); - -// 4cd62a49-59c3-4969-b7f3-bda5d32890a4 -DEFINE_GUID( - FWPM_CONDITION_IP_LOCAL_INTERFACE, - 0x4cd62a49, - 0x59c3, - 0x4969, - 0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4 -); - -/* From fwptypes.h */ - -#define FWP_ACTION_FLAG_TERMINATING (0x00001000) -#define FWP_ACTION_FLAG_NON_TERMINATING (0x00002000) - -#define FWP_ACTION_BLOCK (0x1 | FWP_ACTION_FLAG_TERMINATING) -#define FWP_ACTION_PERMIT (0x2 | FWP_ACTION_FLAG_TERMINATING) - -typedef UINT32 FWP_ACTION_TYPE; - -typedef enum FWP_DATA_TYPE_ { - FWP_EMPTY = 0, - FWP_UINT8 = 1, - FWP_UINT16 = 2, - FWP_UINT32 = 3, - FWP_UINT64 = 4, - FWP_INT8 = 5, - FWP_INT16 = 6, - FWP_INT32 = 7, - FWP_INT64 = 8, - FWP_FLOAT = 9, - FWP_DOUBLE = 10, - FWP_BYTE_ARRAY16_TYPE = 11, - FWP_BYTE_BLOB_TYPE = 12, - FWP_SID = 13, - FWP_SECURITY_DESCRIPTOR_TYPE = 14, - FWP_TOKEN_INFORMATION_TYPE = 15, - FWP_TOKEN_ACCESS_INFORMATION_TYPE = 16, - FWP_UNICODE_STRING_TYPE = 17, - FWP_BYTE_ARRAY6_TYPE = 18, - FWP_SINGLE_DATA_TYPE_MAX = 0xff, - FWP_V4_ADDR_MASK = 0x100, - FWP_V6_ADDR_MASK = 0x101, - FWP_RANGE_TYPE = 0x102, - FWP_DATA_TYPE_MAX = 0x103 -} FWP_DATA_TYPE; - -typedef enum FWP_MATCH_TYPE_ { - FWP_MATCH_EQUAL = 0, - FWP_MATCH_GREATER = 1, - FWP_MATCH_LESS = 2, - FWP_MATCH_GREATER_OR_EQUAL = 3, - FWP_MATCH_LESS_OR_EQUAL = 4, - FWP_MATCH_RANGE = 5, - FWP_MATCH_FLAGS_ALL_SET = 6, - FWP_MATCH_FLAGS_ANY_SET = 7, - FWP_MATCH_FLAGS_NONE_SET = 8, - FWP_MATCH_EQUAL_CASE_INSENSITIVE = 9, - FWP_MATCH_NOT_EQUAL = 10, - FWP_MATCH_TYPE_MAX = 11 -} FWP_MATCH_TYPE; - -typedef struct FWP_BYTE_ARRAY6_ { - UINT8 byteArray6[6]; -} FWP_BYTE_ARRAY6; - -typedef struct FWP_BYTE_ARRAY16_ { - UINT8 byteArray16[16]; -} FWP_BYTE_ARRAY16; - -typedef struct FWP_BYTE_BLOB_ { - UINT32 size; - UINT8 *data; -} FWP_BYTE_BLOB; - -typedef struct FWP_TOKEN_INFORMATION_ { - ULONG sidCount; - PSID_AND_ATTRIBUTES sids; - ULONG restrictedSidCount; - PSID_AND_ATTRIBUTES restrictedSids; -} FWP_TOKEN_INFORMATION; - -typedef struct FWP_VALUE0_ { - FWP_DATA_TYPE type; - union { - UINT8 uint8; - UINT16 uint16; - UINT32 uint32; - UINT64 *uint64; - INT8 int8; - INT16 int16; - INT32 int32; - INT64 *int64; - float float32; - double *double64; - FWP_BYTE_ARRAY16 *byteArray16; - FWP_BYTE_BLOB *byteBlob; - SID *sid; - FWP_BYTE_BLOB *sd; - FWP_TOKEN_INFORMATION *tokenInformation; - FWP_BYTE_BLOB *tokenAccessInformation; - LPWSTR unicodeString; - FWP_BYTE_ARRAY6 *byteArray6; - }; -} FWP_VALUE0; - -typedef struct FWP_V4_ADDR_AND_MASK_ { - UINT32 addr; - UINT32 mask; -} FWP_V4_ADDR_AND_MASK; - -typedef struct FWP_V6_ADDR_AND_MASK_ { - UINT8 addr[16]; - UINT8 prefixLength; -} FWP_V6_ADDR_AND_MASK; - -typedef struct FWP_RANGE0_ { - FWP_VALUE0 valueLow; - FWP_VALUE0 valueHigh; -} FWP_RANGE0; - -typedef struct FWP_CONDITION_VALUE0_ { - FWP_DATA_TYPE type; - union { - UINT8 uint8; - UINT16 uint16; - UINT32 uint32; - UINT64 *uint64; - INT8 int8; - INT16 int16; - INT32 int32; - INT64 *int64; - float float32; - double *double64; - FWP_BYTE_ARRAY16 *byteArray16; - FWP_BYTE_BLOB *byteBlob; - SID *sid; - FWP_BYTE_BLOB *sd; - FWP_TOKEN_INFORMATION *tokenInformation; - FWP_BYTE_BLOB *tokenAccessInformation; - LPWSTR unicodeString; - FWP_BYTE_ARRAY6 *byteArray6; - FWP_V4_ADDR_AND_MASK *v4AddrMask; - FWP_V6_ADDR_AND_MASK *v6AddrMask; - FWP_RANGE0 *rangeValue; - }; -} FWP_CONDITION_VALUE0; - -typedef struct FWPM_DISPLAY_DATA0_ { - wchar_t *name; - wchar_t *description; -} FWPM_DISPLAY_DATA0; - -/* From fwpmtypes.h */ - -typedef struct FWPM_ACTION0_ { - FWP_ACTION_TYPE type; - union { - GUID filterType; - GUID calloutKey; - }; -} FWPM_ACTION0; - -typedef struct FWPM_SESSION0_ { - GUID sessionKey; - FWPM_DISPLAY_DATA0 displayData; - UINT32 flags; - UINT32 txnWaitTimeoutInMSec; - DWORD processId; - SID *sid; - wchar_t *username; - BOOL kernelMode; -} FWPM_SESSION0; - -typedef struct FWPM_SUBLAYER0_ { - GUID subLayerKey; - FWPM_DISPLAY_DATA0 displayData; - UINT16 flags; - GUID *providerKey; - FWP_BYTE_BLOB providerData; - UINT16 weight; -} FWPM_SUBLAYER0; - -typedef struct FWPM_FILTER_CONDITION0_ { - GUID fieldKey; - FWP_MATCH_TYPE matchType; - FWP_CONDITION_VALUE0 conditionValue; -} FWPM_FILTER_CONDITION0; - -typedef struct FWPM_FILTER0_ { - GUID filterKey; - FWPM_DISPLAY_DATA0 displayData; - UINT32 flags; - GUID *providerKey; - FWP_BYTE_BLOB providerData; - GUID layerKey; - GUID subLayerKey; - FWP_VALUE0 weight; - UINT32 numFilterConditions; - FWPM_FILTER_CONDITION0 *filterCondition; - FWPM_ACTION0 action; - union { - UINT64 rawContext; - GUID providerContextKey; - }; - GUID *reserved; - UINT64 filterId; - FWP_VALUE0 effectiveWeight; -} FWPM_FILTER0; - -/* Typedefs of WFP functions */ - -#define NETIO_STATUS DWORD - -typedef NETIO_STATUS *(WINAPI *func_ConvertInterfaceIndexToLuid)( - NET_IFINDEX InterfaceIndex, - PNET_LUID InterfaceLuid -); - -typedef DWORD *(WINAPI *func_FwpmEngineOpen0)( - const wchar_t *serverName, - UINT32 authnService, - SEC_WINNT_AUTH_IDENTITY_W *authIdentity, - const FWPM_SESSION0 *session, - HANDLE *engineHandle -); - -typedef DWORD *(WINAPI *func_FwpmEngineClose0)( - HANDLE engineHandle -); - -typedef DWORD *(WINAPI *func_FwpmFilterAdd0)( - HANDLE engineHandle, - const FWPM_FILTER0 *filter, - PSECURITY_DESCRIPTOR sd, - UINT64 *id -); - -typedef DWORD *(WINAPI *func_FwpmSubLayerAdd0)( - HANDLE engineHandle, - const FWPM_SUBLAYER0 *subLayer, - PSECURITY_DESCRIPTOR sd -); - -typedef DWORD *(WINAPI *func_FwpmSubLayerDeleteByKey0)( - HANDLE engineHandle, - const GUID *key -); - -typedef void *(WINAPI *func_FwpmFreeMemory0)( - void **p -); - -typedef DWORD *(WINAPI *func_FwpmGetAppIdFromFileName0)( - const wchar_t *fileName, - FWP_BYTE_BLOB **appId -); - -#endif diff --git a/src/openvpnserv/Makefile.am b/src/openvpnserv/Makefile.am index a989c25..3521a34 100644 --- a/src/openvpnserv/Makefile.am +++ b/src/openvpnserv/Makefile.am @@ -17,11 +17,23 @@ EXTRA_DIST = \ openvpnserv.vcxproj \ openvpnserv.vcxproj.filters +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat + if WIN32 sbin_PROGRAMS = openvpnserv +openvpnserv_CFLAGS = \ + -municode -D_UNICODE \ + -UNTDDI_VERSION -U_WIN32_WINNT \ + -D_WIN32_WINNT=_WIN32_WINNT_VISTA +openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 endif openvpnserv_SOURCES = \ - openvpnserv.c \ - service.h service.c \ + common.c \ + automatic.c \ + interactive.c \ + service.c service.h \ + validate.c validate.h \ + $(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \ openvpnserv_resources.rc diff --git a/src/openvpnserv/Makefile.in b/src/openvpnserv/Makefile.in index ed1ee90..74a802b 100644 --- a/src/openvpnserv/Makefile.in +++ b/src/openvpnserv/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -37,17 +37,7 @@ # Required to build Windows resource file VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -110,6 +100,8 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ +DIST_COMMON = $(top_srcdir)/build/ltrc.inc $(srcdir)/Makefile.in \ + $(srcdir)/Makefile.am $(top_srcdir)/depcomp @WIN32_TRUE@sbin_PROGRAMS = openvpnserv$(EXEEXT) subdir = src/openvpnserv ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -122,21 +114,28 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" PROGRAMS = $(sbin_PROGRAMS) -am_openvpnserv_OBJECTS = openvpnserv.$(OBJEXT) service.$(OBJEXT) \ +am_openvpnserv_OBJECTS = openvpnserv-common.$(OBJEXT) \ + openvpnserv-automatic.$(OBJEXT) \ + openvpnserv-interactive.$(OBJEXT) \ + openvpnserv-service.$(OBJEXT) openvpnserv-validate.$(OBJEXT) \ + openvpnserv-block_dns.$(OBJEXT) \ openvpnserv_resources.$(OBJEXT) openvpnserv_OBJECTS = $(am_openvpnserv_OBJECTS) -openvpnserv_LDADD = $(LDADD) +openvpnserv_DEPENDENCIES = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = +openvpnserv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(openvpnserv_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -149,7 +148,7 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -197,8 +196,6 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/ltrc.inc \ - $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -212,6 +209,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -246,25 +244,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -290,8 +294,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -306,6 +308,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -372,9 +379,22 @@ EXTRA_DIST = \ openvpnserv.vcxproj \ openvpnserv.vcxproj.filters +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/openvpn -I$(top_srcdir)/src/compat + +@WIN32_TRUE@openvpnserv_CFLAGS = \ +@WIN32_TRUE@ -municode -D_UNICODE \ +@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \ +@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA + +@WIN32_TRUE@openvpnserv_LDADD = -ladvapi32 -luserenv -liphlpapi -lfwpuclnt -lrpcrt4 -lshlwapi -lnetapi32 -lws2_32 openvpnserv_SOURCES = \ - openvpnserv.c \ - service.h service.c \ + common.c \ + automatic.c \ + interactive.c \ + service.c service.h \ + validate.c validate.h \ + $(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \ openvpnserv_resources.rc all: all-am @@ -393,6 +413,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am_ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpnserv/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/openvpnserv/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -401,7 +422,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; -$(top_srcdir)/build/ltrc.inc $(am__empty): +$(top_srcdir)/build/ltrc.inc: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh @@ -463,7 +484,7 @@ clean-sbinPROGRAMS: openvpnserv$(EXEEXT): $(openvpnserv_OBJECTS) $(openvpnserv_DEPENDENCIES) $(EXTRA_openvpnserv_DEPENDENCIES) @rm -f openvpnserv$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(openvpnserv_OBJECTS) $(openvpnserv_LDADD) $(LIBS) + $(AM_V_CCLD)$(openvpnserv_LINK) $(openvpnserv_OBJECTS) $(openvpnserv_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -471,22 +492,26 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-automatic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-block_dns.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-interactive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-service.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv-validate.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -495,6 +520,90 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +openvpnserv-common.o: common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-common.o -MD -MP -MF $(DEPDIR)/openvpnserv-common.Tpo -c -o openvpnserv-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-common.Tpo $(DEPDIR)/openvpnserv-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='openvpnserv-common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-common.o `test -f 'common.c' || echo '$(srcdir)/'`common.c + +openvpnserv-common.obj: common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-common.obj -MD -MP -MF $(DEPDIR)/openvpnserv-common.Tpo -c -o openvpnserv-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-common.Tpo $(DEPDIR)/openvpnserv-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='openvpnserv-common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-common.obj `if test -f 'common.c'; then $(CYGPATH_W) 'common.c'; else $(CYGPATH_W) '$(srcdir)/common.c'; fi` + +openvpnserv-automatic.o: automatic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-automatic.o -MD -MP -MF $(DEPDIR)/openvpnserv-automatic.Tpo -c -o openvpnserv-automatic.o `test -f 'automatic.c' || echo '$(srcdir)/'`automatic.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-automatic.Tpo $(DEPDIR)/openvpnserv-automatic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='automatic.c' object='openvpnserv-automatic.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-automatic.o `test -f 'automatic.c' || echo '$(srcdir)/'`automatic.c + +openvpnserv-automatic.obj: automatic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-automatic.obj -MD -MP -MF $(DEPDIR)/openvpnserv-automatic.Tpo -c -o openvpnserv-automatic.obj `if test -f 'automatic.c'; then $(CYGPATH_W) 'automatic.c'; else $(CYGPATH_W) '$(srcdir)/automatic.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-automatic.Tpo $(DEPDIR)/openvpnserv-automatic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='automatic.c' object='openvpnserv-automatic.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-automatic.obj `if test -f 'automatic.c'; then $(CYGPATH_W) 'automatic.c'; else $(CYGPATH_W) '$(srcdir)/automatic.c'; fi` + +openvpnserv-interactive.o: interactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-interactive.o -MD -MP -MF $(DEPDIR)/openvpnserv-interactive.Tpo -c -o openvpnserv-interactive.o `test -f 'interactive.c' || echo '$(srcdir)/'`interactive.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-interactive.Tpo $(DEPDIR)/openvpnserv-interactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interactive.c' object='openvpnserv-interactive.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-interactive.o `test -f 'interactive.c' || echo '$(srcdir)/'`interactive.c + +openvpnserv-interactive.obj: interactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-interactive.obj -MD -MP -MF $(DEPDIR)/openvpnserv-interactive.Tpo -c -o openvpnserv-interactive.obj `if test -f 'interactive.c'; then $(CYGPATH_W) 'interactive.c'; else $(CYGPATH_W) '$(srcdir)/interactive.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-interactive.Tpo $(DEPDIR)/openvpnserv-interactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interactive.c' object='openvpnserv-interactive.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-interactive.obj `if test -f 'interactive.c'; then $(CYGPATH_W) 'interactive.c'; else $(CYGPATH_W) '$(srcdir)/interactive.c'; fi` + +openvpnserv-service.o: service.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-service.o -MD -MP -MF $(DEPDIR)/openvpnserv-service.Tpo -c -o openvpnserv-service.o `test -f 'service.c' || echo '$(srcdir)/'`service.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-service.Tpo $(DEPDIR)/openvpnserv-service.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='service.c' object='openvpnserv-service.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-service.o `test -f 'service.c' || echo '$(srcdir)/'`service.c + +openvpnserv-service.obj: service.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-service.obj -MD -MP -MF $(DEPDIR)/openvpnserv-service.Tpo -c -o openvpnserv-service.obj `if test -f 'service.c'; then $(CYGPATH_W) 'service.c'; else $(CYGPATH_W) '$(srcdir)/service.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-service.Tpo $(DEPDIR)/openvpnserv-service.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='service.c' object='openvpnserv-service.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-service.obj `if test -f 'service.c'; then $(CYGPATH_W) 'service.c'; else $(CYGPATH_W) '$(srcdir)/service.c'; fi` + +openvpnserv-validate.o: validate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-validate.o -MD -MP -MF $(DEPDIR)/openvpnserv-validate.Tpo -c -o openvpnserv-validate.o `test -f 'validate.c' || echo '$(srcdir)/'`validate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-validate.Tpo $(DEPDIR)/openvpnserv-validate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='validate.c' object='openvpnserv-validate.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-validate.o `test -f 'validate.c' || echo '$(srcdir)/'`validate.c + +openvpnserv-validate.obj: validate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-validate.obj -MD -MP -MF $(DEPDIR)/openvpnserv-validate.Tpo -c -o openvpnserv-validate.obj `if test -f 'validate.c'; then $(CYGPATH_W) 'validate.c'; else $(CYGPATH_W) '$(srcdir)/validate.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-validate.Tpo $(DEPDIR)/openvpnserv-validate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='validate.c' object='openvpnserv-validate.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-validate.obj `if test -f 'validate.c'; then $(CYGPATH_W) 'validate.c'; else $(CYGPATH_W) '$(srcdir)/validate.c'; fi` + +openvpnserv-block_dns.o: $(top_srcdir)/src/openvpn/block_dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-block_dns.o -MD -MP -MF $(DEPDIR)/openvpnserv-block_dns.Tpo -c -o openvpnserv-block_dns.o `test -f '$(top_srcdir)/src/openvpn/block_dns.c' || echo '$(srcdir)/'`$(top_srcdir)/src/openvpn/block_dns.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-block_dns.Tpo $(DEPDIR)/openvpnserv-block_dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/openvpn/block_dns.c' object='openvpnserv-block_dns.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-block_dns.o `test -f '$(top_srcdir)/src/openvpn/block_dns.c' || echo '$(srcdir)/'`$(top_srcdir)/src/openvpn/block_dns.c + +openvpnserv-block_dns.obj: $(top_srcdir)/src/openvpn/block_dns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -MT openvpnserv-block_dns.obj -MD -MP -MF $(DEPDIR)/openvpnserv-block_dns.Tpo -c -o openvpnserv-block_dns.obj `if test -f '$(top_srcdir)/src/openvpn/block_dns.c'; then $(CYGPATH_W) '$(top_srcdir)/src/openvpn/block_dns.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/openvpn/block_dns.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/openvpnserv-block_dns.Tpo $(DEPDIR)/openvpnserv-block_dns.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/openvpn/block_dns.c' object='openvpnserv-block_dns.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(openvpnserv_CFLAGS) $(CFLAGS) -c -o openvpnserv-block_dns.obj `if test -f '$(top_srcdir)/src/openvpn/block_dns.c'; then $(CYGPATH_W) '$(top_srcdir)/src/openvpn/block_dns.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/openvpn/block_dns.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -708,8 +817,6 @@ uninstall-am: uninstall-sbinPROGRAMS mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS -.PRECIOUS: Makefile - .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" diff --git a/src/openvpnserv/automatic.c b/src/openvpnserv/automatic.c new file mode 100644 index 0000000..aa7618f --- /dev/null +++ b/src/openvpnserv/automatic.c @@ -0,0 +1,415 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This program allows one or more OpenVPN processes to be started + * as a service. To build, you must get the service sample from the + * Platform SDK and replace Simple.c with this file. + * + * You should also apply service.patch to + * service.c and service.h from the Platform SDK service sample. + * + * This code is designed to be built with the mingw compiler. + */ + +#include "service.h" + +#include +#include +#include + +/* bool definitions */ +#define bool int +#define true 1 +#define false 0 + +static SERVICE_STATUS_HANDLE service; +static SERVICE_STATUS status; + +openvpn_service_t automatic_service = { + automatic, + TEXT(PACKAGE_NAME "ServiceLegacy"), + TEXT(PACKAGE_NAME " Legacy Service"), + TEXT(SERVICE_DEPENDENCIES), + SERVICE_DEMAND_START +}; + +struct security_attributes +{ + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; +}; + +/* + * Which registry key in HKLM should + * we get config info from? + */ +#define REG_KEY "SOFTWARE\\" PACKAGE_NAME + +static HANDLE exit_event = NULL; + +/* clear an object */ +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + + +bool +init_security_attributes_allow_all (struct security_attributes *obj) +{ + CLEAR (*obj); + + obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES); + obj->sa.lpSecurityDescriptor = &obj->sd; + obj->sa.bInheritHandle = TRUE; + if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION)) + return false; + if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE)) + return false; + return true; +} + +/* + * This event is initially created in the non-signaled + * state. It will transition to the signaled state when + * we have received a terminate signal from the Service + * Control Manager which will cause an asynchronous call + * of ServiceStop below. + */ +#define EXIT_EVENT_NAME TEXT(PACKAGE "_exit_1") + +HANDLE +create_event (LPCTSTR name, bool allow_all, bool initial_state, bool manual_reset) +{ + if (allow_all) + { + struct security_attributes sa; + if (!init_security_attributes_allow_all (&sa)) + return NULL; + return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name); + } + else + return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name); +} + +void +close_if_open (HANDLE h) +{ + if (h != NULL) + CloseHandle (h); +} + +static bool +match (const WIN32_FIND_DATA *find, LPCTSTR ext) +{ + int i; + + if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + + if (!_tcslen (ext)) + return true; + + i = _tcslen (find->cFileName) - _tcslen (ext) - 1; + if (i < 1) + return false; + + return find->cFileName[i] == '.' && !_tcsicmp (find->cFileName + i + 1, ext); +} + +/* + * Modify the extension on a filename. + */ +static bool +modext (LPTSTR dest, int size, LPCTSTR src, LPCTSTR newext) +{ + int i; + + if (size > 0 && (_tcslen (src) + 1) <= size) + { + _tcscpy (dest, src); + dest [size - 1] = TEXT('\0'); + i = _tcslen (dest); + while (--i >= 0) + { + if (dest[i] == TEXT('\\')) + break; + if (dest[i] == TEXT('.')) + { + dest[i] = TEXT('\0'); + break; + } + } + if (_tcslen (dest) + _tcslen(newext) + 2 <= size) + { + _tcscat (dest, TEXT(".")); + _tcscat (dest, newext); + return true; + } + dest[0] = TEXT('\0'); + } + return false; +} + +static DWORD WINAPI +ServiceCtrlAutomatic (DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx) +{ + SERVICE_STATUS *status = ctx; + switch (ctrl_code) + { + case SERVICE_CONTROL_STOP: + status->dwCurrentState = SERVICE_STOP_PENDING; + ReportStatusToSCMgr (service, status); + if (exit_event) + SetEvent (exit_event); + return NO_ERROR; + + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } +} + + +VOID WINAPI +ServiceStartAutomatic (DWORD dwArgc, LPTSTR *lpszArgv) +{ + DWORD error = NO_ERROR; + settings_t settings; + + service = RegisterServiceCtrlHandlerEx (automatic_service.name, ServiceCtrlAutomatic, &status); + if (!service) + return; + + status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwServiceSpecificExitCode = NO_ERROR; + status.dwWin32ExitCode = NO_ERROR; + status.dwWaitHint = 3000; + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #1 failed")); + goto finish; + } + + /* + * Create our exit event + */ + exit_event = create_event (EXIT_EVENT_NAME, false, false, true); + if (!exit_event) + { + MsgToEventLog (M_ERR, TEXT("CreateEvent failed")); + goto finish; + } + + /* + * If exit event is already signaled, it means we were not + * shut down properly. + */ + if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT) + { + MsgToEventLog (M_ERR, TEXT("Exit event is already signaled -- we were not shut down properly")); + goto finish; + } + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #2 failed")); + goto finish; + } + + /* + * Read info from registry in key HKLM\SOFTWARE\OpenVPN + */ + error = GetOpenvpnSettings (&settings); + if (error != ERROR_SUCCESS) + goto finish; + + /* + * Instantiate an OpenVPN process for each configuration + * file found. + */ + { + WIN32_FIND_DATA find_obj; + HANDLE find_handle; + BOOL more_files; + TCHAR find_string[MAX_PATH]; + + openvpn_sntprintf (find_string, MAX_PATH, TEXT("%s\\*"), settings.config_dir); + + find_handle = FindFirstFile (find_string, &find_obj); + if (find_handle == INVALID_HANDLE_VALUE) + { + MsgToEventLog (M_ERR, TEXT("Cannot get configuration file list using: %s"), find_string); + goto finish; + } + + /* + * Loop over each config file + */ + do { + HANDLE log_handle = NULL; + STARTUPINFO start_info; + PROCESS_INFORMATION proc_info; + struct security_attributes sa; + TCHAR log_file[MAX_PATH]; + TCHAR log_path[MAX_PATH]; + TCHAR command_line[256]; + + CLEAR (start_info); + CLEAR (proc_info); + CLEAR (sa); + + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr #3 failed")); + FindClose (find_handle); + goto finish; + } + + /* does file have the correct type and extension? */ + if (match (&find_obj, settings.ext_string)) + { + /* get log file pathname */ + if (!modext (log_file, _countof (log_file), find_obj.cFileName, TEXT("log"))) + { + MsgToEventLog (M_ERR, TEXT("Cannot construct logfile name based on: %s"), find_obj.cFileName); + FindClose (find_handle); + goto finish; + } + openvpn_sntprintf (log_path, _countof (log_path), + TEXT("%s\\%s"), settings.log_dir, log_file); + + /* construct command line */ + openvpn_sntprintf (command_line, _countof (command_line), TEXT(PACKAGE " --service %s 1 --config \"%s\""), + EXIT_EVENT_NAME, + find_obj.cFileName); + + /* Make security attributes struct for logfile handle so it can + be inherited. */ + if (!init_security_attributes_allow_all (&sa)) + { + error = MsgToEventLog (M_SYSERR, TEXT("InitializeSecurityDescriptor start_" PACKAGE " failed")); + goto finish; + } + + /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */ + log_handle = CreateFile (log_path, + GENERIC_WRITE, + FILE_SHARE_READ, + &sa.sa, + settings.append ? OPEN_ALWAYS : CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (log_handle == INVALID_HANDLE_VALUE) + { + error = MsgToEventLog (M_SYSERR, TEXT("Cannot open logfile: %s"), log_path); + FindClose (find_handle); + goto finish; + } + + /* append to logfile? */ + if (settings.append) + { + if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + { + error = MsgToEventLog (M_SYSERR, TEXT("Cannot seek to end of logfile: %s"), log_path); + FindClose (find_handle); + goto finish; + } + } + + /* fill in STARTUPINFO struct */ + GetStartupInfo(&start_info); + start_info.cb = sizeof(start_info); + start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_HIDE; + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdOutput = start_info.hStdError = log_handle; + + /* create an OpenVPN process for one config file */ + if (!CreateProcess(settings.exe_path, + command_line, + NULL, + NULL, + TRUE, + settings.priority | CREATE_NEW_CONSOLE, + NULL, + settings.config_dir, + &start_info, + &proc_info)) + { + error = MsgToEventLog (M_SYSERR, TEXT("CreateProcess failed, exe='%s' cmdline='%s' dir='%s'"), + settings.exe_path, + command_line, + settings.config_dir); + + FindClose (find_handle); + CloseHandle (log_handle); + goto finish; + } + + /* close unneeded handles */ + Sleep (1000); /* try to prevent race if we close logfile + handle before child process DUPs it */ + if (!CloseHandle (proc_info.hProcess) + || !CloseHandle (proc_info.hThread) + || !CloseHandle (log_handle)) + { + error = MsgToEventLog (M_SYSERR, TEXT("CloseHandle failed")); + goto finish; + } + } + + /* more files to process? */ + more_files = FindNextFile (find_handle, &find_obj); + + } while (more_files); + + FindClose (find_handle); + } + + /* we are now fully started */ + status.dwCurrentState = SERVICE_RUNNING; + status.dwWaitHint = 0; + if (!ReportStatusToSCMgr(service, &status)) + { + MsgToEventLog (M_ERR, TEXT("ReportStatusToSCMgr SERVICE_RUNNING failed")); + goto finish; + } + + /* wait for our shutdown signal */ + if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0) + MsgToEventLog (M_ERR, TEXT("wait for shutdown signal failed")); + +finish: + if (exit_event) + CloseHandle (exit_event); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwWin32ExitCode = error; + ReportStatusToSCMgr (service, &status); +} diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c new file mode 100644 index 0000000..dba4724 --- /dev/null +++ b/src/openvpnserv/common.c @@ -0,0 +1,218 @@ +/* + * 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) 2011 Heiko Hund + * + * 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 +#include +/* + * These are necessary due to certain buggy implementations of (v)snprintf, + * that don't guarantee null termination for size > 0. + */ +int +openvpn_vsntprintf (LPTSTR str, size_t size, LPCTSTR format, va_list arglist) +{ + int len = -1; + if (size > 0) + { + len = _vsntprintf (str, size, format, arglist); + str[size - 1] = 0; + } + return (len >= 0 && len < size); +} +int +openvpn_sntprintf (LPTSTR str, size_t size, LPCTSTR format, ...) +{ + va_list arglist; + int len = -1; + if (size > 0) + { + va_start (arglist, format); + len = openvpn_vsntprintf (str, size, format, arglist); + va_end (arglist); + } + return len; +} + +#define REG_KEY TEXT("SOFTWARE\\" PACKAGE_NAME) + +static DWORD +GetRegString (HKEY key, LPCTSTR value, LPTSTR data, DWORD size) +{ + DWORD type; + LONG status = RegQueryValueEx (key, value, NULL, &type, (LPBYTE) data, &size); + + if (status == ERROR_SUCCESS && type != REG_SZ) + status = ERROR_DATATYPE_MISMATCH; + + if (status != ERROR_SUCCESS) + { + SetLastError (status); + return MsgToEventLog (M_SYSERR, TEXT("Error querying registry value: HKLM\\%s\\%s"), REG_KEY, value); + } + + return ERROR_SUCCESS; +} + + +DWORD +GetOpenvpnSettings (settings_t *s) +{ + TCHAR priority[64]; + TCHAR append[2]; + DWORD error; + HKEY key; + + LONG status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_KEY, 0, KEY_READ, &key); + if (status != ERROR_SUCCESS) + { + SetLastError (status); + return MsgToEventLog (M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), REG_KEY); + } + + error = GetRegString (key, TEXT("exe_path"), s->exe_path, sizeof (s->exe_path)); + if (error != ERROR_SUCCESS) + goto out; + + error = GetRegString (key, TEXT("config_dir"), s->config_dir, sizeof (s->config_dir)); + if (error != ERROR_SUCCESS) + goto out; + + error = GetRegString (key, TEXT("config_ext"), s->ext_string, sizeof (s->ext_string)); + if (error != ERROR_SUCCESS) + goto out; + + error = GetRegString (key, TEXT("log_dir"), s->log_dir, sizeof (s->log_dir)); + if (error != ERROR_SUCCESS) + goto out; + + error = GetRegString (key, TEXT("priority"), priority, sizeof (priority)); + if (error != ERROR_SUCCESS) + goto out; + + error = GetRegString (key, TEXT("log_append"), append, sizeof (append)); + if (error != ERROR_SUCCESS) + goto out; + + /* read if present, else use default */ + error = GetRegString (key, TEXT("ovpn_admin_group"), s->ovpn_admin_group, sizeof (s->ovpn_admin_group)); + if (error != ERROR_SUCCESS) + { + openvpn_sntprintf(s->ovpn_admin_group, _countof(s->ovpn_admin_group), OVPN_ADMIN_GROUP); + error = 0; /* this error is not fatal */ + } + /* set process priority */ + if (!_tcsicmp (priority, TEXT("IDLE_PRIORITY_CLASS"))) + s->priority = IDLE_PRIORITY_CLASS; + else if (!_tcsicmp (priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS"))) + s->priority = BELOW_NORMAL_PRIORITY_CLASS; + else if (!_tcsicmp (priority, TEXT("NORMAL_PRIORITY_CLASS"))) + s->priority = NORMAL_PRIORITY_CLASS; + else if (!_tcsicmp (priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS"))) + s->priority = ABOVE_NORMAL_PRIORITY_CLASS; + else if (!_tcsicmp (priority, TEXT("HIGH_PRIORITY_CLASS"))) + s->priority = HIGH_PRIORITY_CLASS; + else + { + SetLastError (ERROR_INVALID_DATA); + error = MsgToEventLog (M_SYSERR, TEXT("Unknown priority name: %s"), priority); + goto out; + } + + /* set log file append/truncate flag */ + if (append[0] == TEXT('0')) + s->append = FALSE; + else if (append[0] == TEXT('1')) + s->append = TRUE; + else + { + SetLastError (ERROR_INVALID_DATA); + error = MsgToEventLog (M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append); + goto out; + } + +out: + RegCloseKey (key); + return error; +} + + +LPCTSTR +GetLastErrorText () +{ + static TCHAR buf[256]; + DWORD len; + LPTSTR tmp = NULL; + + len = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL); + + if (len == 0 || (long) _countof (buf) < (long) len + 14) + buf[0] = TEXT('\0'); + else + { + tmp[_tcslen (tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */ + openvpn_sntprintf (buf, _countof (buf), TEXT("%s (0x%x)"), tmp, GetLastError()); + } + + if (tmp) + LocalFree (tmp); + + return buf; +} + + +DWORD +MsgToEventLog (DWORD flags, LPCTSTR format, ...) +{ + HANDLE hEventSource; + TCHAR msg[2][256]; + DWORD error = 0; + LPCTSTR err_msg = TEXT(""); + va_list arglist; + + if (flags & MSG_FLAGS_SYS_CODE) + { + error = GetLastError (); + err_msg = GetLastErrorText (); + } + + hEventSource = RegisterEventSource (NULL, APPNAME); + if (hEventSource != NULL) + { + openvpn_sntprintf (msg[0], _countof (msg[0]), + TEXT("%s%s: %s"), APPNAME, + (flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg); + + va_start (arglist, format); + openvpn_vsntprintf (msg[1], _countof (msg[1]), format, arglist); + va_end (arglist); + + const TCHAR *mesg[] = { msg[0], msg[1] }; + ReportEvent (hEventSource, flags & MSG_FLAGS_ERROR ? + EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, + 0, 0, NULL, 2, 0, mesg, NULL); + DeregisterEventSource (hEventSource); + } + + return error; +} diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c new file mode 100644 index 0000000..ffaa171 --- /dev/null +++ b/src/openvpnserv/interactive.c @@ -0,0 +1,1652 @@ +/* + * 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) 2012 Heiko Hund + * + * 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 "service.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openvpn-msg.h" +#include "validate.h" +#include "block_dns.h" + +#define IO_TIMEOUT 2000 /*ms*/ + +#define ERROR_OPENVPN_STARTUP 0x20000000 +#define ERROR_STARTUP_DATA 0x20000001 +#define ERROR_MESSAGE_DATA 0x20000002 +#define ERROR_MESSAGE_TYPE 0x20000003 + +static SERVICE_STATUS_HANDLE service; +static SERVICE_STATUS status; +static HANDLE exit_event = NULL; +static settings_t settings; +static HANDLE rdns_semaphore = NULL; +#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */ + + +openvpn_service_t interactive_service = { + interactive, + TEXT(PACKAGE_NAME "ServiceInteractive"), + TEXT(PACKAGE_NAME " Interactive Service"), + TEXT(SERVICE_DEPENDENCIES), + SERVICE_AUTO_START +}; + + +typedef struct { + WCHAR *directory; + WCHAR *options; + WCHAR *std_input; +} STARTUP_DATA; + + +/* Datatype for linked lists */ +typedef struct _list_item { + struct _list_item *next; + LPVOID data; +} list_item_t; + + +/* Datatypes for undo information */ +typedef enum { + address, + route, + block_dns, + _undo_type_max +} undo_type_t; +typedef list_item_t* undo_lists_t[_undo_type_max]; + + +static DWORD +AddListItem (list_item_t **pfirst, LPVOID data) +{ + list_item_t *new_item = malloc (sizeof (list_item_t)); + if (new_item == NULL) + return ERROR_OUTOFMEMORY; + + new_item->next = *pfirst; + new_item->data = data; + + *pfirst = new_item; + return NO_ERROR; +} + +typedef BOOL (*match_fn_t) (LPVOID item, LPVOID ctx); + +static LPVOID +RemoveListItem (list_item_t **pfirst, match_fn_t match, LPVOID ctx) +{ + LPVOID data = NULL; + list_item_t **pnext; + + for (pnext = pfirst; *pnext; pnext = &(*pnext)->next) + { + list_item_t *item = *pnext; + if (!match (item->data, ctx)) + continue; + + /* Found item, remove from the list and free memory */ + *pnext = item->next; + data = item->data; + free (item); + break; + } + return data; +} + + +static HANDLE +CloseHandleEx (LPHANDLE handle) +{ + if (handle && *handle && *handle != INVALID_HANDLE_VALUE) + { + CloseHandle (*handle); + *handle = INVALID_HANDLE_VALUE; + } + return INVALID_HANDLE_VALUE; +} + + +static HANDLE +InitOverlapped (LPOVERLAPPED overlapped) +{ + ZeroMemory (overlapped, sizeof (OVERLAPPED)); + overlapped->hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + return overlapped->hEvent; +} + + +static BOOL +ResetOverlapped (LPOVERLAPPED overlapped) +{ + HANDLE io_event = overlapped->hEvent; + if (!ResetEvent (io_event)) + return FALSE; + ZeroMemory (overlapped, sizeof (OVERLAPPED)); + overlapped->hEvent = io_event; + return TRUE; +} + + +typedef enum { + peek, + read, + write +} async_op_t; + +static DWORD +AsyncPipeOp (async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events) +{ + int i; + BOOL success; + HANDLE io_event; + DWORD res, bytes = 0; + OVERLAPPED overlapped; + LPHANDLE handles = NULL; + + io_event = InitOverlapped (&overlapped); + if (!io_event) + goto out; + + handles = malloc ((count + 1) * sizeof (HANDLE)); + if (!handles) + goto out; + + if (op == write) + success = WriteFile (pipe, buffer, size, NULL, &overlapped); + else + success = ReadFile (pipe, buffer, size, NULL, &overlapped); + if (!success && GetLastError () != ERROR_IO_PENDING && GetLastError () != ERROR_MORE_DATA) + goto out; + + handles[0] = io_event; + for (i = 0; i < count; i++) + handles[i + 1] = events[i]; + + res = WaitForMultipleObjects (count + 1, handles, FALSE, + op == peek ? INFINITE : IO_TIMEOUT); + if (res != WAIT_OBJECT_0) + { + CancelIo (pipe); + goto out; + } + + if (op == peek) + PeekNamedPipe (pipe, NULL, 0, NULL, &bytes, NULL); + else + GetOverlappedResult (pipe, &overlapped, &bytes, TRUE); + +out: + CloseHandleEx (&io_event); + free (handles); + return bytes; +} + +static DWORD +PeekNamedPipeAsync (HANDLE pipe, DWORD count, LPHANDLE events) +{ + return AsyncPipeOp (peek, pipe, NULL, 0, count, events); +} + +static DWORD +ReadPipeAsync (HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events) +{ + return AsyncPipeOp (read, pipe, buffer, size, count, events); +} + +static DWORD +WritePipeAsync (HANDLE pipe, LPVOID data, DWORD size, DWORD count, LPHANDLE events) +{ + return AsyncPipeOp (write, pipe, data, size, count, events); +} + +static VOID +ReturnProcessId (HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events) +{ + const WCHAR msg[] = L"Process ID"; + WCHAR buf[22 + _countof(msg)]; /* 10 chars each for error and PID and 2 for line breaks */ + + /* + * Same format as error messages (3 line string) with error = 0 in + * 0x%08x format, PID on line 2 and a description "Process ID" on line 3 + */ + _snwprintf (buf, _countof(buf), L"0x%08x\n0x%08x\n%s", 0, pid, msg); + buf[_countof(buf) - 1] = '\0'; + + WritePipeAsync (pipe, buf, wcslen (buf) * 2, count, events); +} + +static VOID +ReturnError (HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events) +{ + DWORD result_len; + LPWSTR result = L"0xffffffff\nFormatMessage failed\nCould not return result"; + DWORD_PTR args[] = { + (DWORD_PTR) error, + (DWORD_PTR) func, + (DWORD_PTR) "" + }; + + if (error != ERROR_OPENVPN_STARTUP) + { + FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + 0, error, 0, (LPWSTR) &args[2], 0, NULL); + } + + result_len = FormatMessageW (FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ARGUMENT_ARRAY, + L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0, + (LPWSTR) &result, 0, (va_list*) args); + + WritePipeAsync (pipe, result, wcslen (result) * 2, count, events); +#ifdef UNICODE + MsgToEventLog (MSG_FLAGS_ERROR, result); +#else + MsgToEventLog (MSG_FLAGS_ERROR, "%S", result); +#endif + + if (error != ERROR_OPENVPN_STARTUP) + LocalFree ((LPVOID) args[2]); + if (result_len) + LocalFree (result); +} + + +static VOID +ReturnLastError (HANDLE pipe, LPCWSTR func) +{ + ReturnError (pipe, GetLastError (), func, 1, &exit_event); +} + + +static VOID +ReturnOpenvpnOutput (HANDLE pipe, HANDLE ovpn_output, DWORD count, LPHANDLE events) +{ + WCHAR *wide_output = NULL; + CHAR output[512]; + DWORD size; + + ReadFile (ovpn_output, output, sizeof (output), &size, NULL); + if (size == 0) + return; + + wide_output = malloc ((size) * sizeof (WCHAR)); + if (wide_output) + { + MultiByteToWideChar (CP_UTF8, 0, output, size, wide_output, size); + wide_output[size - 1] = 0; + } + + ReturnError (pipe, ERROR_OPENVPN_STARTUP, wide_output, count, events); + free (wide_output); +} + +/* + * Validate options against a white list. Also check the config_file is + * inside the config_dir. The white list is defined in validate.c + * Returns true on success + */ +static BOOL +ValidateOptions (HANDLE pipe, const WCHAR *workdir, const WCHAR *options) +{ + WCHAR **argv; + int argc; + WCHAR buf[256]; + BOOL ret = FALSE; + int i; + const WCHAR *msg1 = L"You have specified a config file location (%s relative to %s)" + " that requires admin approval. This error may be avoided" + " by adding your account to the \"%s\" group"; + + const WCHAR *msg2 = L"You have specified an option (%s) that may be used" + " only with admin approval. This error may be avoided" + " by adding your account to the \"%s\" group"; + + argv = CommandLineToArgvW (options, &argc); + + if (!argv) + { + ReturnLastError (pipe, L"CommandLineToArgvW"); + ReturnError (pipe, ERROR_STARTUP_DATA, L"Cannot validate options", 1, &exit_event); + goto out; + } + + /* Note: argv[0] is the first option */ + if (argc < 1) /* no options */ + { + ret = TRUE; + goto out; + } + + /* + * If only one argument, it is the config file + */ + if (argc == 1) + { + WCHAR *argv_tmp[2] = { L"--config", argv[0] }; + + if (!CheckOption (workdir, 2, argv_tmp, &settings)) + { + snwprintf (buf, _countof(buf), msg1, argv[0], workdir, + settings.ovpn_admin_group); + buf[_countof(buf) - 1] = L'\0'; + ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event); + } + goto out; + } + + for (i = 0; i < argc; ++i) + { + if (!IsOption(argv[i])) + continue; + + if (!CheckOption (workdir, argc-i, &argv[i], &settings)) + { + if (wcscmp(L"--config", argv[i]) == 0 && argc-i > 1) + { + snwprintf (buf, _countof(buf), msg1, argv[i+1], workdir, + settings.ovpn_admin_group); + buf[_countof(buf) - 1] = L'\0'; + ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event); + } + else + { + snwprintf (buf, _countof(buf), msg2, argv[i], + settings.ovpn_admin_group); + buf[_countof(buf) - 1] = L'\0'; + ReturnError (pipe, ERROR_STARTUP_DATA, buf, 1, &exit_event); + } + goto out; + } + } + + /* all options passed */ + ret = TRUE; + +out: + if (argv) + LocalFree (argv); + return ret; +} + +static BOOL +GetStartupData (HANDLE pipe, STARTUP_DATA *sud) +{ + size_t len; + BOOL ret = FALSE; + WCHAR *data = NULL; + DWORD size, bytes, read; + + bytes = PeekNamedPipeAsync (pipe, 1, &exit_event); + if (bytes == 0) + { + MsgToEventLog (M_SYSERR, TEXT("PeekNamedPipeAsync failed")); + ReturnLastError (pipe, L"PeekNamedPipeAsync"); + goto out; + } + + size = bytes / sizeof (*data); + data = malloc (bytes); + if (data == NULL) + { + MsgToEventLog (M_SYSERR, TEXT("malloc failed")); + ReturnLastError (pipe, L"malloc"); + goto out; + } + + read = ReadPipeAsync (pipe, data, bytes, 1, &exit_event); + if (bytes != read) + { + MsgToEventLog (M_SYSERR, TEXT("ReadPipeAsync failed")); + ReturnLastError (pipe, L"ReadPipeAsync"); + goto out; + } + + if (data[size - 1] != 0) + { + MsgToEventLog (M_ERR, TEXT("Startup data is not NULL terminated")); + ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event); + goto out; + } + + sud->directory = data; + len = wcslen (sud->directory) + 1; + size -= len; + if (size <= 0) + { + MsgToEventLog (M_ERR, TEXT("Startup data ends at working directory")); + ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event); + goto out; + } + + sud->options = sud->directory + len; + len = wcslen (sud->options) + 1; + size -= len; + if (size <= 0) + { + MsgToEventLog (M_ERR, TEXT("Startup data ends at command line options")); + ReturnError (pipe, ERROR_STARTUP_DATA, L"GetStartupData", 1, &exit_event); + goto out; + } + + sud->std_input = sud->options + len; + data = NULL; /* don't free data */ + ret = TRUE; + +out: + free (data); + return ret; +} + + +static VOID +FreeStartupData (STARTUP_DATA *sud) +{ + free (sud->directory); +} + + +static SOCKADDR_INET +sockaddr_inet (short family, inet_address_t *addr) +{ + SOCKADDR_INET sa_inet; + ZeroMemory (&sa_inet, sizeof (sa_inet)); + sa_inet.si_family = family; + if (family == AF_INET) + sa_inet.Ipv4.sin_addr = addr->ipv4; + else if (family == AF_INET6) + sa_inet.Ipv6.sin6_addr = addr->ipv6; + return sa_inet; +} + +static DWORD +InterfaceLuid (const char *iface_name, PNET_LUID luid) +{ + NETIO_STATUS status; + LPWSTR wide_name; + int n; + + typedef NETIO_STATUS WINAPI (*ConvertInterfaceAliasToLuidFn) (LPCWSTR, PNET_LUID); + static ConvertInterfaceAliasToLuidFn ConvertInterfaceAliasToLuid = NULL; + if (!ConvertInterfaceAliasToLuid) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + ConvertInterfaceAliasToLuid = (ConvertInterfaceAliasToLuidFn) GetProcAddress (iphlpapi, "ConvertInterfaceAliasToLuid"); + if (!ConvertInterfaceAliasToLuid) + return GetLastError (); + } + + n = MultiByteToWideChar (CP_UTF8, 0, iface_name, -1, NULL, 0); + wide_name = malloc (n * sizeof (WCHAR)); + MultiByteToWideChar (CP_UTF8, 0, iface_name, -1, wide_name, n); + status = ConvertInterfaceAliasToLuid (wide_name, luid); + free (wide_name); + + return status; +} + +static BOOL +CmpAddress (LPVOID item, LPVOID address) +{ + return memcmp (item, address, sizeof (MIB_UNICASTIPADDRESS_ROW)) == 0 ? TRUE : FALSE; +} + +static DWORD +DeleteAddress (PMIB_UNICASTIPADDRESS_ROW addr_row) +{ + typedef NETIOAPI_API (*DeleteUnicastIpAddressEntryFn) (const PMIB_UNICASTIPADDRESS_ROW); + static DeleteUnicastIpAddressEntryFn DeleteUnicastIpAddressEntry = NULL; + + if (!DeleteUnicastIpAddressEntry) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + DeleteUnicastIpAddressEntry = (DeleteUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "DeleteUnicastIpAddressEntry"); + if (!DeleteUnicastIpAddressEntry) + return GetLastError (); + } + + return DeleteUnicastIpAddressEntry (addr_row); +} + +static DWORD +HandleAddressMessage (address_message_t *msg, undo_lists_t *lists) +{ + DWORD err; + PMIB_UNICASTIPADDRESS_ROW addr_row; + BOOL add = msg->header.type == msg_add_address; + + typedef NETIOAPI_API (*CreateUnicastIpAddressEntryFn) (const PMIB_UNICASTIPADDRESS_ROW); + typedef NETIOAPI_API (*InitializeUnicastIpAddressEntryFn) (PMIB_UNICASTIPADDRESS_ROW); + static CreateUnicastIpAddressEntryFn CreateUnicastIpAddressEntry = NULL; + static InitializeUnicastIpAddressEntryFn InitializeUnicastIpAddressEntry = NULL; + + if (!CreateUnicastIpAddressEntry || !InitializeUnicastIpAddressEntry) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + CreateUnicastIpAddressEntry = (CreateUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "CreateUnicastIpAddressEntry"); + if (!CreateUnicastIpAddressEntry) + return GetLastError (); + + InitializeUnicastIpAddressEntry = (InitializeUnicastIpAddressEntryFn) GetProcAddress (iphlpapi, "InitializeUnicastIpAddressEntry"); + if (!InitializeUnicastIpAddressEntry) + return GetLastError (); + } + + addr_row = malloc (sizeof (*addr_row)); + if (addr_row == NULL) + return ERROR_OUTOFMEMORY; + + InitializeUnicastIpAddressEntry (addr_row); + addr_row->Address = sockaddr_inet (msg->family, &msg->address); + addr_row->OnLinkPrefixLength = (UINT8) msg->prefix_len; + + if (msg->iface.index != -1) + { + addr_row->InterfaceIndex = msg->iface.index; + } + else + { + NET_LUID luid; + err = InterfaceLuid (msg->iface.name, &luid); + if (err) + goto out; + addr_row->InterfaceLuid = luid; + } + + if (add) + { + err = CreateUnicastIpAddressEntry (addr_row); + if (err) + goto out; + + err = AddListItem (&(*lists)[address], addr_row); + if (err) + DeleteAddress (addr_row); + } + else + { + err = DeleteAddress (addr_row); + if (err) + goto out; + + free (RemoveListItem (&(*lists)[address], CmpAddress, addr_row)); + } + +out: + if (!add || err) + free (addr_row); + + return err; +} + +static BOOL +CmpRoute (LPVOID item, LPVOID route) +{ + return memcmp (item, route, sizeof (MIB_IPFORWARD_ROW2)) == 0 ? TRUE : FALSE; +} + +static DWORD +DeleteRoute (PMIB_IPFORWARD_ROW2 fwd_row) +{ + typedef NETIOAPI_API (*DeleteIpForwardEntry2Fn) (PMIB_IPFORWARD_ROW2); + static DeleteIpForwardEntry2Fn DeleteIpForwardEntry2 = NULL; + + if (!DeleteIpForwardEntry2) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + DeleteIpForwardEntry2 = (DeleteIpForwardEntry2Fn) GetProcAddress (iphlpapi, "DeleteIpForwardEntry2"); + if (!DeleteIpForwardEntry2) + return GetLastError (); + } + + return DeleteIpForwardEntry2 (fwd_row); +} + +static DWORD +HandleRouteMessage (route_message_t *msg, undo_lists_t *lists) +{ + DWORD err; + PMIB_IPFORWARD_ROW2 fwd_row; + BOOL add = msg->header.type == msg_add_route; + + typedef NETIOAPI_API (*CreateIpForwardEntry2Fn) (PMIB_IPFORWARD_ROW2); + static CreateIpForwardEntry2Fn CreateIpForwardEntry2 = NULL; + + if (!CreateIpForwardEntry2) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + CreateIpForwardEntry2 = (CreateIpForwardEntry2Fn) GetProcAddress (iphlpapi, "CreateIpForwardEntry2"); + if (!CreateIpForwardEntry2) + return GetLastError (); + } + + fwd_row = malloc (sizeof (*fwd_row)); + if (fwd_row == NULL) + return ERROR_OUTOFMEMORY; + + ZeroMemory (fwd_row, sizeof (*fwd_row)); + fwd_row->ValidLifetime = 0xffffffff; + fwd_row->PreferredLifetime = 0xffffffff; + fwd_row->Protocol = MIB_IPPROTO_NETMGMT; + fwd_row->Metric = msg->metric; + fwd_row->DestinationPrefix.Prefix = sockaddr_inet (msg->family, &msg->prefix); + fwd_row->DestinationPrefix.PrefixLength = (UINT8) msg->prefix_len; + fwd_row->NextHop = sockaddr_inet (msg->family, &msg->gateway); + + if (msg->iface.index != -1) + { + fwd_row->InterfaceIndex = msg->iface.index; + } + else if (strlen (msg->iface.name)) + { + NET_LUID luid; + err = InterfaceLuid (msg->iface.name, &luid); + if (err) + goto out; + fwd_row->InterfaceLuid = luid; + } + + if (add) + { + err = CreateIpForwardEntry2 (fwd_row); + if (err) + goto out; + + err = AddListItem (&(*lists)[route], fwd_row); + if (err) + DeleteRoute (fwd_row); + } + else + { + err = DeleteRoute (fwd_row); + if (err) + goto out; + + free (RemoveListItem (&(*lists)[route], CmpRoute, fwd_row)); + } + +out: + if (!add || err) + free (fwd_row); + + return err; +} + + +static DWORD +HandleFlushNeighborsMessage (flush_neighbors_message_t *msg) +{ + typedef NETIOAPI_API (*FlushIpNetTable2Fn) (ADDRESS_FAMILY, NET_IFINDEX); + static FlushIpNetTable2Fn flush_fn = NULL; + + if (msg->family == AF_INET) + return FlushIpNetTable (msg->iface.index); + + if (!flush_fn) + { + HMODULE iphlpapi = GetModuleHandle (TEXT("iphlpapi.dll")); + if (iphlpapi == NULL) + return GetLastError (); + + flush_fn = (FlushIpNetTable2Fn) GetProcAddress (iphlpapi, "FlushIpNetTable2"); + if (!flush_fn) + { + if (GetLastError () == ERROR_PROC_NOT_FOUND) + return WSAEPFNOSUPPORT; + else + return GetLastError (); + } + } + return flush_fn (msg->family, msg->iface.index); +} + +static void +BlockDNSErrHandler (DWORD err, const char *msg) +{ + TCHAR buf[256]; + LPCTSTR err_str; + + if (!err) return; + + err_str = TEXT("Unknown Win32 Error"); + + if (FormatMessage (FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, err, 0, buf, sizeof (buf), NULL)) + { + err_str = buf; + } + +#ifdef UNICODE + MsgToEventLog (M_ERR, L"%S (status = %lu): %s", msg, err, err_str); +#else + MsgToEventLog (M_ERR, "%s (status = %lu): %s", msg, err, err_str); +#endif + +} + +/* Use an always-true match_fn to get the head of the list */ +static BOOL +CmpEngine (LPVOID item, LPVOID any) +{ + return TRUE; +} + +static DWORD +HandleBlockDNSMessage (const block_dns_message_t *msg, undo_lists_t *lists) +{ + DWORD err = 0; + HANDLE engine = NULL; + LPCWSTR exe_path; + +#ifdef UNICODE + exe_path = settings.exe_path; +#else + WCHAR wide_path[MAX_PATH]; + MultiByteToWideChar (CP_UTF8, 0, settings.exe_path, MAX_PATH, wide_path, MAX_PATH); + exe_path = wide_path; +#endif + + if (msg->header.type == msg_add_block_dns) + { + err = add_block_dns_filters (&engine, msg->iface.index, exe_path, BlockDNSErrHandler); + if (!err) + err = AddListItem (&(*lists)[block_dns], engine); + } + else + { + engine = RemoveListItem (&(*lists)[block_dns], CmpEngine, NULL); + if (engine) + { + err = delete_block_dns_filters (engine); + engine = NULL; + } + else + MsgToEventLog (M_ERR, TEXT("No previous block DNS filters to delete")); + } + + if (err && engine) + { + delete_block_dns_filters (engine); + } + + return err; +} + +/* + * Execute a command and return its exit code. If timeout > 0, terminate + * the process if still running after timeout milliseconds. In that case + * the return value is the windows error code WAIT_TIMEOUT = 0x102 + */ +static DWORD +ExecCommand (const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout) +{ + DWORD exit_code; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + DWORD proc_flags = CREATE_NO_WINDOW|CREATE_UNICODE_ENVIRONMENT; + WCHAR *cmdline_dup = NULL; + + ZeroMemory (&si, sizeof(si)); + ZeroMemory (&pi, sizeof(pi)); + + si.cb = sizeof(si); + + /* CreateProcess needs a modifiable cmdline: make a copy */ + cmdline_dup = wcsdup (cmdline); + if ( cmdline_dup && CreateProcessW (argv0, cmdline_dup, NULL, NULL, FALSE, + proc_flags, NULL, NULL, &si, &pi) ) + { + WaitForSingleObject (pi.hProcess, timeout ? timeout : INFINITE); + if (!GetExitCodeProcess (pi.hProcess, &exit_code)) + { + MsgToEventLog (M_SYSERR, TEXT("ExecCommand: Error getting exit_code:")); + exit_code = GetLastError(); + } + else if (exit_code == STILL_ACTIVE) + { + exit_code = WAIT_TIMEOUT; /* Windows error code 0x102 */ + + /* kill without impunity */ + TerminateProcess (pi.hProcess, exit_code); + MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" killed after timeout"), + argv0, cmdline); + } + else if (exit_code) + MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" exited with status = %lu"), + argv0, cmdline, exit_code); + else + MsgToEventLog (M_INFO, TEXT("ExecCommand: \"%s %s\" completed"), argv0, cmdline); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + else + { + exit_code = GetLastError(); + MsgToEventLog (M_SYSERR, TEXT("ExecCommand: could not run \"%s %s\" :"), + argv0, cmdline); + } + + free (cmdline_dup); + return exit_code; +} + +/* + * Entry point for register-dns thread. + */ +static DWORD WINAPI +RegisterDNS (LPVOID unused) +{ + DWORD err; + DWORD i; + WCHAR sys_path[MAX_PATH]; + DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */ + + /* default paths of net and ipconfig commands */ + WCHAR net[MAX_PATH] = L"C:\\Windows\\system32\\net.exe"; + WCHAR ipcfg[MAX_PATH] = L"C:\\Windows\\system32\\ipconfig.exe"; + + struct + { + WCHAR *argv0; + WCHAR *cmdline; + DWORD timeout; + } cmds [] = { + { net, L"net stop dnscache", timeout }, + { net, L"net start dnscache", timeout }, + { ipcfg, L"ipconfig /flushdns", timeout }, + { ipcfg, L"ipconfig /registerdns", timeout }, + }; + int ncmds = sizeof (cmds) / sizeof (cmds[0]); + + HANDLE wait_handles[2] = {rdns_semaphore, exit_event}; + + if(GetSystemDirectory(sys_path, MAX_PATH)) + { + _snwprintf (net, MAX_PATH, L"%s\\%s", sys_path, L"net.exe"); + net[MAX_PATH-1] = L'\0'; + + _snwprintf (ipcfg, MAX_PATH, L"%s\\%s", sys_path, L"ipconfig.exe"); + ipcfg[MAX_PATH-1] = L'\0'; + } + + if (WaitForMultipleObjects (2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0) + { + /* Semaphore locked */ + for (i = 0; i < ncmds; ++i) + { + ExecCommand (cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout); + } + err = 0; + if ( !ReleaseSemaphore (rdns_semaphore, 1, NULL) ) + err = MsgToEventLog (M_SYSERR, TEXT("RegisterDNS: Failed to release regsiter-dns semaphore:")); + } + else + { + MsgToEventLog (M_ERR, TEXT("RegisterDNS: Failed to lock register-dns semaphore")); + err = ERROR_SEM_TIMEOUT; /* Windows error code 0x79 */ + } + return err; +} + +static DWORD +HandleRegisterDNSMessage (void) +{ + DWORD err; + HANDLE thread = NULL; + + /* Delegate this job to a sub-thread */ + thread = CreateThread (NULL, 0, RegisterDNS, NULL, 0, NULL); + + /* + * We don't add these thread handles to the undo list -- the thread and + * processes it spawns are all supposed to terminate or timeout by themselves. + */ + if (thread) + { + err = 0; + CloseHandle (thread); + } + else + err = GetLastError(); + + return err; +} + +static VOID +HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists) +{ + DWORD read; + union { + message_header_t header; + address_message_t address; + route_message_t route; + flush_neighbors_message_t flush_neighbors; + block_dns_message_t block_dns; + } msg; + ack_message_t ack = { + .header = { + .type = msg_acknowledgement, + .size = sizeof (ack), + .message_id = -1 + }, + .error_number = ERROR_MESSAGE_DATA + }; + + read = ReadPipeAsync (pipe, &msg, bytes, count, events); + if (read != bytes || read < sizeof (msg.header) || read != msg.header.size) + goto out; + + ack.header.message_id = msg.header.message_id; + + switch (msg.header.type) + { + case msg_add_address: + case msg_del_address: + if (msg.header.size == sizeof (msg.address)) + ack.error_number = HandleAddressMessage (&msg.address, lists); + break; + + case msg_add_route: + case msg_del_route: + if (msg.header.size == sizeof (msg.route)) + ack.error_number = HandleRouteMessage (&msg.route, lists); + break; + + case msg_flush_neighbors: + if (msg.header.size == sizeof (msg.flush_neighbors)) + ack.error_number = HandleFlushNeighborsMessage (&msg.flush_neighbors); + break; + + case msg_add_block_dns: + case msg_del_block_dns: + if (msg.header.size == sizeof (msg.block_dns)) + ack.error_number = HandleBlockDNSMessage (&msg.block_dns, lists); + break; + + case msg_register_dns: + ack.error_number = HandleRegisterDNSMessage (); + break; + + default: + ack.error_number = ERROR_MESSAGE_TYPE; + MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type); + break; + } + +out: + WritePipeAsync (pipe, &ack, sizeof (ack), count, events); +} + + +static VOID +Undo (undo_lists_t *lists) +{ + undo_type_t type; + for (type = 0; type < _undo_type_max; type++) + { + list_item_t **pnext = &(*lists)[type]; + while (*pnext) + { + list_item_t *item = *pnext; + switch (type) + { + case address: + DeleteAddress (item->data); + break; + + case route: + DeleteRoute (item->data); + break; + + case block_dns: + delete_block_dns_filters (item->data); + item->data = NULL; + break; + } + + /* Remove from the list and free memory */ + *pnext = item->next; + free (item->data); + free (item); + } + } +} + +static DWORD WINAPI +RunOpenvpn (LPVOID p) +{ + HANDLE pipe = p; + HANDLE ovpn_pipe, svc_pipe; + PTOKEN_USER svc_user, ovpn_user; + HANDLE svc_token = NULL, imp_token = NULL, pri_token = NULL; + HANDLE stdin_read = NULL, stdin_write = NULL; + HANDLE stdout_write = NULL; + DWORD pipe_mode, len, exit_code = 0; + STARTUP_DATA sud = { 0, 0, 0 }; + STARTUPINFOW startup_info; + PROCESS_INFORMATION proc_info; + LPVOID user_env = NULL; + TCHAR ovpn_pipe_name[36]; + LPCWSTR exe_path; + WCHAR *cmdline = NULL; + size_t cmdline_size; + undo_lists_t undo_lists; + + SECURITY_ATTRIBUTES inheritable = { + .nLength = sizeof (inheritable), + .lpSecurityDescriptor = NULL, + .bInheritHandle = TRUE + }; + + PACL ovpn_dacl; + EXPLICIT_ACCESS ea[2]; + SECURITY_DESCRIPTOR ovpn_sd; + SECURITY_ATTRIBUTES ovpn_sa = { + .nLength = sizeof (ovpn_sa), + .lpSecurityDescriptor = &ovpn_sd, + .bInheritHandle = FALSE + }; + + ZeroMemory (&ea, sizeof (ea)); + ZeroMemory (&startup_info, sizeof (startup_info)); + ZeroMemory (&undo_lists, sizeof (undo_lists)); + ZeroMemory (&proc_info, sizeof (proc_info)); + + if (!GetStartupData (pipe, &sud)) + goto out; + + if (!InitializeSecurityDescriptor (&ovpn_sd, SECURITY_DESCRIPTOR_REVISION)) + { + ReturnLastError (pipe, L"InitializeSecurityDescriptor"); + goto out; + } + + /* Get SID of user the service is running under */ + if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &svc_token)) + { + ReturnLastError (pipe, L"OpenProcessToken"); + goto out; + } + len = 0; + svc_user = NULL; + while (!GetTokenInformation (svc_token, TokenUser, svc_user, len, &len)) + { + if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + ReturnLastError (pipe, L"GetTokenInformation (service token)"); + goto out; + } + free (svc_user); + svc_user = malloc (len); + if (svc_user == NULL) + { + ReturnLastError (pipe, L"malloc (service token user)"); + goto out; + } + } + if (!IsValidSid (svc_user->User.Sid)) + { + ReturnLastError (pipe, L"IsValidSid (service token user)"); + goto out; + } + + if (!ImpersonateNamedPipeClient (pipe)) + { + ReturnLastError (pipe, L"ImpersonateNamedPipeClient"); + goto out; + } + if (!OpenThreadToken (GetCurrentThread (), TOKEN_ALL_ACCESS, FALSE, &imp_token)) + { + ReturnLastError (pipe, L"OpenThreadToken"); + goto out; + } + len = 0; + ovpn_user = NULL; + while (!GetTokenInformation (imp_token, TokenUser, ovpn_user, len, &len)) + { + if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + ReturnLastError (pipe, L"GetTokenInformation (impersonation token)"); + goto out; + } + free (ovpn_user); + ovpn_user = malloc (len); + if (ovpn_user == NULL) + { + ReturnLastError (pipe, L"malloc (impersonation token user)"); + goto out; + } + } + if (!IsValidSid (ovpn_user->User.Sid)) + { + ReturnLastError (pipe, L"IsValidSid (impersonation token user)"); + goto out; + } + + /* Check user is authorized or options are white-listed */ + if (!IsAuthorizedUser (ovpn_user->User.Sid, &settings) && + !ValidateOptions (pipe, sud.directory, sud.options)) + { + goto out; + } + + /* OpenVPN process DACL entry for access by service and user */ + ea[0].grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL; + ea[0].grfAccessMode = SET_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + ea[0].Trustee.ptstrName = (LPTSTR) svc_user->User.Sid; + ea[1].grfAccessPermissions = READ_CONTROL | SYNCHRONIZE | PROCESS_VM_READ | + SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION; + ea[1].grfAccessMode = SET_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + ea[1].Trustee.ptstrName = (LPTSTR) ovpn_user->User.Sid; + + /* Set owner and DACL of OpenVPN security descriptor */ + if (!SetSecurityDescriptorOwner (&ovpn_sd, svc_user->User.Sid, FALSE)) + { + ReturnLastError (pipe, L"SetSecurityDescriptorOwner"); + goto out; + } + if (SetEntriesInAcl (2, ea, NULL, &ovpn_dacl) != ERROR_SUCCESS) + { + ReturnLastError (pipe, L"SetEntriesInAcl"); + goto out; + } + if (!SetSecurityDescriptorDacl (&ovpn_sd, TRUE, ovpn_dacl, FALSE)) + { + ReturnLastError (pipe, L"SetSecurityDescriptorDacl"); + goto out; + } + + /* Create primary token from impersonation token */ + if (!DuplicateTokenEx (imp_token, TOKEN_ALL_ACCESS, NULL, 0, TokenPrimary, &pri_token)) + { + ReturnLastError (pipe, L"DuplicateTokenEx"); + goto out; + } + + /* use /dev/null for stdout of openvpn (client should use --log for output) */ + stdout_write = CreateFile(_T("NUL"), GENERIC_WRITE, FILE_SHARE_WRITE, + &inheritable, OPEN_EXISTING, 0, NULL); + if (stdout_write == INVALID_HANDLE_VALUE) + { + ReturnLastError (pipe, L"CreateFile for stdout"); + goto out; + } + + if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0) || + !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0)) + { + ReturnLastError (pipe, L"CreatePipe"); + goto out; + } + + openvpn_sntprintf (ovpn_pipe_name, _countof (ovpn_pipe_name), + TEXT("\\\\.\\pipe\\openvpn\\service_%lu"), GetCurrentThreadId ()); + ovpn_pipe = CreateNamedPipe (ovpn_pipe_name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 128, 128, 0, NULL); + if (ovpn_pipe == INVALID_HANDLE_VALUE) + { + ReturnLastError (pipe, L"CreateNamedPipe"); + goto out; + } + + svc_pipe = CreateFile (ovpn_pipe_name, GENERIC_READ | GENERIC_WRITE, 0, + &inheritable, OPEN_EXISTING, 0, NULL); + if (svc_pipe == INVALID_HANDLE_VALUE) + { + ReturnLastError (pipe, L"CreateFile"); + goto out; + } + + pipe_mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState (svc_pipe, &pipe_mode, NULL, NULL)) + { + ReturnLastError (pipe, L"SetNamedPipeHandleState"); + goto out; + } + + cmdline_size = wcslen (sud.options) + 128; + cmdline = malloc (cmdline_size * sizeof (*cmdline)); + if (cmdline == NULL) + { + ReturnLastError (pipe, L"malloc"); + goto out; + } + openvpn_sntprintf (cmdline, cmdline_size, L"openvpn %s --msg-channel %lu", + sud.options, svc_pipe); + + if (!CreateEnvironmentBlock (&user_env, imp_token, FALSE)) + { + ReturnLastError (pipe, L"CreateEnvironmentBlock"); + goto out; + } + + startup_info.cb = sizeof (startup_info); + startup_info.lpDesktop = L"winsta0\\default"; + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = stdin_read; + startup_info.hStdOutput = stdout_write; + startup_info.hStdError = stdout_write; + +#ifdef UNICODE + exe_path = settings.exe_path; +#else + WCHAR wide_path[MAX_PATH]; + MultiByteToWideChar (CP_UTF8, 0, settings.exe_path, MAX_PATH, wide_path, MAX_PATH); + exe_path = wide_path; +#endif + + // TODO: make sure HKCU is correct or call LoadUserProfile() + if (!CreateProcessAsUserW (pri_token, exe_path, cmdline, &ovpn_sa, NULL, TRUE, + settings.priority | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, + user_env, sud.directory, &startup_info, &proc_info)) + { + ReturnLastError (pipe, L"CreateProcessAsUser"); + goto out; + } + + if (!RevertToSelf ()) + { + TerminateProcess (proc_info.hProcess, 1); + ReturnLastError (pipe, L"RevertToSelf"); + goto out; + } + + ReturnProcessId (pipe, proc_info.dwProcessId, 1, &exit_event); + + CloseHandleEx (&stdout_write); + CloseHandleEx (&stdin_read); + CloseHandleEx (&svc_pipe); + + DWORD input_size = WideCharToMultiByte (CP_UTF8, 0, sud.std_input, -1, NULL, 0, NULL, NULL); + LPSTR input = NULL; + if (input_size && (input = malloc (input_size))) + { + DWORD written; + WideCharToMultiByte (CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL); + WriteFile (stdin_write, input, strlen (input), &written, NULL); + free (input); + } + + while (TRUE) + { + DWORD bytes = PeekNamedPipeAsync (ovpn_pipe, 1, &exit_event); + if (bytes == 0) + break; + + HandleMessage (ovpn_pipe, bytes, 1, &exit_event, &undo_lists); + } + + WaitForSingleObject (proc_info.hProcess, IO_TIMEOUT); + GetExitCodeProcess (proc_info.hProcess, &exit_code); + if (exit_code == STILL_ACTIVE) + TerminateProcess (proc_info.hProcess, 1); + else if (exit_code != 0) + { + WCHAR buf[256]; + int len = _snwprintf (buf, _countof (buf), + L"OpenVPN exited with error: exit code = %lu", exit_code); + buf[_countof (buf) - 1] = L'\0'; + ReturnError (pipe, ERROR_OPENVPN_STARTUP, buf, 1, &exit_event); + } + Undo (&undo_lists); + +out: + FlushFileBuffers (pipe); + DisconnectNamedPipe (pipe); + + free (ovpn_user); + free (svc_user); + free (cmdline); + DestroyEnvironmentBlock (user_env); + FreeStartupData (&sud); + CloseHandleEx (&proc_info.hProcess); + CloseHandleEx (&proc_info.hThread); + CloseHandleEx (&stdin_read); + CloseHandleEx (&stdin_write); + CloseHandleEx (&stdout_write); + CloseHandleEx (&svc_token); + CloseHandleEx (&imp_token); + CloseHandleEx (&pri_token); + CloseHandleEx (&ovpn_pipe); + CloseHandleEx (&svc_pipe); + CloseHandleEx (&pipe); + + return 0; +} + + +static DWORD WINAPI +ServiceCtrlInteractive (DWORD ctrl_code, DWORD event, LPVOID data, LPVOID ctx) +{ + SERVICE_STATUS *status = ctx; + switch (ctrl_code) + { + case SERVICE_CONTROL_STOP: + status->dwCurrentState = SERVICE_STOP_PENDING; + ReportStatusToSCMgr (service, status); + if (exit_event) + SetEvent (exit_event); + return NO_ERROR; + + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } +} + + +static HANDLE +CreateClientPipeInstance (VOID) +{ + HANDLE pipe = NULL; + PACL old_dacl, new_dacl; + PSECURITY_DESCRIPTOR sd; + static EXPLICIT_ACCESS ea[2]; + static BOOL initialized = FALSE; + DWORD flags = PIPE_ACCESS_DUPLEX | WRITE_DAC | FILE_FLAG_OVERLAPPED; + + if (!initialized) + { + PSID everyone, anonymous; + + ConvertStringSidToSid (TEXT("S-1-1-0"), &everyone); + ConvertStringSidToSid (TEXT("S-1-5-7"), &anonymous); + + ea[0].grfAccessPermissions = FILE_GENERIC_WRITE; + ea[0].grfAccessMode = GRANT_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.pMultipleTrustee = NULL; + ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + ea[0].Trustee.ptstrName = (LPTSTR) everyone; + + ea[1].grfAccessPermissions = 0; + ea[1].grfAccessMode = REVOKE_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.pMultipleTrustee = NULL; + ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + ea[1].Trustee.ptstrName = (LPTSTR) anonymous; + + flags |= FILE_FLAG_FIRST_PIPE_INSTANCE; + initialized = TRUE; + } + + pipe = CreateNamedPipe (TEXT("\\\\.\\pipe\\openvpn\\service"), flags, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL); + if (pipe == INVALID_HANDLE_VALUE) + { + MsgToEventLog (M_SYSERR, TEXT("Could not create named pipe")); + return INVALID_HANDLE_VALUE; + } + + if (GetSecurityInfo (pipe, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, + NULL, NULL, &old_dacl, NULL, &sd) != ERROR_SUCCESS) + { + MsgToEventLog (M_SYSERR, TEXT("Could not get pipe security info")); + return CloseHandleEx (&pipe); + } + + if (SetEntriesInAcl (2, ea, old_dacl, &new_dacl) != ERROR_SUCCESS) + { + MsgToEventLog (M_SYSERR, TEXT("Could not set entries in new acl")); + return CloseHandleEx (&pipe); + } + + if (SetSecurityInfo (pipe, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, + NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS) + { + MsgToEventLog (M_SYSERR, TEXT("Could not set pipe security info")); + return CloseHandleEx (&pipe); + } + + return pipe; +} + + +static DWORD +UpdateWaitHandles (LPHANDLE *handles_ptr, LPDWORD count, + HANDLE io_event, HANDLE exit_event, list_item_t *threads) +{ + static DWORD size = 10; + static LPHANDLE handles = NULL; + DWORD pos = 0; + + if (handles == NULL) + { + handles = malloc (size * sizeof (HANDLE)); + *handles_ptr = handles; + if (handles == NULL) + return ERROR_OUTOFMEMORY; + } + + handles[pos++] = io_event; + + if (!threads) + handles[pos++] = exit_event; + + while (threads) + { + if (pos == size) + { + LPHANDLE tmp; + size += 10; + tmp = realloc (handles, size * sizeof (HANDLE)); + if (tmp == NULL) + { + size -= 10; + *count = pos; + return ERROR_OUTOFMEMORY; + } + handles = tmp; + *handles_ptr = handles; + } + handles[pos++] = threads->data; + threads = threads->next; + } + + *count = pos; + return NO_ERROR; +} + + +static VOID +FreeWaitHandles (LPHANDLE h) +{ + free (h); +} + + +VOID WINAPI +ServiceStartInteractive (DWORD dwArgc, LPTSTR *lpszArgv) +{ + HANDLE pipe, io_event = NULL; + OVERLAPPED overlapped; + DWORD error = NO_ERROR; + list_item_t *threads = NULL; + PHANDLE handles = NULL; + DWORD handle_count; + BOOL CmpHandle (LPVOID item, LPVOID hnd) { return item == hnd; } + + service = RegisterServiceCtrlHandlerEx (interactive_service.name, ServiceCtrlInteractive, &status); + if (!service) + return; + + status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwServiceSpecificExitCode = NO_ERROR; + status.dwWin32ExitCode = NO_ERROR; + status.dwWaitHint = 3000; + ReportStatusToSCMgr (service, &status); + + /* Read info from registry in key HKLM\SOFTWARE\OpenVPN */ + error = GetOpenvpnSettings (&settings); + if (error != ERROR_SUCCESS) + goto out; + + io_event = InitOverlapped (&overlapped); + exit_event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (!exit_event || !io_event) + { + error = MsgToEventLog (M_SYSERR, TEXT("Could not create event")); + goto out; + } + + rdns_semaphore = CreateSemaphoreW (NULL, 1, 1, NULL); + if (!rdns_semaphore) + { + error = MsgToEventLog (M_SYSERR, TEXT("Could not create semaphore for register-dns")); + goto out; + } + + error = UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads); + if (error != NO_ERROR) + goto out; + + pipe = CreateClientPipeInstance (); + if (pipe == INVALID_HANDLE_VALUE) + goto out; + + status.dwCurrentState = SERVICE_RUNNING; + status.dwWaitHint = 0; + ReportStatusToSCMgr (service, &status); + + while (TRUE) + { + if (ConnectNamedPipe (pipe, &overlapped) == FALSE && + GetLastError () != ERROR_PIPE_CONNECTED && + GetLastError () != ERROR_IO_PENDING) + { + MsgToEventLog (M_SYSERR, TEXT("Could not connect pipe")); + break; + } + + error = WaitForMultipleObjects (handle_count, handles, FALSE, INFINITE); + if (error == WAIT_OBJECT_0) + { + /* Client connected, spawn a worker thread for it */ + HANDLE next_pipe = CreateClientPipeInstance (); + HANDLE thread = CreateThread (NULL, 0, RunOpenvpn, pipe, CREATE_SUSPENDED, NULL); + if (thread) + { + error = AddListItem (&threads, thread); + if (!error) + error = UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads); + if (error) + { + ReturnError (pipe, error, L"Insufficient resources to service new clients", 1, &exit_event); + /* Update wait handles again after removing the last worker thread */ + RemoveListItem (&threads, CmpHandle, thread); + UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads); + TerminateThread (thread, 1); + CloseHandleEx (&thread); + CloseHandleEx (&pipe); + } + else + ResumeThread (thread); + } + else + CloseHandleEx (&pipe); + + ResetOverlapped (&overlapped); + pipe = next_pipe; + } + else + { + CancelIo (pipe); + if (error == WAIT_FAILED) + { + MsgToEventLog (M_SYSERR, TEXT("WaitForMultipleObjects failed")); + SetEvent (exit_event); + /* Give some time for worker threads to exit and then terminate */ + Sleep (1000); + break; + } + if (!threads) + { + /* exit event signaled */ + CloseHandleEx (&pipe); + ResetEvent (exit_event); + error = NO_ERROR; + break; + } + + /* Worker thread ended */ + HANDLE thread = RemoveListItem (&threads, CmpHandle, handles[error]); + UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads); + CloseHandleEx (&thread); + } + } + +out: + FreeWaitHandles (handles); + CloseHandleEx (&io_event); + CloseHandleEx (&exit_event); + CloseHandleEx (&rdns_semaphore); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwWin32ExitCode = error; + ReportStatusToSCMgr (service, &status); +} diff --git a/src/openvpnserv/openvpnserv.c b/src/openvpnserv/openvpnserv.c deleted file mode 100755 index 56f5a02..0000000 --- a/src/openvpnserv/openvpnserv.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * 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. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (see the file COPYING included with this - * distribution); if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * This program allows one or more OpenVPN processes to be started - * as a service. To build, you must get the service sample from the - * Platform SDK and replace Simple.c with this file. - * - * You should also apply service.patch to - * service.c and service.h from the Platform SDK service sample. - * - * This code is designed to be built with the mingw compiler. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif -#include -#include -#include -#include -#include -#include "service.h" - -/* bool definitions */ -#define bool int -#define true 1 -#define false 0 - -/* These are new for 2000/XP, so they aren't in the mingw headers yet */ -#ifndef BELOW_NORMAL_PRIORITY_CLASS -#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 -#endif -#ifndef ABOVE_NORMAL_PRIORITY_CLASS -#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 -#endif - -struct security_attributes -{ - SECURITY_ATTRIBUTES sa; - SECURITY_DESCRIPTOR sd; -}; - -/* - * This event is initially created in the non-signaled - * state. It will transition to the signaled state when - * we have received a terminate signal from the Service - * Control Manager which will cause an asynchronous call - * of ServiceStop below. - */ -#define EXIT_EVENT_NAME PACKAGE "_exit_1" - -/* - * Which registry key in HKLM should - * we get config info from? - */ -#define REG_KEY "SOFTWARE\\" PACKAGE_NAME - -static HANDLE exit_event = NULL; - -/* clear an object */ -#define CLEAR(x) memset(&(x), 0, sizeof(x)) - -/* - * Message handling - */ -#define M_INFO (0) /* informational */ -#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */ -#define M_ERR (MSG_FLAGS_ERROR) /* error */ - -/* write error to event log */ -#define MSG(flags, ...) \ - { \ - char x_msg[256]; \ - openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__); \ - AddToMessageLog ((flags), x_msg); \ - } - -/* get a registry string */ -#define QUERY_REG_STRING(name, data) \ - { \ - len = sizeof (data); \ - status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \ - if (status != ERROR_SUCCESS || type != REG_SZ) \ - { \ - SetLastError (status); \ - MSG (M_SYSERR, error_format_str, name); \ - RegCloseKey (openvpn_key); \ - goto finish; \ - } \ - } - -/* get a registry string */ -#define QUERY_REG_DWORD(name, data) \ - { \ - len = sizeof (DWORD); \ - status = RegQueryValueEx(openvpn_key, name, NULL, &type, (LPBYTE)&data, &len); \ - if (status != ERROR_SUCCESS || type != REG_DWORD || len != sizeof (DWORD)) \ - { \ - SetLastError (status); \ - MSG (M_SYSERR, error_format_dword, name); \ - RegCloseKey (openvpn_key); \ - goto finish; \ - } \ - } - -/* - * This is necessary due to certain buggy implementations of snprintf, - * that don't guarantee null termination for size > 0. - * (copied from ../buffer.c, line 217) - * (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c) - */ - -int openvpn_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list arglist; - int len = -1; - if (size > 0) - { - va_start (arglist, format); - len = vsnprintf (str, size, format, arglist); - va_end (arglist); - str[size - 1] = 0; - } - return (len >= 0 && len < size); -} - - -bool -init_security_attributes_allow_all (struct security_attributes *obj) -{ - CLEAR (*obj); - - obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES); - obj->sa.lpSecurityDescriptor = &obj->sd; - obj->sa.bInheritHandle = TRUE; - if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION)) - return false; - if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE)) - return false; - return true; -} - -HANDLE -create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset) -{ - if (allow_all) - { - struct security_attributes sa; - if (!init_security_attributes_allow_all (&sa)) - return NULL; - return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name); - } - else - return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name); -} - -void -close_if_open (HANDLE h) -{ - if (h != NULL) - CloseHandle (h); -} - -static bool -match (const WIN32_FIND_DATA *find, const char *ext) -{ - int i; - - if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - return false; - - if (!strlen (ext)) - return true; - - i = strlen (find->cFileName) - strlen (ext) - 1; - if (i < 1) - return false; - - return find->cFileName[i] == '.' && !_stricmp (find->cFileName + i + 1, ext); -} - -/* - * Modify the extension on a filename. - */ -static bool -modext (char *dest, int size, const char *src, const char *newext) -{ - int i; - - if (size > 0 && (strlen (src) + 1) <= size) - { - strcpy (dest, src); - dest [size - 1] = '\0'; - i = strlen (dest); - while (--i >= 0) - { - if (dest[i] == '\\') - break; - if (dest[i] == '.') - { - dest[i] = '\0'; - break; - } - } - if (strlen (dest) + strlen(newext) + 2 <= size) - { - strcat (dest, "."); - strcat (dest, newext); - return true; - } - dest [0] = '\0'; - } - return false; -} - -VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) -{ - char exe_path[MAX_PATH]; - char config_dir[MAX_PATH]; - char ext_string[16]; - char log_dir[MAX_PATH]; - char priority_string[64]; - char append_string[2]; - - DWORD priority; - bool append; - - ResetError (); - - if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) - { - MSG (M_ERR, "ReportStatusToSCMgr #1 failed"); - goto finish; - } - - /* - * Create our exit event - */ - exit_event = create_event (EXIT_EVENT_NAME, false, false, true); - if (!exit_event) - { - MSG (M_ERR, "CreateEvent failed"); - goto finish; - } - - /* - * If exit event is already signaled, it means we were not - * shut down properly. - */ - if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT) - { - MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly"); - goto finish; - } - - if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) - { - MSG (M_ERR, "ReportStatusToSCMgr #2 failed"); - goto finish; - } - - /* - * Read info from registry in key HKLM\SOFTWARE\OpenVPN - */ - { - HKEY openvpn_key; - LONG status; - DWORD len; - DWORD type; - - static const char error_format_str[] = - "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s"; - - static const char error_format_dword[] = - "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s"; - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - REG_KEY, - 0, - KEY_READ, - &openvpn_key); - - if (status != ERROR_SUCCESS) - { - SetLastError (status); - MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found"); - goto finish; - } - - /* get path to openvpn.exe */ - QUERY_REG_STRING ("exe_path", exe_path); - - /* get path to configuration directory */ - QUERY_REG_STRING ("config_dir", config_dir); - - /* get extension on configuration files */ - QUERY_REG_STRING ("config_ext", ext_string); - - /* get path to log directory */ - QUERY_REG_STRING ("log_dir", log_dir); - - /* get priority for spawned OpenVPN subprocesses */ - QUERY_REG_STRING ("priority", priority_string); - - /* should we truncate or append to logfile? */ - QUERY_REG_STRING ("log_append", append_string); - - RegCloseKey (openvpn_key); - } - - /* set process priority */ - priority = NORMAL_PRIORITY_CLASS; - if (!_stricmp (priority_string, "IDLE_PRIORITY_CLASS")) - priority = IDLE_PRIORITY_CLASS; - else if (!_stricmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS")) - priority = BELOW_NORMAL_PRIORITY_CLASS; - else if (!_stricmp (priority_string, "NORMAL_PRIORITY_CLASS")) - priority = NORMAL_PRIORITY_CLASS; - else if (!_stricmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS")) - priority = ABOVE_NORMAL_PRIORITY_CLASS; - else if (!_stricmp (priority_string, "HIGH_PRIORITY_CLASS")) - priority = HIGH_PRIORITY_CLASS; - else - { - MSG (M_ERR, "Unknown priority name: %s", priority_string); - goto finish; - } - - /* set log file append/truncate flag */ - append = false; - if (append_string[0] == '0') - append = false; - else if (append_string[0] == '1') - append = true; - else - { - MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string); - goto finish; - } - - /* - * Instantiate an OpenVPN process for each configuration - * file found. - */ - { - WIN32_FIND_DATA find_obj; - HANDLE find_handle; - BOOL more_files; - char find_string[MAX_PATH]; - - openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir); - - find_handle = FindFirstFile (find_string, &find_obj); - if (find_handle == INVALID_HANDLE_VALUE) - { - MSG (M_ERR, "Cannot get configuration file list using: %s", find_string); - goto finish; - } - - /* - * Loop over each config file - */ - do { - HANDLE log_handle = NULL; - STARTUPINFO start_info; - PROCESS_INFORMATION proc_info; - struct security_attributes sa; - char log_file[MAX_PATH]; - char log_path[MAX_PATH]; - char command_line[256]; - - CLEAR (start_info); - CLEAR (proc_info); - CLEAR (sa); - - if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) - { - MSG (M_ERR, "ReportStatusToSCMgr #3 failed"); - FindClose (find_handle); - goto finish; - } - - /* does file have the correct type and extension? */ - if (match (&find_obj, ext_string)) - { - /* get log file pathname */ - if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log")) - { - MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName); - FindClose (find_handle); - goto finish; - } - openvpn_snprintf (log_path, sizeof(log_path), - "%s\\%s", log_dir, log_file); - - /* construct command line */ - openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"", - EXIT_EVENT_NAME, - find_obj.cFileName); - - /* Make security attributes struct for logfile handle so it can - be inherited. */ - if (!init_security_attributes_allow_all (&sa)) - { - MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed"); - goto finish; - } - - /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */ - log_handle = CreateFile (log_path, - GENERIC_WRITE, - FILE_SHARE_READ, - &sa.sa, - append ? OPEN_ALWAYS : CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (log_handle == INVALID_HANDLE_VALUE) - { - MSG (M_SYSERR, "Cannot open logfile: %s", log_path); - FindClose (find_handle); - goto finish; - } - - /* append to logfile? */ - if (append) - { - if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) - { - MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path); - FindClose (find_handle); - goto finish; - } - } - - /* fill in STARTUPINFO struct */ - GetStartupInfo(&start_info); - start_info.cb = sizeof(start_info); - start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; - start_info.wShowWindow = SW_HIDE; - start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - start_info.hStdOutput = start_info.hStdError = log_handle; - - /* create an OpenVPN process for one config file */ - if (!CreateProcess(exe_path, - command_line, - NULL, - NULL, - TRUE, - priority | CREATE_NEW_CONSOLE, - NULL, - config_dir, - &start_info, - &proc_info)) - { - MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'", - exe_path, - command_line, - config_dir); - - FindClose (find_handle); - CloseHandle (log_handle); - goto finish; - } - - /* close unneeded handles */ - Sleep (1000); /* try to prevent race if we close logfile - handle before child process DUPs it */ - if (!CloseHandle (proc_info.hProcess) - || !CloseHandle (proc_info.hThread) - || !CloseHandle (log_handle)) - { - MSG (M_SYSERR, "CloseHandle failed"); - goto finish; - } - } - - /* more files to process? */ - more_files = FindNextFile (find_handle, &find_obj); - - } while (more_files); - - FindClose (find_handle); - } - - /* we are now fully started */ - if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0)) - { - MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed"); - goto finish; - } - - /* wait for our shutdown signal */ - if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0) - { - MSG (M_ERR, "wait for shutdown signal failed"); - } - - finish: - ServiceStop (); - if (exit_event) - CloseHandle (exit_event); -} - -VOID ServiceStop() -{ - if (exit_event) - SetEvent(exit_event); -} diff --git a/src/openvpnserv/openvpnserv.vcxproj b/src/openvpnserv/openvpnserv.vcxproj index 2a8943d..c6760da 100644 --- a/src/openvpnserv/openvpnserv.vcxproj +++ b/src/openvpnserv/openvpnserv.vcxproj @@ -111,4 +111,4 @@ - \ No newline at end of file + diff --git a/src/openvpnserv/service.c b/src/openvpnserv/service.c index d7562b3..82f5551 100644 --- a/src/openvpnserv/service.c +++ b/src/openvpnserv/service.c @@ -1,700 +1,245 @@ -/*--------------------------------------------------------------------------- -THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -PARTICULAR PURPOSE. - -Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved. - -MODULE: service.c - -PURPOSE: Implements functions required by all Windows NT services - -FUNCTIONS: - main(int argc, char **argv); - service_ctrl(DWORD dwCtrlCode); - service_main(DWORD dwArgc, LPTSTR *lpszArgv); - CmdInstallService(); - CmdRemoveService(); - CmdStartService(); - CmdDebugService(int argc, char **argv); - ControlHandler ( DWORD dwCtrlType ); - GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); - ----------------------------------------------------------------------------*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif -#include -#include -#include -#include -#include +/* + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF + * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A + * PARTICULAR PURPOSE. + * + * Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved. + * 2013 Heiko Hund + */ #include "service.h" -// internal variables -SERVICE_STATUS ssStatus; // current status of the service -SERVICE_STATUS_HANDLE sshStatusHandle; -DWORD dwErr = 0; -BOOL bDebug = FALSE; -TCHAR szErr[256]; - -// internal function prototypes -VOID WINAPI service_ctrl(DWORD dwCtrlCode); -VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv); -int CmdInstallService(); -int CmdRemoveService(); -int CmdStartService(); -VOID CmdDebugService(int argc, char **argv); -BOOL WINAPI ControlHandler ( DWORD dwCtrlType ); -LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); - -// -// FUNCTION: main -// -// PURPOSE: entrypoint for service -// -// PARAMETERS: -// argc - number of command line arguments -// argv - array of command line arguments -// -// RETURN VALUE: -// none -// -// COMMENTS: -// main() either performs the command line task, or -// call StartServiceCtrlDispatcher to register the -// main service thread. When the this call returns, -// the service has stopped, so exit. -// -int __cdecl main(int argc, char **argv) -{ - SERVICE_TABLE_ENTRY dispatchTable[] = - { - { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main}, - { NULL, NULL} - }; - - if ( (argc > 1) && - ((*argv[1] == '-') || (*argv[1] == '/')) ) - { - if ( _stricmp( "install", argv[1]+1 ) == 0 ) - { - return CmdInstallService(); - } - else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) - { - return CmdRemoveService(); - } - else if ( _stricmp( "start", argv[1]+1 ) == 0) - { - return CmdStartService(); - } - else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) - { - bDebug = TRUE; - CmdDebugService(argc, argv); - } - else - { - goto dispatch; - } - return 0; - } - - // if it doesn't match any of the above parameters - // the service control manager may be starting the service - // so we must call StartServiceCtrlDispatcher - dispatch: - // this is just to be friendly - printf( "%s -install to install the service\n", SZAPPNAME ); - printf( "%s -start to start the service\n", SZAPPNAME ); - printf( "%s -remove to remove the service\n", SZAPPNAME ); - printf( "%s -debug to run as a console app for debugging\n", SZAPPNAME ); - printf( "\nStartServiceCtrlDispatcher being called.\n" ); - printf( "This may take several seconds. Please wait.\n" ); - - if (!StartServiceCtrlDispatcher(dispatchTable)) - AddToMessageLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed.")); - - return 0; -} - - - -// -// FUNCTION: service_main -// -// PURPOSE: To perform actual initialization of the service -// -// PARAMETERS: -// dwArgc - number of command line arguments -// lpszArgv - array of command line arguments -// -// RETURN VALUE: -// none -// -// COMMENTS: -// This routine performs the service initialization and then calls -// the user defined ServiceStart() routine to perform majority -// of the work. -// -void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv) -{ - - // register our service control handler: - // - sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl); - - if (!sshStatusHandle) - goto cleanup; - - // SERVICE_STATUS members that don't change in example - // - ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - ssStatus.dwServiceSpecificExitCode = 0; - - - // report the status to the service control manager. - // - if (!ReportStatusToSCMgr( - SERVICE_START_PENDING, // service state - NO_ERROR, // exit code - 3000)) // wait hint - goto cleanup; - - - ServiceStart( dwArgc, lpszArgv ); - - cleanup: +#include +#include +#include - // try to report the stopped status to the service control manager. - // - if (sshStatusHandle) - (VOID)ReportStatusToSCMgr( - SERVICE_STOPPED, - dwErr, - 0); - return; -} +openvpn_service_t openvpn_service[_service_max]; - -// -// FUNCTION: service_ctrl -// -// PURPOSE: This function is called by the SCM whenever -// ControlService() is called on this service. -// -// PARAMETERS: -// dwCtrlCode - type of control requested -// -// RETURN VALUE: -// none -// -// COMMENTS: -// -VOID WINAPI service_ctrl(DWORD dwCtrlCode) +BOOL +ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status) { - // Handle the requested control code. - // - switch (dwCtrlCode) - { - // Stop the service. - // - // SERVICE_STOP_PENDING should be reported before - // setting the Stop Event - hServerStopEvent - in - // ServiceStop(). This avoids a race condition - // which may result in a 1053 - The Service did not respond... - // error. - case SERVICE_CONTROL_STOP: - ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0); - ServiceStop(); - return; - - // Update the service status. - // - case SERVICE_CONTROL_INTERROGATE: - break; - - // invalid control code - // - default: - break; - - } - - ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0); + static DWORD dwCheckPoint = 1; + BOOL res = TRUE; + + if (status->dwCurrentState == SERVICE_START_PENDING) + status->dwControlsAccepted = 0; + else + status->dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if (status->dwCurrentState == SERVICE_RUNNING || + status->dwCurrentState == SERVICE_STOPPED) + status->dwCheckPoint = 0; + else + status->dwCheckPoint = dwCheckPoint++; + + /* Report the status of the service to the service control manager. */ + res = SetServiceStatus (service, status); + if (!res) + MsgToEventLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus")); + + return res; } - - -// -// FUNCTION: ReportStatusToSCMgr() -// -// PURPOSE: Sets the current status of the service and -// reports it to the Service Control Manager -// -// PARAMETERS: -// dwCurrentState - the state of the service -// dwWin32ExitCode - error code to report -// dwWaitHint - worst case estimate to next checkpoint -// -// RETURN VALUE: -// TRUE - success -// FALSE - failure -// -// COMMENTS: -// -BOOL ReportStatusToSCMgr(DWORD dwCurrentState, - DWORD dwWin32ExitCode, - DWORD dwWaitHint) +static int +CmdInstallServices () { - static DWORD dwCheckPoint = 1; - BOOL fResult = TRUE; - - - if ( !bDebug ) // when debugging we don't report to the SCM - { - if (dwCurrentState == SERVICE_START_PENDING) - ssStatus.dwControlsAccepted = 0; - else - ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; - - ssStatus.dwCurrentState = dwCurrentState; - ssStatus.dwWin32ExitCode = dwWin32ExitCode; - ssStatus.dwWaitHint = dwWaitHint; + SC_HANDLE service; + SC_HANDLE svc_ctl_mgr; + TCHAR path[512]; + int i, ret = _service_max; + + if (GetModuleFileName (NULL, path + 1, 510) == 0) + { + _tprintf (TEXT("Unable to install service - %s\n"), GetLastErrorText ()); + return 1; + } + + path[0] = TEXT('\"'); + _tcscat (path, TEXT("\"")); + + svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (svc_ctl_mgr == NULL) + { + _tprintf (TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ()); + return 1; + } - if ( ( dwCurrentState == SERVICE_RUNNING ) || - ( dwCurrentState == SERVICE_STOPPED ) ) - ssStatus.dwCheckPoint = 0; + for (i = 0; i < _service_max; i++) + { + service = CreateService (svc_ctl_mgr, + openvpn_service[i].name, + openvpn_service[i].display_name, + SERVICE_QUERY_STATUS, + SERVICE_WIN32_SHARE_PROCESS, + openvpn_service[i].start_type, + SERVICE_ERROR_NORMAL, + path, NULL, NULL, + openvpn_service[i].dependencies, + NULL, NULL); + if (service) + { + _tprintf (TEXT("%s installed.\n"), openvpn_service[i].display_name); + CloseServiceHandle (service); + --ret; + } else - ssStatus.dwCheckPoint = dwCheckPoint++; - + _tprintf (TEXT("CreateService failed - %s\n"), GetLastErrorText ()); + } - // Report the status of the service to the service control manager. - // - if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) - { - AddToMessageLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus")); - } - } - return fResult; + CloseServiceHandle (svc_ctl_mgr); + return ret; } - -// -// FUNCTION: AddToMessageLog(LPTSTR lpszMsg) -// -// PURPOSE: Allows any thread to log an error message -// -// PARAMETERS: -// lpszMsg - text for message -// -// RETURN VALUE: -// none -// -// COMMENTS: -// -void AddToMessageLog(DWORD flags, LPTSTR lpszMsg) +static int +CmdStartService (openvpn_service_type type) { - TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ]; - HANDLE hEventSource; - LPCSTR lpszStrings[2]; - - if ( !bDebug ) - { - if (flags & MSG_FLAGS_SYS_CODE) - dwErr = GetLastError(); - else - dwErr = 0; - - // Use event logging to log the error. - // - hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME)); - - _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), (int)dwErr); - lpszStrings[0] = szMsg; - lpszStrings[1] = lpszMsg; - - if (hEventSource != NULL) - { - ReportEvent(hEventSource, // handle of event source - // event type - (flags & MSG_FLAGS_ERROR) - ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, - 0, // event category - 0, // event ID - NULL, // current user's SID - 2, // strings in lpszStrings - 0, // no bytes of raw data - lpszStrings, // array of error strings - NULL); // no raw data - - (VOID) DeregisterEventSource(hEventSource); - } - } -} - -void ResetError (void) -{ - dwErr = 0; -} + int ret = 1; + SC_HANDLE svc_ctl_mgr; + SC_HANDLE service; -/////////////////////////////////////////////////////////////////// -// -// The following code handles service installation and removal -// - - -// -// FUNCTION: CmdInstallService() -// -// PURPOSE: Installs the service -// -// PARAMETERS: -// none -// -// RETURN VALUE: -// 0 if success -// -// COMMENTS: -// -int CmdInstallService() -{ - SC_HANDLE schService; - SC_HANDLE schSCManager; - - TCHAR szPath[512]; - - int ret = 0; - - if ( GetModuleFileName( NULL, szPath+1, 510 ) == 0 ) - { - _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256)); + svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (svc_ctl_mgr == NULL) + { + _tprintf (TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ()); return 1; - } - szPath[0] = '\"'; - strcat(szPath, "\""); - - schSCManager = OpenSCManager( - NULL, // machine (NULL == local) - NULL, // database (NULL == default) - SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required - ); - if ( schSCManager ) - { - schService = CreateService( - schSCManager, // SCManager database - TEXT(SZSERVICENAME), // name of service - TEXT(SZSERVICEDISPLAYNAME), // name to display - SERVICE_QUERY_STATUS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START - SERVICE_ERROR_NORMAL, // error control type - szPath, // service's binary - NULL, // no load ordering group - NULL, // no tag identifier - TEXT(SZDEPENDENCIES), // dependencies - NULL, // LocalSystem account - NULL); // no password - - if ( schService ) - { - _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); - CloseServiceHandle(schService); - } - else - { - _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256)); - ret = 1; - } - - CloseServiceHandle(schSCManager); - } - else - { - _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; - } - return ret; -} - -// -// FUNCTION: CmdStartService() -// -// PURPOSE: Start the service -// -// PARAMETERS: -// none -// -// RETURN VALUE: -// 0 if success -// -// COMMENTS: - -int CmdStartService() -{ - int ret = 0; - - SC_HANDLE schSCManager; - SC_HANDLE schService; - - - // Open a handle to the SC Manager database. - schSCManager = OpenSCManager( - NULL, // local machine - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (NULL == schSCManager) { - _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; } - schService = OpenService( - schSCManager, // SCM database - SZSERVICENAME, // service name - SERVICE_ALL_ACCESS); + service = OpenService (svc_ctl_mgr, openvpn_service[type].name, SERVICE_ALL_ACCESS); + if (service) + { + if (StartService (service, 0, NULL)) + { + _tprintf (TEXT("Service Started\n")); + ret = 0; + } + else + _tprintf (TEXT("StartService failed - %s\n"), GetLastErrorText ()); - if (schService == NULL) { - _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; + CloseServiceHandle(service); } - - if (!StartService( - schService, // handle to service - 0, // number of arguments - NULL) ) // no arguments + else { - _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; + _tprintf (TEXT("OpenService failed - %s\n"), GetLastErrorText ()); } - else - { - _tprintf(TEXT("Service Started\n")); - ret = 0; - } - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); - return ret; -} -// -// FUNCTION: CmdRemoveService() -// -// PURPOSE: Stops and removes the service -// -// PARAMETERS: -// none -// -// RETURN VALUE: -// 0 if success -// -// COMMENTS: -// -int CmdRemoveService() -{ - SC_HANDLE schService; - SC_HANDLE schSCManager; + CloseServiceHandle(svc_ctl_mgr); + return ret; +} - int ret = 0; - schSCManager = OpenSCManager( - NULL, // machine (NULL == local) - NULL, // database (NULL == default) - SC_MANAGER_CONNECT // access required - ); - if ( schSCManager ) - { - schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); +static int +CmdRemoveServices () +{ + SC_HANDLE service; + SC_HANDLE svc_ctl_mgr; + SERVICE_STATUS status; + int i, ret = _service_max; - if (schService) - { - // try to stop the service - if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) - { - _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME)); - Sleep( 1000 ); + svc_ctl_mgr = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT); + if (svc_ctl_mgr == NULL) + { + _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText ()); + return 1; + } - while ( QueryServiceStatus( schService, &ssStatus ) ) + for (i = 0; i < _service_max; i++) + { + openvpn_service_t *ovpn_svc = &openvpn_service[i]; + service = OpenService (svc_ctl_mgr, ovpn_svc->name, + DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); + if (service == NULL) + { + _tprintf (TEXT("OpenService failed - %s\n"), GetLastErrorText ()); + goto out; + } + + /* try to stop the service */ + if (ControlService (service, SERVICE_CONTROL_STOP, &status)) + { + _tprintf (TEXT("Stopping %s."), ovpn_svc->display_name); + Sleep (1000); + + while (QueryServiceStatus (service, &status)) { - if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) - { - _tprintf(TEXT(".")); - Sleep( 1000 ); - } - else - break; + if (status.dwCurrentState == SERVICE_STOP_PENDING) + { + _tprintf (TEXT(".")); + Sleep (1000); + } + else + break; } - if ( ssStatus.dwCurrentState == SERVICE_STOPPED ) - _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) ); - else - { - _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); - ret = 1; - } - - } - - // now remove the service - if ( DeleteService(schService) ) - _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); - else - { - _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; - } - - - CloseServiceHandle(schService); - } + if (status.dwCurrentState == SERVICE_STOPPED) + _tprintf (TEXT("\n%s stopped.\n"), ovpn_svc->display_name); + else + _tprintf (TEXT("\n%s failed to stop.\n"), ovpn_svc->display_name); + } + + /* now remove the service */ + if (DeleteService (service)) + { + _tprintf (TEXT("%s removed.\n"), ovpn_svc->display_name); + --ret; + } else - { - _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; - } - - CloseServiceHandle(schSCManager); - } - else - { - _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); - ret = 1; - } - return ret; -} - - - - -/////////////////////////////////////////////////////////////////// -// -// The following code is for running the service as a console app -// - - -// -// FUNCTION: CmdDebugService(int argc, char ** argv) -// -// PURPOSE: Runs the service as a console application -// -// PARAMETERS: -// argc - number of command line arguments -// argv - array of command line arguments -// -// RETURN VALUE: -// none -// -// COMMENTS: -// -void CmdDebugService(int argc, char ** argv) -{ - DWORD dwArgc; - LPTSTR *lpszArgv; - -#ifdef UNICODE - lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) ); - if (NULL == lpszArgv) - { - // CommandLineToArvW failed!! - _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n")); - return; - } -#else - dwArgc = (DWORD) argc; - lpszArgv = argv; -#endif - - _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); - - SetConsoleCtrlHandler( ControlHandler, TRUE ); + _tprintf (TEXT("DeleteService failed - %s\n"), GetLastErrorText ()); - ServiceStart( dwArgc, lpszArgv ); - -#ifdef UNICODE -// Must free memory allocated for arguments - - GlobalFree(lpszArgv); -#endif // UNICODE + CloseServiceHandle (service); + } +out: + CloseServiceHandle (svc_ctl_mgr); + return ret; } -// -// FUNCTION: ControlHandler ( DWORD dwCtrlType ) -// -// PURPOSE: Handled console control events -// -// PARAMETERS: -// dwCtrlType - type of control event -// -// RETURN VALUE: -// True - handled -// False - unhandled -// -// COMMENTS: -// -BOOL WINAPI ControlHandler ( DWORD dwCtrlType ) +int +_tmain (int argc, TCHAR *argv[]) { - switch ( dwCtrlType ) - { - case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate - case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode - _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); - ServiceStop(); - return TRUE; - break; - - } - return FALSE; -} - -// -// FUNCTION: GetLastErrorText -// -// PURPOSE: copies error message text to string -// -// PARAMETERS: -// lpszBuf - destination buffer -// dwSize - size of buffer -// -// RETURN VALUE: -// destination buffer -// -// COMMENTS: -// -LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ) -{ - DWORD dwRet; - LPTSTR lpszTemp = NULL; - - dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY, - NULL, - GetLastError(), - LANG_NEUTRAL, - (LPTSTR)&lpszTemp, - 0, - NULL ); - - // supplied buffer is not long enough - if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) ) - lpszBuf[0] = TEXT('\0'); - else - { - lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character - _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() ); - } - - if ( lpszTemp ) - LocalFree((HLOCAL) lpszTemp ); - - return lpszBuf; + SERVICE_TABLE_ENTRY dispatchTable[] = { + { automatic_service.name, ServiceStartAutomatic }, + { interactive_service.name, ServiceStartInteractive }, + { NULL, NULL } + }; + + openvpn_service[0] = automatic_service; + openvpn_service[1] = interactive_service; + + if (argc > 1 && (*argv[1] == TEXT('-') || *argv[1] == TEXT('/'))) + { + if (_tcsicmp (TEXT("install"), argv[1] + 1) == 0) + return CmdInstallServices (); + else if (_tcsicmp (TEXT("remove"), argv[1] + 1) == 0) + return CmdRemoveServices (); + else if (_tcsicmp (TEXT("start"), argv[1] + 1) == 0) + { + BOOL is_auto = argc < 3 || _tcsicmp (TEXT("interactive"), argv[2]) != 0; + return CmdStartService (is_auto ? automatic : interactive); + } + else + goto dispatch; + + return 0; + } + + /* If it doesn't match any of the above parameters + * the service control manager may be starting the service + * so we must call StartServiceCtrlDispatcher + */ +dispatch: + _tprintf (TEXT("%s -install to install the services\n"), APPNAME); + _tprintf (TEXT("%s -start to start a service (\"automatic\" or \"interactive\")\n"), APPNAME); + _tprintf (TEXT("%s -remove to remove the services\n"), APPNAME); + _tprintf (TEXT("\nStartServiceCtrlDispatcher being called.\n")); + _tprintf (TEXT("This may take several seconds. Please wait.\n")); + + if (!StartServiceCtrlDispatcher (dispatchTable)) + MsgToEventLog (MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed.")); + + return 0; } diff --git a/src/openvpnserv/service.h b/src/openvpnserv/service.h index e89a89f..94bfb07 100644 --- a/src/openvpnserv/service.h +++ b/src/openvpnserv/service.h @@ -1,139 +1,92 @@ -/*--------------------------------------------------------------------------- -THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -PARTICULAR PURPOSE. - -Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved. - - MODULE: service.h - - Comments: The use of this header file and the accompanying service.c - file simplifies the process of writting a service. You as a developer - simply need to follow the TODO's outlined in this header file, and - implement the ServiceStart() and ServiceStop() functions. - - There is no need to modify the code in service.c. Just add service.c - to your project and link with the following libraries... - - libcmt.lib kernel32.lib advapi.lib shell32.lib - - This code also supports unicode. Be sure to compile both service.c and - and code #include "service.h" with the same Unicode setting. - - Upon completion, your code will have the following command line interface - - -? to display this list - -install to install the service - -remove to remove the service - -debug to run as a console app for debugging - - Note: This code also implements Ctrl+C and Ctrl+Break handlers - when using the debug option. These console events cause - your ServiceStop routine to be called - - Also, this code only handles the OWN_SERVICE service type - running in the LOCAL_SYSTEM security context. - - To control your service ( start, stop, etc ) you may use the - Services control panel applet or the NET.EXE program. - - To aid in writing/debugging service, the - SDK contains a utility (MSTOOLS\BIN\SC.EXE) that - can be used to control, configure, or obtain service status. - SC displays complete status for any service/driver - in the service database, and allows any of the configuration - parameters to be easily changed at the command line. - For more information on SC.EXE, type SC at the command line. - - -------------------------------------------------------------------------------*/ +/* + * 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) 2013 Heiko Hund + * + * 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 + */ #ifndef _SERVICE_H #define _SERVICE_H - -#ifdef __cplusplus -extern "C" { +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" #endif -////////////////////////////////////////////////////////////////////////////// -//// todo: change to desired strings -//// -// name of the executable -#define SZAPPNAME PACKAGE "serv" -// internal name of the service -#define SZSERVICENAME PACKAGE_NAME "Service" -// displayed name of the service -#define SZSERVICEDISPLAYNAME PACKAGE_NAME " Service" -// list of service dependencies - "dep1\0dep2\0\0" -#define SZDEPENDENCIES TAP_WIN_COMPONENT_ID "\0Dhcp\0\0" -////////////////////////////////////////////////////////////////////////////// - - - -////////////////////////////////////////////////////////////////////////////// -//// todo: ServiceStart()must be defined by in your code. -//// The service should use ReportStatusToSCMgr to indicate -//// progress. This routine must also be used by StartService() -//// to report to the SCM when the service is running. -//// -//// If a ServiceStop procedure is going to take longer than -//// 3 seconds to execute, it should spawn a thread to -//// execute the stop code, and return. Otherwise, the -//// ServiceControlManager will believe that the service has -//// stopped responding -//// - VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv); - VOID ServiceStop(); -////////////////////////////////////////////////////////////////////////////// - - - -////////////////////////////////////////////////////////////////////////////// -//// The following are procedures which -//// may be useful to call within the above procedures, -//// but require no implementation by the user. -//// They are implemented in service.c - -// -// FUNCTION: ReportStatusToSCMgr() -// -// PURPOSE: Sets the current status of the service and -// reports it to the Service Control Manager -// -// PARAMETERS: -// dwCurrentState - the state of the service -// dwWin32ExitCode - error code to report -// dwWaitHint - worst case estimate to next checkpoint -// -// RETURN VALUE: -// TRUE - success -// FALSE - failure -// - BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); - - -// -// FUNCTION: AddToMessageLog(LPTSTR lpszMsg) -// -// PURPOSE: Allows any thread to log an error message -// -// PARAMETERS: -// lpszMsg - text for message -// -// RETURN VALUE: -// none -// -# define MSG_FLAGS_ERROR (1<<0) -# define MSG_FLAGS_SYS_CODE (1<<1) - void AddToMessageLog(DWORD flags, LPTSTR lpszMsg); - void ResetError (void); -////////////////////////////////////////////////////////////////////////////// - - -#ifdef __cplusplus -} -#endif +#include +#include +#include + +#define APPNAME TEXT(PACKAGE "serv") +#define SERVICE_DEPENDENCIES TAP_WIN_COMPONENT_ID "\0Dhcp\0\0" + +/* + * Message handling + */ +#define MSG_FLAGS_ERROR (1<<0) +#define MSG_FLAGS_SYS_CODE (1<<1) +#define M_INFO (0) /* informational */ +#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) /* error + system code */ +#define M_ERR (MSG_FLAGS_ERROR) /* error */ + +typedef enum { + automatic, + interactive, + _service_max +} openvpn_service_type; + +typedef struct { + openvpn_service_type type; + TCHAR *name; + TCHAR *display_name; + TCHAR *dependencies; + DWORD start_type; +} openvpn_service_t; + +#define MAX_NAME 256 +typedef struct { + TCHAR exe_path[MAX_PATH]; + TCHAR config_dir[MAX_PATH]; + TCHAR ext_string[16]; + TCHAR log_dir[MAX_PATH]; + TCHAR ovpn_admin_group[MAX_NAME]; + DWORD priority; + BOOL append; +} settings_t; + +extern openvpn_service_t automatic_service; +extern openvpn_service_t interactive_service; + + +VOID WINAPI ServiceStartAutomatic (DWORD argc, LPTSTR *argv); +VOID WINAPI ServiceStartInteractive (DWORD argc, LPTSTR *argv); + +int openvpn_vsntprintf (LPTSTR str, size_t size, LPCTSTR format, va_list arglist); +int openvpn_sntprintf (LPTSTR str, size_t size, LPCTSTR format, ...); + +DWORD GetOpenvpnSettings (settings_t *s); + +BOOL ReportStatusToSCMgr (SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status); + +LPCTSTR GetLastErrorText (); +DWORD MsgToEventLog (DWORD flags, LPCTSTR lpszMsg, ...); #endif diff --git a/src/openvpnserv/validate.c b/src/openvpnserv/validate.c new file mode 100644 index 0000000..7458d75 --- /dev/null +++ b/src/openvpnserv/validate.c @@ -0,0 +1,249 @@ +/* + * 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) 2016 Selva Nair + * + * 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 "validate.h" + +#include +#include +#include + +static const WCHAR *white_list[] = + { + L"auth-retry", + L"config", + L"log", + L"log-append", + L"management", + L"management-forget-disconnect", + L"management-hold", + L"management-query-passwords", + L"management-query-proxy", + L"management-signal", + L"management-up-down", + L"mute", + L"setenv", + L"service", + L"verb", + + NULL /* last value */ + }; + +/* + * Check workdir\fname is inside config_dir + * The logic here is simple: we may reject some valid paths if ..\ is in any of the strings + */ +static BOOL +CheckConfigPath (const WCHAR *workdir, const WCHAR *fname, const settings_t *s) +{ + WCHAR tmp[MAX_PATH]; + const WCHAR *config_file = NULL; + const WCHAR *config_dir = NULL; + + /* convert fname to full path */ + if (PathIsRelativeW (fname) ) + { + snwprintf (tmp, _countof(tmp), L"%s\\%s", workdir, fname); + tmp[_countof(tmp)-1] = L'\0'; + config_file = tmp; + } + else + { + config_file = fname; + } + +#ifdef UNICODE + config_dir = s->config_dir; +#else + if (MultiByteToWideChar (CP_UTF8, 0, s->config_dir, -1, widepath, MAX_PATH) == 0) + { + MsgToEventLog (M_SYSERR, TEXT("Failed to convert config_dir name to WideChar")); + return FALSE; + } + config_dir = widepath; +#endif + + if (wcsncmp (config_dir, config_file, wcslen(config_dir)) == 0 && + wcsstr (config_file + wcslen(config_dir), L"..") == NULL ) + return TRUE; + + return FALSE; +} + + +/* + * A simple linear search meant for a small wchar_t *array. + * Returns index to the item if found, -1 otherwise. + */ +static int +OptionLookup (const WCHAR *name, const WCHAR *white_list[]) +{ + int i; + + for (i = 0 ; white_list[i]; i++) + { + if ( wcscmp(white_list[i], name) == 0 ) + return i; + } + + return -1; +} + +/* + * The Administrators group may be localized or renamed by admins. + * Get the local name of the group using the SID. + */ +static BOOL +GetBuiltinAdminGroupName (WCHAR *name, DWORD nlen) +{ + BOOL b = FALSE; + PSID admin_sid = NULL; + DWORD sid_size = SECURITY_MAX_SID_SIZE; + SID_NAME_USE snu; + + WCHAR domain[MAX_NAME]; + DWORD dlen = _countof(domain); + + admin_sid = malloc(sid_size); + if (!admin_sid) + return FALSE; + + b = CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, admin_sid, &sid_size); + if(b) + { + b = LookupAccountSidW(NULL, admin_sid, name, &nlen, domain, &dlen, &snu); + } + + free (admin_sid); + + return b; +} + +/* + * Check whether user is a member of Administrators group or + * the group specified in s->ovpn_admin_group + */ +BOOL +IsAuthorizedUser (SID *sid, settings_t *s) +{ + LOCALGROUP_USERS_INFO_0 *groups = NULL; + DWORD nread; + DWORD nmax; + WCHAR *tmp = NULL; + const WCHAR *admin_group[2]; + WCHAR username[MAX_NAME]; + WCHAR domain[MAX_NAME]; + WCHAR sysadmin_group[MAX_NAME]; + DWORD err, len = MAX_NAME; + int i; + BOOL ret = FALSE; + SID_NAME_USE sid_type; + + /* Get username */ + if (!LookupAccountSidW (NULL, sid, username, &len, domain, &len, &sid_type)) + { + MsgToEventLog (M_SYSERR, TEXT("LookupAccountSid")); + goto out; + } + + /* Get an array of groups the user is member of */ + err = NetUserGetLocalGroups (NULL, username, 0, LG_INCLUDE_INDIRECT, (LPBYTE *) &groups, + MAX_PREFERRED_LENGTH, &nread, &nmax); + if (err && err != ERROR_MORE_DATA) + { + SetLastError (err); + MsgToEventLog (M_SYSERR, TEXT("NetUserGetLocalGroups")); + goto out; + } + + if (GetBuiltinAdminGroupName(sysadmin_group, _countof(sysadmin_group))) + { + admin_group[0] = sysadmin_group; + } + else + { + MsgToEventLog (M_SYSERR, TEXT("Failed to get the name of Administrators group. Using the default.")); + /* use the default value */ + admin_group[0] = SYSTEM_ADMIN_GROUP; + } + +#ifdef UNICODE + admin_group[1] = s->ovpn_admin_group; +#else + tmp = NULL; + len = MultiByteToWideChar (CP_UTF8, 0, s->ovpn_admin_group, -1, NULL, 0); + if (len == 0 || (tmp = malloc (len*sizeof(WCHAR))) == NULL) + { + MsgToEventLog (M_SYSERR, TEXT("Failed to convert admin group name to WideChar")); + goto out; + } + MultiByteToWideChar (CP_UTF8, 0, s->ovpn_admin_group, -1, tmp, len); + admin_group[1] = tmp; +#endif + + /* Check if user's groups include any of the admin groups */ + for (i = 0; i < nread; i++) + { + if ( wcscmp (groups[i].lgrui0_name, admin_group[0]) == 0 || + wcscmp (groups[i].lgrui0_name, admin_group[1]) == 0 + ) + { + MsgToEventLog (M_INFO, TEXT("Authorizing user %s by virtue of membership in group %s"), + username, groups[i].lgrui0_name); + ret = TRUE; + break; + } + } + +out: + if (groups) + NetApiBufferFree (groups); + free (tmp); + + return ret; +} + +/* + * Check whether option argv[0] is white-listed. If argv[0] == "--config", + * also check that argv[1], if present, passes CheckConfigPath(). + * The caller should set argc to the number of valid elements in argv[] array. + */ +BOOL +CheckOption (const WCHAR *workdir, int argc, WCHAR *argv[], const settings_t *s) +{ + /* Do not modify argv or *argv -- ideally it should be const WCHAR *const *, but alas...*/ + + if ( wcscmp (argv[0], L"--config") == 0 && + argc > 1 && + !CheckConfigPath (workdir, argv[1], s) + ) + { + return FALSE; + } + + /* option name starts at 2 characters from argv[i] */ + if (OptionLookup (argv[0] + 2, white_list) == -1) /* not found */ + return FALSE; + + return TRUE; +} diff --git a/src/openvpnserv/validate.h b/src/openvpnserv/validate.h new file mode 100644 index 0000000..0d0270a --- /dev/null +++ b/src/openvpnserv/validate.h @@ -0,0 +1,48 @@ + +/* + * 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) 2016 Selva Nair + * + * 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 + */ + +#ifndef VALIDATE_H +#define VALIDATE_H + +#include "service.h" + +/* Authorized groups who can use any options and config locations */ +#define SYSTEM_ADMIN_GROUP TEXT("Administrators") +#define OVPN_ADMIN_GROUP TEXT("OpenVPN Administrators") +/* The last one may be reset in registry: HKLM\Software\OpenVPN\ovpn_admin_group */ + +BOOL +IsAuthorizedUser (SID *sid, settings_t *s); + +BOOL +CheckOption (const WCHAR *workdir, int narg, WCHAR *argv[], const settings_t *s); + +static inline BOOL +IsOption (const WCHAR *o) +{ + return (wcsncmp (o, L"--", 2) == 0); +} + +#endif diff --git a/src/plugins/Makefile.in b/src/plugins/Makefile.in index cc26bf4..0d1d59b 100644 --- a/src/plugins/Makefile.in +++ b/src/plugins/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -25,17 +25,7 @@ # Copyright (C) 2006-2012 Alon Bar-Lev # VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -99,6 +89,7 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/m4/ax_socklen_t.m4 \ @@ -109,9 +100,9 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) @@ -169,7 +160,6 @@ am__define_uniq_tagged_files = \ ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) -am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -208,6 +198,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -242,25 +233,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -286,8 +283,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -302,6 +297,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -378,6 +378,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -673,8 +674,6 @@ uninstall-am: mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am -.PRECIOUS: Makefile - # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/plugins/auth-pam/Makefile.am b/src/plugins/auth-pam/Makefile.am index 2aef311..e6dc27e 100644 --- a/src/plugins/auth-pam/Makefile.am +++ b/src/plugins/auth-pam/Makefile.am @@ -18,6 +18,7 @@ dist_doc_DATA = README.auth-pam endif openvpn_plugin_auth_pam_la_SOURCES = \ + utils.c \ auth-pam.c \ pamdl.c pamdl.h \ auth-pam.exports diff --git a/src/plugins/auth-pam/Makefile.in b/src/plugins/auth-pam/Makefile.in index ee67141..90d5058 100644 --- a/src/plugins/auth-pam/Makefile.in +++ b/src/plugins/auth-pam/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -22,17 +22,7 @@ VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -96,6 +86,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/auth-pam +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(am__dist_doc_DATA_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/m4/ax_socklen_t.m4 \ @@ -106,10 +98,9 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__dist_doc_DATA_DIST) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -143,7 +134,7 @@ am__installdirs = "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(docdir)" LTLIBRARIES = $(plugin_LTLIBRARIES) am__DEPENDENCIES_1 = openvpn_plugin_auth_pam_la_DEPENDENCIES = $(am__DEPENDENCIES_1) -am_openvpn_plugin_auth_pam_la_OBJECTS = auth-pam.lo pamdl.lo +am_openvpn_plugin_auth_pam_la_OBJECTS = utils.lo auth-pam.lo pamdl.lo openvpn_plugin_auth_pam_la_OBJECTS = \ $(am_openvpn_plugin_auth_pam_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -168,7 +159,7 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -218,7 +209,6 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -232,6 +222,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -266,25 +257,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -310,8 +307,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -326,6 +321,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -394,6 +394,7 @@ AM_CFLAGS = \ @ENABLE_PLUGIN_AUTH_PAM_TRUE@plugin_LTLIBRARIES = openvpn-plugin-auth-pam.la @ENABLE_PLUGIN_AUTH_PAM_TRUE@dist_doc_DATA = README.auth-pam openvpn_plugin_auth_pam_la_SOURCES = \ + utils.c \ auth-pam.c \ pamdl.c pamdl.h \ auth-pam.exports @@ -421,6 +422,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/auth-pam/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/auth-pam/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -485,20 +487,21 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth-pam.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pamdl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -743,8 +746,6 @@ uninstall-am: uninstall-dist_docDATA uninstall-pluginLTLIBRARIES tags tags-am uninstall uninstall-am uninstall-dist_docDATA \ uninstall-pluginLTLIBRARIES -.PRECIOUS: Makefile - # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/plugins/auth-pam/auth-pam.c b/src/plugins/auth-pam/auth-pam.c index 710accc..5ad3ec8 100644 --- a/src/plugins/auth-pam/auth-pam.c +++ b/src/plugins/auth-pam/auth-pam.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -48,7 +47,7 @@ #include #include #include -#include +#include "utils.h" #include @@ -117,94 +116,6 @@ struct user_pass { /* Background process function */ static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list); -/* Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return - * a pointer to the NEW string. Does not modify the input strings. Will not enter an - * infinite loop with clever 'searchfor' and 'replacewith' strings. - * Daniel Johnson - Progman2000@usa.net / djohnson@progman.us - * - * Retuns NULL when - * - any parameter is NULL - * - the worst-case result is to large ( >= SIZE_MAX) - */ -static char * -searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith) -{ - if (!tosearch || !searchfor || !replacewith) return NULL; - - size_t tosearchlen = strlen(tosearch); - size_t replacewithlen = strlen(replacewith); - size_t templen = tosearchlen * replacewithlen; - - if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) { - return NULL; - } - - bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); - - if (is_potential_integer_overflow) { - return NULL; - } - - // state: all parameters are valid - - const char *searching=tosearch; - char *scratch; - - char temp[templen+1]; - temp[0]=0; - - scratch = strstr(searching,searchfor); - if (!scratch) return strdup(tosearch); - - while (scratch) { - strncat(temp,searching,scratch-searching); - strcat(temp,replacewith); - - searching=scratch+strlen(searchfor); - scratch = strstr(searching,searchfor); - } - return strdup(temp); -} - -/* - * Given an environmental variable name, search - * the envp array for its value, returning it - * if found or NULL otherwise. - */ -static const char * -get_env (const char *name, const char *envp[]) -{ - if (envp) - { - int i; - const int namelen = strlen (name); - for (i = 0; envp[i]; ++i) - { - if (!strncmp (envp[i], name, namelen)) - { - const char *cp = envp[i] + namelen; - if (*cp == '=') - return cp + 1; - } - } - } - return NULL; -} - -/* - * Return the length of a string array - */ -static int -string_array_len (const char *array[]) -{ - int i = 0; - if (array) - { - while (array[i]) - ++i; - } - return i; -} /* * Socket read/write functions. diff --git a/src/plugins/auth-pam/utils.c b/src/plugins/auth-pam/utils.c new file mode 100644 index 0000000..4f2bec1 --- /dev/null +++ b/src/plugins/auth-pam/utils.c @@ -0,0 +1,113 @@ +/* + * 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. + * + * 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 + */ + +/* + * OpenVPN plugin module to do PAM authentication using a split + * privilege model. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +char * +searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith) +{ + if (!tosearch || !searchfor || !replacewith) return NULL; + + size_t tosearchlen = strlen(tosearch); + size_t replacewithlen = strlen(replacewith); + size_t templen = tosearchlen * replacewithlen; + + if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) { + return NULL; + } + + bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); + + if (is_potential_integer_overflow) { + return NULL; + } + + // state: all parameters are valid + + const char *searching=tosearch; + char *scratch; + + char temp[templen+1]; + temp[0]=0; + + scratch = strstr(searching,searchfor); + if (!scratch) return strdup(tosearch); + + while (scratch) { + strncat(temp,searching,scratch-searching); + strcat(temp,replacewith); + + searching=scratch+strlen(searchfor); + scratch = strstr(searching,searchfor); + } + return strdup(temp); +} + +const char * +get_env (const char *name, const char *envp[]) +{ + if (envp) + { + int i; + const int namelen = strlen (name); + for (i = 0; envp[i]; ++i) + { + if (!strncmp (envp[i], name, namelen)) + { + const char *cp = envp[i] + namelen; + if (*cp == '=') + return cp + 1; + } + } + } + return NULL; +} + +int +string_array_len (const char *array[]) +{ + int i = 0; + if (array) + { + while (array[i]) + ++i; + } + return i; +} diff --git a/src/plugins/auth-pam/utils.h b/src/plugins/auth-pam/utils.h new file mode 100644 index 0000000..2f72040 --- /dev/null +++ b/src/plugins/auth-pam/utils.h @@ -0,0 +1,66 @@ +/* + * 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. + * + * 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 + */ + +#ifndef _PLUGIN_AUTH_PAM_UTILS__H +#define _PLUGIN_AUTH_PAM_UTILS__H + +/** + * Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return + * a pointer to the NEW string. Does not modify the input strings. Will not enter an + * infinite loop with clever 'searchfor' and 'replacewith' strings. + * + * @author Daniel Johnson - Progman2000@usa.net / djohnson@progman.us + * + * @param tosearch haystack to search in + * @param searchfor needle to search for in the haystack + * @param replacewith when a match is found, replace needle with this string + * + * @return Retuns NULL when any parameter is NULL or the worst-case result is to large ( >= SIZE_MAX). + * Otherwise it returns a pointer to a new buffer containing the modified input + */ +char * +searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith); + +/** + * Given an environmental variable name, search + * the envp array for its value + * + * @param name Environment variable to look up + * @param envp Environment variable table with all key/value pairs + * + * @return Returns a pointer to the value of the enviroment variable if found, otherwise NULL is returned. + */ +const char * +get_env (const char *name, const char *envp[]); + +/** + * Return the length of a string array + * + * @param array Pointer to the array to calculate size of + * + */ +int +string_array_len (const char *array[]); + +#endif diff --git a/src/plugins/down-root/Makefile.in b/src/plugins/down-root/Makefile.in index c9768d5..e5c0ad5 100644 --- a/src/plugins/down-root/Makefile.in +++ b/src/plugins/down-root/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -22,17 +22,7 @@ VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -96,6 +86,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/plugins/down-root +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(am__dist_doc_DATA_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/m4/ax_socklen_t.m4 \ @@ -106,10 +98,9 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__dist_doc_DATA_DIST) \ - $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -167,7 +158,7 @@ AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -217,7 +208,6 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -231,6 +221,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CMAKE = @CMAKE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -265,25 +256,31 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ LZO_CFLAGS = @LZO_CFLAGS@ LZO_LIBS = @LZO_LIBS@ MAKEINFO = @MAKEINFO@ MAN2HTML = @MAN2HTML@ MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ MKDIR_P = @MKDIR_P@ NETSTAT = @NETSTAT@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OPENSSL_CRYPTO_CFLAGS = @OPENSSL_CRYPTO_CFLAGS@ -OPENSSL_CRYPTO_LIBS = @OPENSSL_CRYPTO_LIBS@ -OPENSSL_SSL_CFLAGS = @OPENSSL_SSL_CFLAGS@ -OPENSSL_SSL_LIBS = @OPENSSL_SSL_LIBS@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ @@ -309,8 +306,6 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -POLARSSL_CFLAGS = @POLARSSL_CFLAGS@ -POLARSSL_LIBS = @POLARSSL_LIBS@ RANLIB = @RANLIB@ RC = @RC@ ROUTE = @ROUTE@ @@ -325,6 +320,11 @@ TAP_CFLAGS = @TAP_CFLAGS@ TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +VENDOR_BUILD_ROOT = @VENDOR_BUILD_ROOT@ +VENDOR_DIST_ROOT = @VENDOR_DIST_ROOT@ +VENDOR_SRC_ROOT = @VENDOR_SRC_ROOT@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ @@ -415,6 +415,7 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/down-root/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/plugins/down-root/Makefile +.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -484,14 +485,14 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -736,8 +737,6 @@ uninstall-am: uninstall-dist_docDATA uninstall-pluginLTLIBRARIES tags tags-am uninstall uninstall-am uninstall-dist_docDATA \ uninstall-pluginLTLIBRARIES -.PRECIOUS: Makefile - # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. -- cgit v1.2.3