diff options
author | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
---|---|---|
committer | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
commit | 1079962e4c06f88a54e50d997c1b7e84303d30b4 (patch) | |
tree | 4d019426928435425214ccedd6f89b70dbdf035d /src/openvpn | |
parent | 620785fe268a1221c1ba7a9cb5a70f3140a4f1ca (diff) |
New upstream version 2.5~beta1upstream/2.5_beta1
Diffstat (limited to 'src/openvpn')
147 files changed, 16180 insertions, 9454 deletions
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index 0ff23ba..37b002c 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -16,7 +16,8 @@ MAINTAINERCLEANFILES = \ EXTRA_DIST = \ openvpn.vcxproj \ - openvpn.vcxproj.filters + openvpn.vcxproj.filters \ + openvpn.manifest AM_CPPFLAGS = \ -I$(top_srcdir)/include \ @@ -40,6 +41,7 @@ sbin_PROGRAMS = openvpn openvpn_SOURCES = \ argv.c argv.h \ + auth_token.c auth_token.h \ base64.c base64.h \ basic.h \ buffer.c buffer.h \ @@ -52,11 +54,12 @@ openvpn_SOURCES = \ crypto_openssl.c crypto_openssl.h \ crypto_mbedtls.c crypto_mbedtls.h \ dhcp.c dhcp.h \ + env_set.c env_set.h \ errlevel.h \ error.c error.h \ event.c event.h \ fdmisc.c fdmisc.h \ - forward.c forward.h forward-inline.h \ + forward.c forward.h \ fragment.c fragment.h \ gremlin.c gremlin.h \ helper.c helper.h \ @@ -80,8 +83,11 @@ openvpn_SOURCES = \ mtu.c mtu.h \ mudp.c mudp.h \ multi.c multi.h \ + networking_iproute2.c networking_iproute2.h \ + networking_sitnl.c networking_sitnl.h \ + networking.h \ ntlm.c ntlm.h \ - occ.c occ.h occ-inline.h \ + occ.c occ.h \ openssl_compat.h \ pkcs11.c pkcs11.h pkcs11_backend.h \ pkcs11_openssl.c \ @@ -91,8 +97,8 @@ openvpn_SOURCES = \ 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 \ + pf.c pf.h \ + ping.c ping.h \ plugin.c plugin.h \ pool.c pool.h \ proto.c proto.h \ @@ -102,6 +108,7 @@ openvpn_SOURCES = \ pushlist.h \ reliable.c reliable.h \ route.c route.h \ + run_command.c run_command.h \ schedule.c schedule.h \ session_id.c session_id.h \ shaper.c shaper.h \ @@ -111,6 +118,7 @@ openvpn_SOURCES = \ ssl.c ssl.h ssl_backend.h \ ssl_openssl.c ssl_openssl.h \ ssl_mbedtls.c ssl_mbedtls.h \ + ssl_ncp.c ssl_ncp.h \ ssl_common.h \ ssl_verify.c ssl_verify.h ssl_verify_backend.h \ ssl_verify_openssl.c ssl_verify_openssl.h \ @@ -119,6 +127,7 @@ openvpn_SOURCES = \ syshead.h \ tls_crypt.c tls_crypt.h \ tun.c tun.h \ + vlan.c vlan.h \ win32.h win32.c \ cryptoapi.h cryptoapi.c openvpn_LDADD = \ @@ -133,6 +142,6 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) \ $(OPTIONAL_INOTIFY_LIBS) if WIN32 -openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt +openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h ring_buffer.h +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -lsetupapi endif diff --git a/src/openvpn/Makefile.in b/src/openvpn/Makefile.in deleted file mode 100644 index 963f6ab..0000000 --- a/src/openvpn/Makefile.in +++ /dev/null @@ -1,1112 +0,0 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2018 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# -# 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-2018 OpenVPN Inc <sales@openvpn.net> -# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com> -# - -# -# 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) 2008-2012 Alon Bar-Lev <alon.barlev@gmail.com> -# -# 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__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ - -# 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 block_dns.c block_dns.h -@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -subdir = src/openvpn -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ - $(top_srcdir)/m4/ax_socklen_t.m4 \ - $(top_srcdir)/m4/ax_varargs.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/version.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 \ - $(top_builddir)/include/openvpn-plugin.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -am__installdirs = "$(DESTDIR)$(sbindir)" -PROGRAMS = $(sbin_PROGRAMS) -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 \ - 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 \ - openssl_compat.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) 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 = -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_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 = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -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) -I$(top_builddir)/include -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/argv.Po ./$(DEPDIR)/base64.Po \ - ./$(DEPDIR)/block_dns.Po ./$(DEPDIR)/buffer.Po \ - ./$(DEPDIR)/clinat.Po ./$(DEPDIR)/comp-lz4.Po \ - ./$(DEPDIR)/comp.Po ./$(DEPDIR)/compstub.Po \ - ./$(DEPDIR)/console.Po ./$(DEPDIR)/console_builtin.Po \ - ./$(DEPDIR)/console_systemd.Po ./$(DEPDIR)/crypto.Po \ - ./$(DEPDIR)/crypto_mbedtls.Po ./$(DEPDIR)/crypto_openssl.Po \ - ./$(DEPDIR)/cryptoapi.Po ./$(DEPDIR)/dhcp.Po \ - ./$(DEPDIR)/error.Po ./$(DEPDIR)/event.Po \ - ./$(DEPDIR)/fdmisc.Po ./$(DEPDIR)/forward.Po \ - ./$(DEPDIR)/fragment.Po ./$(DEPDIR)/gremlin.Po \ - ./$(DEPDIR)/helper.Po ./$(DEPDIR)/httpdigest.Po \ - ./$(DEPDIR)/init.Po ./$(DEPDIR)/interval.Po \ - ./$(DEPDIR)/list.Po ./$(DEPDIR)/lladdr.Po ./$(DEPDIR)/lzo.Po \ - ./$(DEPDIR)/manage.Po ./$(DEPDIR)/mbuf.Po ./$(DEPDIR)/misc.Po \ - ./$(DEPDIR)/mroute.Po ./$(DEPDIR)/mss.Po ./$(DEPDIR)/mstats.Po \ - ./$(DEPDIR)/mtcp.Po ./$(DEPDIR)/mtu.Po ./$(DEPDIR)/mudp.Po \ - ./$(DEPDIR)/multi.Po ./$(DEPDIR)/ntlm.Po ./$(DEPDIR)/occ.Po \ - ./$(DEPDIR)/openvpn.Po ./$(DEPDIR)/options.Po \ - ./$(DEPDIR)/otime.Po ./$(DEPDIR)/packet_id.Po \ - ./$(DEPDIR)/perf.Po ./$(DEPDIR)/pf.Po ./$(DEPDIR)/ping.Po \ - ./$(DEPDIR)/pkcs11.Po ./$(DEPDIR)/pkcs11_mbedtls.Po \ - ./$(DEPDIR)/pkcs11_openssl.Po ./$(DEPDIR)/platform.Po \ - ./$(DEPDIR)/plugin.Po ./$(DEPDIR)/pool.Po ./$(DEPDIR)/proto.Po \ - ./$(DEPDIR)/proxy.Po ./$(DEPDIR)/ps.Po ./$(DEPDIR)/push.Po \ - ./$(DEPDIR)/reliable.Po ./$(DEPDIR)/route.Po \ - ./$(DEPDIR)/schedule.Po ./$(DEPDIR)/session_id.Po \ - ./$(DEPDIR)/shaper.Po ./$(DEPDIR)/sig.Po ./$(DEPDIR)/socket.Po \ - ./$(DEPDIR)/socks.Po ./$(DEPDIR)/ssl.Po \ - ./$(DEPDIR)/ssl_mbedtls.Po ./$(DEPDIR)/ssl_openssl.Po \ - ./$(DEPDIR)/ssl_verify.Po ./$(DEPDIR)/ssl_verify_mbedtls.Po \ - ./$(DEPDIR)/ssl_verify_openssl.Po ./$(DEPDIR)/status.Po \ - ./$(DEPDIR)/tls_crypt.Po ./$(DEPDIR)/tun.Po \ - ./$(DEPDIR)/win32.Po -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(openvpn_SOURCES) -DIST_SOURCES = $(am__openvpn_SOURCES_DIST) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - 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@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -AR = @AR@ -AS = @AS@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CMAKE = @CMAKE@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DL_LIBS = @DL_LIBS@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GIT = @GIT@ -GREP = @GREP@ -IFCONFIG = @IFCONFIG@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -IPROUTE = @IPROUTE@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBOBJS = @LIBOBJS@ -LIBPAM_CFLAGS = @LIBPAM_CFLAGS@ -LIBPAM_LIBS = @LIBPAM_LIBS@ -LIBS = @LIBS@ -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_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_INOTIFY_CFLAGS = @OPTIONAL_INOTIFY_CFLAGS@ -OPTIONAL_INOTIFY_LIBS = @OPTIONAL_INOTIFY_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@ -OPTIONAL_PKCS11_HELPER_LIBS = @OPTIONAL_PKCS11_HELPER_LIBS@ -OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ -OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -P11KIT_CFLAGS = @P11KIT_CFLAGS@ -P11KIT_LIBS = @P11KIT_LIBS@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKCS11_HELPER_CFLAGS = @PKCS11_HELPER_CFLAGS@ -PKCS11_HELPER_LIBS = @PKCS11_HELPER_LIBS@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PLUGINDIR = @PLUGINDIR@ -PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ -PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ -RANLIB = @RANLIB@ -RC = @RC@ -ROUTE = @ROUTE@ -SED = @SED@ -SELINUX_LIBS = @SELINUX_LIBS@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -SOCKETS_LIBS = @SOCKETS_LIBS@ -STRIP = @STRIP@ -SYSTEMD_ASK_PASSWORD = @SYSTEMD_ASK_PASSWORD@ -SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ -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@ -TMPFILES_DIR = @TMPFILES_DIR@ -VERSION = @VERSION@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -libsystemd_CFLAGS = @libsystemd_CFLAGS@ -libsystemd_LIBS = @libsystemd_LIBS@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -plugindir = @plugindir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -sampledir = @sampledir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -systemdunitdir = @systemdunitdir@ -target_alias = @target_alias@ -tmpfilesdir = @tmpfilesdir@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) - -LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) -MAINTAINERCLEANFILES = \ - $(srcdir)/Makefile.in - -EXTRA_DIST = \ - openvpn.vcxproj \ - openvpn.vcxproj.filters - -AM_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/src/compat - -AM_CFLAGS = $(TAP_CFLAGS) $(OPTIONAL_CRYPTO_CFLAGS) \ - $(OPTIONAL_LZO_CFLAGS) $(OPTIONAL_LZ4_CFLAGS) \ - $(OPTIONAL_PKCS11_HELPER_CFLAGS) $(OPTIONAL_INOTIFY_CFLAGS) \ - -DPLUGIN_LIBDIR=\"${plugindir}\" $(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_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 \ - 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 \ - openssl_compat.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) $(OPTIONAL_LZ4_LIBS) \ - $(OPTIONAL_PKCS11_HELPER_LIBS) $(OPTIONAL_CRYPTO_LIBS) \ - $(OPTIONAL_SELINUX_LIBS) $(OPTIONAL_SYSTEMD_LIBS) \ - $(OPTIONAL_DL_LIBS) $(OPTIONAL_INOTIFY_LIBS) $(am__append_3) -all: all-am - -.SUFFIXES: -.SUFFIXES: .c .lo .mc .o .obj .rc -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpn/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign src/openvpn/Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ - esac; -$(top_srcdir)/build/ltrc.inc $(am__empty): - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -install-sbinPROGRAMS: $(sbin_PROGRAMS) - @$(NORMAL_INSTALL) - @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ - fi; \ - for p in $$list; do echo "$$p $$p"; done | \ - sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p \ - || test -f $$p1 \ - ; then echo "$$p"; echo "$$p"; else :; fi; \ - done | \ - sed -e 'p;s,.*/,,;n;h' \ - -e 's|.*|.|' \ - -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ - sed 'N;N;N;s,\n, ,g' | \ - $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ - { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ - if ($$2 == $$4) files[d] = files[d] " " $$1; \ - else { print "f", $$3 "/" $$4, $$1; } } \ - END { for (d in files) print "f", d, files[d] }' | \ - while read type dir files; do \ - if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ - test -z "$$files" || { \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ - } \ - ; done - -uninstall-sbinPROGRAMS: - @$(NORMAL_UNINSTALL) - @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ - files=`for p in $$list; do echo "$$p"; done | \ - sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' \ - `; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(sbindir)" && rm -f $$files - -clean-sbinPROGRAMS: - @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -openvpn$(EXEEXT): $(openvpn_OBJECTS) $(openvpn_DEPENDENCIES) $(EXTRA_openvpn_DEPENDENCIES) - @rm -f openvpn$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(openvpn_OBJECTS) $(openvpn_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/block_dns.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clinat.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp-lz4.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compstub.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console_builtin.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console_systemd.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_mbedtls.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_openssl.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoapi.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdmisc.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/forward.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fragment.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gremlin.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpdigest.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interval.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lladdr.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lzo.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manage.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbuf.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mroute.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mss.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mstats.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mtcp.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mtu.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mudp.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multi.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlm.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/occ.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpn.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otime.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pf.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ping.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_mbedtls.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_openssl.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ps.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reliable.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/schedule.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_id.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shaper.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_mbedtls.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_openssl.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify_mbedtls.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_verify_openssl.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tun.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32.Po@am__quote@ # am--include-marker - -$(am__depfiles_remade): - @$(MKDIR_P) $(@D) - @echo '# dummy' >$@-t && $(am__mv) $@-t $@ - -am--depfiles: $(am__depfiles_remade) - -.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 $@ $< - -.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) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) distdir-am - -distdir-am: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(PROGRAMS) -installdirs: - for dir in "$(DESTDIR)$(sbindir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." - -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) -clean: clean-am - -clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ - mostlyclean-am - -distclean: distclean-am - -rm -f ./$(DEPDIR)/argv.Po - -rm -f ./$(DEPDIR)/base64.Po - -rm -f ./$(DEPDIR)/block_dns.Po - -rm -f ./$(DEPDIR)/buffer.Po - -rm -f ./$(DEPDIR)/clinat.Po - -rm -f ./$(DEPDIR)/comp-lz4.Po - -rm -f ./$(DEPDIR)/comp.Po - -rm -f ./$(DEPDIR)/compstub.Po - -rm -f ./$(DEPDIR)/console.Po - -rm -f ./$(DEPDIR)/console_builtin.Po - -rm -f ./$(DEPDIR)/console_systemd.Po - -rm -f ./$(DEPDIR)/crypto.Po - -rm -f ./$(DEPDIR)/crypto_mbedtls.Po - -rm -f ./$(DEPDIR)/crypto_openssl.Po - -rm -f ./$(DEPDIR)/cryptoapi.Po - -rm -f ./$(DEPDIR)/dhcp.Po - -rm -f ./$(DEPDIR)/error.Po - -rm -f ./$(DEPDIR)/event.Po - -rm -f ./$(DEPDIR)/fdmisc.Po - -rm -f ./$(DEPDIR)/forward.Po - -rm -f ./$(DEPDIR)/fragment.Po - -rm -f ./$(DEPDIR)/gremlin.Po - -rm -f ./$(DEPDIR)/helper.Po - -rm -f ./$(DEPDIR)/httpdigest.Po - -rm -f ./$(DEPDIR)/init.Po - -rm -f ./$(DEPDIR)/interval.Po - -rm -f ./$(DEPDIR)/list.Po - -rm -f ./$(DEPDIR)/lladdr.Po - -rm -f ./$(DEPDIR)/lzo.Po - -rm -f ./$(DEPDIR)/manage.Po - -rm -f ./$(DEPDIR)/mbuf.Po - -rm -f ./$(DEPDIR)/misc.Po - -rm -f ./$(DEPDIR)/mroute.Po - -rm -f ./$(DEPDIR)/mss.Po - -rm -f ./$(DEPDIR)/mstats.Po - -rm -f ./$(DEPDIR)/mtcp.Po - -rm -f ./$(DEPDIR)/mtu.Po - -rm -f ./$(DEPDIR)/mudp.Po - -rm -f ./$(DEPDIR)/multi.Po - -rm -f ./$(DEPDIR)/ntlm.Po - -rm -f ./$(DEPDIR)/occ.Po - -rm -f ./$(DEPDIR)/openvpn.Po - -rm -f ./$(DEPDIR)/options.Po - -rm -f ./$(DEPDIR)/otime.Po - -rm -f ./$(DEPDIR)/packet_id.Po - -rm -f ./$(DEPDIR)/perf.Po - -rm -f ./$(DEPDIR)/pf.Po - -rm -f ./$(DEPDIR)/ping.Po - -rm -f ./$(DEPDIR)/pkcs11.Po - -rm -f ./$(DEPDIR)/pkcs11_mbedtls.Po - -rm -f ./$(DEPDIR)/pkcs11_openssl.Po - -rm -f ./$(DEPDIR)/platform.Po - -rm -f ./$(DEPDIR)/plugin.Po - -rm -f ./$(DEPDIR)/pool.Po - -rm -f ./$(DEPDIR)/proto.Po - -rm -f ./$(DEPDIR)/proxy.Po - -rm -f ./$(DEPDIR)/ps.Po - -rm -f ./$(DEPDIR)/push.Po - -rm -f ./$(DEPDIR)/reliable.Po - -rm -f ./$(DEPDIR)/route.Po - -rm -f ./$(DEPDIR)/schedule.Po - -rm -f ./$(DEPDIR)/session_id.Po - -rm -f ./$(DEPDIR)/shaper.Po - -rm -f ./$(DEPDIR)/sig.Po - -rm -f ./$(DEPDIR)/socket.Po - -rm -f ./$(DEPDIR)/socks.Po - -rm -f ./$(DEPDIR)/ssl.Po - -rm -f ./$(DEPDIR)/ssl_mbedtls.Po - -rm -f ./$(DEPDIR)/ssl_openssl.Po - -rm -f ./$(DEPDIR)/ssl_verify.Po - -rm -f ./$(DEPDIR)/ssl_verify_mbedtls.Po - -rm -f ./$(DEPDIR)/ssl_verify_openssl.Po - -rm -f ./$(DEPDIR)/status.Po - -rm -f ./$(DEPDIR)/tls_crypt.Po - -rm -f ./$(DEPDIR)/tun.Po - -rm -f ./$(DEPDIR)/win32.Po - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: install-sbinPROGRAMS - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f ./$(DEPDIR)/argv.Po - -rm -f ./$(DEPDIR)/base64.Po - -rm -f ./$(DEPDIR)/block_dns.Po - -rm -f ./$(DEPDIR)/buffer.Po - -rm -f ./$(DEPDIR)/clinat.Po - -rm -f ./$(DEPDIR)/comp-lz4.Po - -rm -f ./$(DEPDIR)/comp.Po - -rm -f ./$(DEPDIR)/compstub.Po - -rm -f ./$(DEPDIR)/console.Po - -rm -f ./$(DEPDIR)/console_builtin.Po - -rm -f ./$(DEPDIR)/console_systemd.Po - -rm -f ./$(DEPDIR)/crypto.Po - -rm -f ./$(DEPDIR)/crypto_mbedtls.Po - -rm -f ./$(DEPDIR)/crypto_openssl.Po - -rm -f ./$(DEPDIR)/cryptoapi.Po - -rm -f ./$(DEPDIR)/dhcp.Po - -rm -f ./$(DEPDIR)/error.Po - -rm -f ./$(DEPDIR)/event.Po - -rm -f ./$(DEPDIR)/fdmisc.Po - -rm -f ./$(DEPDIR)/forward.Po - -rm -f ./$(DEPDIR)/fragment.Po - -rm -f ./$(DEPDIR)/gremlin.Po - -rm -f ./$(DEPDIR)/helper.Po - -rm -f ./$(DEPDIR)/httpdigest.Po - -rm -f ./$(DEPDIR)/init.Po - -rm -f ./$(DEPDIR)/interval.Po - -rm -f ./$(DEPDIR)/list.Po - -rm -f ./$(DEPDIR)/lladdr.Po - -rm -f ./$(DEPDIR)/lzo.Po - -rm -f ./$(DEPDIR)/manage.Po - -rm -f ./$(DEPDIR)/mbuf.Po - -rm -f ./$(DEPDIR)/misc.Po - -rm -f ./$(DEPDIR)/mroute.Po - -rm -f ./$(DEPDIR)/mss.Po - -rm -f ./$(DEPDIR)/mstats.Po - -rm -f ./$(DEPDIR)/mtcp.Po - -rm -f ./$(DEPDIR)/mtu.Po - -rm -f ./$(DEPDIR)/mudp.Po - -rm -f ./$(DEPDIR)/multi.Po - -rm -f ./$(DEPDIR)/ntlm.Po - -rm -f ./$(DEPDIR)/occ.Po - -rm -f ./$(DEPDIR)/openvpn.Po - -rm -f ./$(DEPDIR)/options.Po - -rm -f ./$(DEPDIR)/otime.Po - -rm -f ./$(DEPDIR)/packet_id.Po - -rm -f ./$(DEPDIR)/perf.Po - -rm -f ./$(DEPDIR)/pf.Po - -rm -f ./$(DEPDIR)/ping.Po - -rm -f ./$(DEPDIR)/pkcs11.Po - -rm -f ./$(DEPDIR)/pkcs11_mbedtls.Po - -rm -f ./$(DEPDIR)/pkcs11_openssl.Po - -rm -f ./$(DEPDIR)/platform.Po - -rm -f ./$(DEPDIR)/plugin.Po - -rm -f ./$(DEPDIR)/pool.Po - -rm -f ./$(DEPDIR)/proto.Po - -rm -f ./$(DEPDIR)/proxy.Po - -rm -f ./$(DEPDIR)/ps.Po - -rm -f ./$(DEPDIR)/push.Po - -rm -f ./$(DEPDIR)/reliable.Po - -rm -f ./$(DEPDIR)/route.Po - -rm -f ./$(DEPDIR)/schedule.Po - -rm -f ./$(DEPDIR)/session_id.Po - -rm -f ./$(DEPDIR)/shaper.Po - -rm -f ./$(DEPDIR)/sig.Po - -rm -f ./$(DEPDIR)/socket.Po - -rm -f ./$(DEPDIR)/socks.Po - -rm -f ./$(DEPDIR)/ssl.Po - -rm -f ./$(DEPDIR)/ssl_mbedtls.Po - -rm -f ./$(DEPDIR)/ssl_openssl.Po - -rm -f ./$(DEPDIR)/ssl_verify.Po - -rm -f ./$(DEPDIR)/ssl_verify_mbedtls.Po - -rm -f ./$(DEPDIR)/ssl_verify_openssl.Po - -rm -f ./$(DEPDIR)/status.Po - -rm -f ./$(DEPDIR)/tls_crypt.Po - -rm -f ./$(DEPDIR)/tun.Po - -rm -f ./$(DEPDIR)/win32.Po - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-sbinPROGRAMS - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ - clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \ - ctags ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-sbinPROGRAMS install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - 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 "$@" - -.rc.o: - $(RCCOMPILE) -i "$<" -o "$@" - -.mc.rc: - $(WINDMC) "$<" - -# 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. -.NOEXPORT: diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c index 7d06951..b799c97 100644 --- a/src/openvpn/argv.c +++ b/src/openvpn/argv.c @@ -37,16 +37,55 @@ #include "argv.h" #include "integer.h" +#include "env_set.h" #include "options.h" +/** + * Resizes the list of arguments struct argv can carry. This resize + * operation will only increase the size, never decrease the size. + * + * @param *a Valid pointer to a struct argv to resize + * @param newcap size_t with the new size of the argument list. + */ +static void +argv_extend(struct argv *a, const size_t newcap) +{ + if (newcap > a->capacity) + { + char **newargv; + size_t i; + ALLOC_ARRAY_CLEAR_GC(newargv, char *, newcap, &a->gc); + for (i = 0; i < a->argc; ++i) + { + newargv[i] = a->argv[i]; + } + a->argv = newargv; + a->capacity = newcap; + } +} + +/** + * Initialise an already allocated struct argv. + * It is expected that the input argument is a valid pointer. + * + * @param *a Pointer to a struct argv to initialise + */ static void argv_init(struct argv *a) { a->capacity = 0; a->argc = 0; a->argv = NULL; + a->gc = gc_new(); + argv_extend(a, 8); } +/** + * Allocates a new struct argv and ensures it is initialised. + * Note that it does not return a pointer, but a struct argv directly. + * + * @returns Returns an initialised and empty struct argv. + */ struct argv argv_new(void) { @@ -55,36 +94,51 @@ argv_new(void) return ret; } +/** + * Frees all memory allocations allocated by the struct argv + * related functions. + * + * @param *a Valid pointer to a struct argv to release memory from + */ void -argv_reset(struct argv *a) +argv_free(struct argv *a) { - size_t i; - for (i = 0; i < a->argc; ++i) - { - free(a->argv[i]); - } - free(a->argv); - argv_init(a); + gc_free(&a->gc); } +/** + * Resets the struct argv to an initial state. No memory buffers + * will be released by this call. + * + * @param *a Valid pointer to a struct argv to resize + */ static void -argv_extend(struct argv *a, const size_t newcap) +argv_reset(struct argv *a) { - if (newcap > a->capacity) + if (a->argc) { - char **newargv; size_t i; - ALLOC_ARRAY_CLEAR(newargv, char *, newcap); for (i = 0; i < a->argc; ++i) { - newargv[i] = a->argv[i]; + a->argv[i] = NULL; } - free(a->argv); - a->argv = newargv; - a->capacity = newcap; + a->argc = 0; } } +/** + * Extends an existing struct argv to carry minimum 'add' number + * of new arguments. This builds on argv_extend(), which ensures the + * new size will only be higher than the current capacity. + * + * The new size is also calculated based on the result of adjust_power_of_2(). + * This approach ensures that the list does grow bulks and only when the + * current limit is reached. + * + * @param *a Valid pointer to the struct argv to extend + * @param add size_t with the number of elements to add. + * + */ static void argv_grow(struct argv *a, const size_t add) { @@ -93,114 +147,100 @@ argv_grow(struct argv *a, const size_t add) argv_extend(a, adjust_power_of_2(newargc)); } +/** + * Appends a string to to the list of arguments stored in a struct argv + * This will ensure the list size in struct argv has the needed capacity to + * store the value. + * + * @param *a struct argv where to append the new string value + * @param *str Pointer to string to append. The provided string *MUST* have + * been malloc()ed or NULL. + */ static void -argv_append(struct argv *a, char *str) /* str must have been malloced or be NULL */ +argv_append(struct argv *a, char *str) { argv_grow(a, 1); a->argv[a->argc++] = str; } +/** + * Clones a struct argv with all the contents to a new allocated struct argv. + * If 'headroom' is larger than 0, it will create a head-room in front of the + * values being copied from the source input. + * + * + * @param *source Valid pointer to the source struct argv to clone. It may + * be NULL. + * @param headroom Number of slots to leave empty in front of the slots + * copied from the source. + * + * @returns Returns a new struct argv containing a copy of the source + * struct argv, with the given headroom in front of the copy. + * + */ static struct argv -argv_clone(const struct argv *a, const size_t headroom) +argv_clone(const struct argv *source, const size_t headroom) { struct argv r; - size_t i; - argv_init(&r); - for (i = 0; i < headroom; ++i) + + for (size_t i = 0; i < headroom; ++i) { argv_append(&r, NULL); } - if (a) + if (source) { - for (i = 0; i < a->argc; ++i) + for (size_t i = 0; i < source->argc; ++i) { - argv_append(&r, string_alloc(a->argv[i], NULL)); + argv_append(&r, string_alloc(source->argv[i], &r.gc)); } } return r; } +/** + * Inserts an argument string in front of all other argument slots. + * + * @param *a Valid pointer to the struct argv to insert the argument into + * @param *head Pointer to the char * string with the argument to insert + * + * @returns Returns a new struct argv with the inserted argument in front + */ 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); + r.argv[0] = string_alloc(head, &r.gc); 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; - } -} - +/** + * Generate a single string with all the arguments in a struct argv + * concatenated. + * + * @param *a Valid pointer to the struct argv with the arguments to list + * @param *gc Pointer to a struct gc_arena managed buffer + * @param flags Flags passed to the print_argv() function. + * + * @returns Returns a string generated by print_argv() with all the arguments + * concatenated. If the argument count is 0, it will return an empty + * string. The return string is allocated in the gc_arena managed + * buffer. If the gc_arena pointer is NULL, the returned string + * must be free()d explicitly to avoid memory leaks. + */ 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 ""; - } + return print_argv((const char **)a->argv, gc, flags); } +/** + * Write the arguments stored in a struct argv via the msg() command. + * + * @param msglev Integer with the message level used by msg(). + * @param *a Valid pointer to the struct argv with the arguments to write. + */ void argv_msg(const int msglev, const struct argv *a) { @@ -209,6 +249,15 @@ argv_msg(const int msglev, const struct argv *a) gc_free(&gc); } +/** + * Similar to argv_msg() but prefixes the messages being written with a + * given string. + * + * @param msglev Integer with the message level used by msg(). + * @param *a Valid pointer to the struct argv with the arguments to write + * @param *prefix Valid char * pointer to the prefix string + * + */ void argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) { @@ -217,144 +266,239 @@ argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix) gc_free(&gc); } -static void -argv_printf_arglist(struct argv *a, const char *format, va_list arglist) +/** + * Prepares argv format string for further processing + * + * Individual argument must be separated by space. Ignores leading and + * trailing spaces. Consecutive spaces count as one. Returns prepared + * format string, with space replaced by delim and adds the number of + * arguments to the count parameter. + * + * @param *format Pointer to a the format string to process + * @param delim Char with the delimiter to use + * @param *count size_t pointer used to return the number of + * tokens (argument slots) found in the format string. + * @param *gc Pointer to a gc_arena managed buffer. + * + * @returns Returns a parsed format string (char *), together with the + * number of tokens parts found (via *count). The result string + * is allocated within the gc_arena managed buffer. If the + * gc_arena pointer is NULL, the returned string must be explicitly + * free()d to avoid memory leaks. + */ +static char * +argv_prep_format(const char *format, const char delim, size_t *count, + struct gc_arena *gc) { - char *term; - const char *f = format; - - argv_extend(a, 1); /* ensure trailing NULL */ + if (format == NULL) + { + return NULL; + } - while ((term = argv_term(&f)) != NULL) + bool in_token = false; + char *f = gc_malloc(strlen(format) + 1, true, gc); + for (int i = 0, j = 0; i < strlen(format); i++) { - if (term[0] == '%') + if (format[i] == ' ') { - 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, "%lu")) - { - char numstr[64]; - openvpn_snprintf(numstr, sizeof(numstr), "%lu", - va_arg(arglist, unsigned long)); - 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); + in_token = false; + continue; } - else + + if (!in_token) { - argv_append(a, term); + (*count)++; + + /* + * We don't add any delimiter to the output string if + * the string is empty; the resulting format string + * will never start with a delimiter. + */ + if (j > 0) /* Has anything been written to the output string? */ + { + f[j++] = delim; + } } + + f[j++] = format[i]; + in_token = true; } + + return f; } -void -argv_printf(struct argv *a, const char *format, ...) +/** + * Create a struct argv based on a format string + * + * Instead of parsing the format string ourselves place delimiters via + * argv_prep_format() before we let libc's printf() do the parsing. + * Then split the resulting string at the injected delimiters. + * + * @param *argres Valid pointer to a struct argv where the resulting parsed + * arguments, based on the format string. + * @param *format Char* string with a printf() compliant format string + * @param arglist A va_list with the arguments to be consumed by the format + * string + * + * @returns Returns true if the parsing and processing was successfully. If + * the resulting number of arguments does not match the expected + * number of arguments (based on the format string), it is + * considered a failure, which returns false. This can happen if + * the ASCII Group Separator (GS - 0x1D) is put into the arguments + * list or format string. + */ +static bool +argv_printf_arglist(struct argv *argres, const char *format, va_list arglist) +{ + const char delim = 0x1D; /* ASCII Group Separator (GS) */ + bool res = false; + + /* + * Prepare a format string which will be used by vsnprintf() later on. + * + * This means all space separators in the input format string will be + * replaced by the GS (0x1D), so we can split this up again after the + * the vsnprintf() call into individual arguments again which will be + * saved in the struct argv. + * + */ + size_t argc = argres->argc; + char *f = argv_prep_format(format, delim, &argc, &argres->gc); + if (f == NULL) + { + goto out; + } + + /* + * Determine minimum buffer size. + * + * With C99, vsnprintf(NULL, 0, ...) will return the number of bytes + * it would have written, had the buffer been large enough. + */ + va_list tmplist; + va_copy(tmplist, arglist); + int len = vsnprintf(NULL, 0, f, tmplist); + va_end(tmplist); + if (len < 0) + { + goto out; + } + + /* + * Do the actual vsnprintf() operation, which expands the format + * string with the provided arguments. + */ + size_t size = len + 1; + char *buf = gc_malloc(size, false, &argres->gc); + len = vsnprintf(buf, size, f, arglist); + if (len < 0 || len >= size) + { + goto out; + } + + /* + * Split the string at the GS (0x1D) delimiters and put each elemen + * into the struct argv being returned to the caller. + */ + char *end = strchr(buf, delim); + while (end) + { + *end = '\0'; + argv_append(argres, buf); + buf = end + 1; + end = strchr(buf, delim); + } + argv_append(argres, buf); + + if (argres->argc != argc) + { + /* Someone snuck in a GS (0x1D), fail gracefully */ + argv_reset(argres); + goto out; + } + res = true; + +out: + return res; +} + +/** + * printf() variant which populates a struct argv. It processes the + * format string with the provided arguments. For each space separator found + * in the format string, a new argument will be added to the resulting + * struct argv. + * + * This will always reset and ensure the result is based on a pristine + * struct argv. + * + * @param *argres Valid pointer to a struct argv where the result will be put. + * @param *format printf() compliant (char *) format string. + * + * @returns Returns true if the parsing was successful. See + * argv_printf_arglist() for more details. The parsed result will + * be put into argres. + */ +bool +argv_printf(struct argv *argres, const char *format, ...) { va_list arglist; - argv_reset(a); va_start(arglist, format); - argv_printf_arglist(a, format, arglist); + + argv_reset(argres); + bool res = argv_printf_arglist(argres, format, arglist); va_end(arglist); + return res; } -void -argv_printf_cat(struct argv *a, const char *format, ...) +/** + * printf() inspired argv concatenation. Adds arguments to an existing + * struct argv and populets the argument slots based on the printf() based + * format string. + * + * @param *argres Valid pointer to a struct argv where the result will be put. + * @param *format printf() compliant (char *) format string. + * + * @returns Returns true if the parsing was successful. See + * argv_printf_arglist() for more details. The parsed result will + * be put into argres. + */ +bool +argv_printf_cat(struct argv *argres, const char *format, ...) { va_list arglist; va_start(arglist, format); - argv_printf_arglist(a, format, arglist); + bool res = argv_printf_arglist(argres, format, arglist); va_end(arglist); + return res; } +/** + * Parses a command string, tokenizes it and puts each element into a separate + * struct argv argument slot. + * + * @params *argres Valid pointer to a struct argv where the parsed result + * will be found. + * @params *cmdstr Char * based string to parse + * + */ void -argv_parse_cmd(struct argv *a, const char *s) +argv_parse_cmd(struct argv *argres, const char *cmdstr) { - int nparms; - char *parms[MAX_PARMS + 1]; - struct gc_arena gc = gc_new(); - - argv_reset(a); - argv_extend(a, 1); /* ensure trailing NULL */ + argv_reset(argres); - nparms = parse_line(s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc); + char *parms[MAX_PARMS + 1] = { 0 }; + int nparms = parse_line(cmdstr, parms, MAX_PARMS, "SCRIPT-ARGV", 0, + D_ARGV_PARSE_CMD, &argres->gc); if (nparms) { int i; for (i = 0; i < nparms; ++i) { - argv_append(a, string_alloc(parms[i], NULL)); + argv_append(argres, parms[i]); } } else { - argv_append(a, string_alloc(s, NULL)); + argv_append(argres, string_alloc(cmdstr, &argres->gc)); } - - gc_free(&gc); } diff --git a/src/openvpn/argv.h b/src/openvpn/argv.h index 9d9f387..943c78e 100644 --- a/src/openvpn/argv.h +++ b/src/openvpn/argv.h @@ -33,6 +33,7 @@ #include "buffer.h" struct argv { + struct gc_arena gc; size_t capacity; size_t argc; char **argv; @@ -40,7 +41,7 @@ struct argv { struct argv argv_new(void); -void argv_reset(struct argv *a); +void argv_free(struct argv *a); const char *argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags); @@ -52,7 +53,7 @@ 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, ...) +bool argv_printf(struct argv *a, const char *format, ...) #ifdef __GNUC__ #if __USE_MINGW_ANSI_STDIO __attribute__ ((format(gnu_printf, 2, 3))) @@ -62,7 +63,7 @@ __attribute__ ((format(__printf__, 2, 3))) #endif ; -void argv_printf_cat(struct argv *a, const char *format, ...) +bool argv_printf_cat(struct argv *a, const char *format, ...) #ifdef __GNUC__ #if __USE_MINGW_ANSI_STDIO __attribute__ ((format(gnu_printf, 2, 3))) diff --git a/src/openvpn/auth_token.c b/src/openvpn/auth_token.c new file mode 100644 index 0000000..cc70c06 --- /dev/null +++ b/src/openvpn/auth_token.c @@ -0,0 +1,408 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "base64.h" +#include "buffer.h" +#include "crypto.h" +#include "openvpn.h" +#include "ssl_common.h" +#include "auth_token.h" +#include "push.h" +#include "integer.h" +#include "ssl.h" +#include "ssl_verify.h" +#include <inttypes.h> + +const char *auth_token_pem_name = "OpenVPN auth-token server key"; + +#define AUTH_TOKEN_SESSION_ID_LEN 12 +#if AUTH_TOKEN_SESSION_ID_LEN % 3 +#error AUTH_TOKEN_SESSION_ID_LEN needs to be multiple a 3 +#endif + +/* Size of the data of the token (not b64 encoded and without prefix) */ +#define TOKEN_DATA_LEN (2 * sizeof(int64_t) + AUTH_TOKEN_SESSION_ID_LEN + 32) + +static struct key_type +auth_token_kt(void) +{ + struct key_type kt = { 0 }; + /* We do not encrypt our session tokens */ + kt.cipher = NULL; + kt.digest = md_kt_get("SHA256"); + + if (!kt.digest) + { + msg(M_WARN, "ERROR: --tls-crypt requires HMAC-SHA-256 support."); + return (struct key_type) { 0 }; + } + + kt.hmac_length = md_kt_size(kt.digest); + + return kt; +} + + +void +add_session_token_env(struct tls_session *session, struct tls_multi *multi, + const struct user_pass *up) +{ + if (!multi->opt.auth_token_generate) + { + return; + } + + + const char *state; + + if (!is_auth_token(up->password)) + { + state = "Initial"; + } + else if (multi->auth_token_state_flags & AUTH_TOKEN_HMAC_OK) + { + switch (multi->auth_token_state_flags & (AUTH_TOKEN_VALID_EMPTYUSER|AUTH_TOKEN_EXPIRED)) + { + case 0: + state = "Authenticated"; + break; + + case AUTH_TOKEN_EXPIRED: + state = "Expired"; + break; + + case AUTH_TOKEN_VALID_EMPTYUSER: + state = "AuthenticatedEmptyUser"; + break; + + case AUTH_TOKEN_VALID_EMPTYUSER | AUTH_TOKEN_EXPIRED: + state = "ExpiredEmptyUser"; + break; + + default: + /* Silence compiler warning, all four possible combinations are covered */ + ASSERT(0); + } + } + else + { + state = "Invalid"; + } + + setenv_str(session->opt->es, "session_state", state); + + /* We had a valid session id before */ + const char *session_id_source; + if (multi->auth_token_state_flags & AUTH_TOKEN_HMAC_OK + &!(multi->auth_token_state_flags & AUTH_TOKEN_EXPIRED)) + { + session_id_source = up->password; + } + else + { + /* + * No session before, generate a new session token for the new session + */ + if (!multi->auth_token) + { + generate_auth_token(up, multi); + } + session_id_source = multi->auth_token; + } + /* + * In the auth-token the auth token is already base64 encoded + * and being a multiple of 4 ensure that it a multiple of bytes + * in the encoding + */ + + char session_id[AUTH_TOKEN_SESSION_ID_LEN*2] = {0}; + memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX), + AUTH_TOKEN_SESSION_ID_LEN*8/6); + + setenv_str(session->opt->es, "session_id", session_id); +} + +void +auth_token_write_server_key_file(const char *filename) +{ + write_pem_key_file(filename, auth_token_pem_name); +} + +void +auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file, + bool key_inline) +{ + struct key_type kt = auth_token_kt(); + + struct buffer server_secret_key = alloc_buf(2048); + + bool key_loaded = false; + if (key_file) + { + key_loaded = read_pem_key_file(&server_secret_key, + auth_token_pem_name, + key_file, key_inline); + } + else + { + key_loaded = generate_ephemeral_key(&server_secret_key, + auth_token_pem_name); + } + + if (!key_loaded) + { + msg(M_FATAL, "ERROR: Cannot load auth-token secret"); + } + + struct key key; + + if (!buf_read(&server_secret_key, &key, sizeof(key))) + { + msg(M_FATAL, "ERROR: not enough data in auth-token secret"); + } + init_key_ctx(key_ctx, &key, &kt, false, "auth-token secret"); + + free_buf(&server_secret_key); +} + +void +generate_auth_token(const struct user_pass *up, struct tls_multi *multi) +{ + struct gc_arena gc = gc_new(); + + int64_t timestamp = htonll((uint64_t)now); + int64_t initial_timestamp = timestamp; + + hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac; + ASSERT(hmac_ctx_size(ctx) == 256/8); + + uint8_t sessid[AUTH_TOKEN_SESSION_ID_LEN]; + + if (multi->auth_token) + { + /* Just enough space to fit 8 bytes+ 1 extra to decode a non padded + * base64 string (multiple of 3 bytes). 9 bytes => 12 bytes base64 + * bytes + */ + char old_tstamp_decode[9]; + + /* + * reuse the same session id and timestamp and null terminate it at + * for base64 decode it only decodes the session id part of it + */ + char *old_sessid = multi->auth_token + strlen(SESSION_ID_PREFIX); + char *old_tsamp_initial = old_sessid + AUTH_TOKEN_SESSION_ID_LEN*8/6; + + old_tsamp_initial[12] = '\0'; + ASSERT(openvpn_base64_decode(old_tsamp_initial, old_tstamp_decode, 9) == 9); + + /* + * Avoid old gcc (4.8.x) complaining about strict aliasing + * by using a temporary variable instead of doing it in one + * line + */ + uint64_t *tstamp_ptr = (uint64_t *) old_tstamp_decode; + initial_timestamp = *tstamp_ptr; + + old_tsamp_initial[0] = '\0'; + ASSERT(openvpn_base64_decode(old_sessid, sessid, AUTH_TOKEN_SESSION_ID_LEN)==AUTH_TOKEN_SESSION_ID_LEN); + + + /* free the auth-token, we will replace it with a new one */ + free(multi->auth_token); + } + else if (!rand_bytes(sessid, AUTH_TOKEN_SESSION_ID_LEN)) + { + msg( M_FATAL, "Failed to get enough randomness for " + "authentication token"); + } + + /* Calculate the HMAC */ + /* We enforce up->username to be \0 terminated in ssl.c.. Allowing username + * with \0 in them is asking for troubles in so many ways anyway that we + * ignore that corner case here + */ + uint8_t hmac_output[256/8]; + + hmac_ctx_reset(ctx); + + /* + * If the token was only valid for the empty user, also generate + * a new token with the empty username since we do not want to loose + * the information that the username cannot be trusted + */ + if (multi->auth_token_state_flags & AUTH_TOKEN_VALID_EMPTYUSER) + { + hmac_ctx_update(ctx, (const uint8_t *) "", 0); + } + else + { + hmac_ctx_update(ctx, (uint8_t *) up->username, (int) strlen(up->username)); + } + hmac_ctx_update(ctx, sessid, AUTH_TOKEN_SESSION_ID_LEN); + hmac_ctx_update(ctx, (uint8_t *) &initial_timestamp, sizeof(initial_timestamp)); + hmac_ctx_update(ctx, (uint8_t *) ×tamp, sizeof(timestamp)); + hmac_ctx_final(ctx, hmac_output); + + /* Construct the unencoded session token */ + struct buffer token = alloc_buf_gc( + 2*sizeof(uint64_t) + AUTH_TOKEN_SESSION_ID_LEN + 256/8, &gc); + + ASSERT(buf_write(&token, sessid, sizeof(sessid))); + ASSERT(buf_write(&token, &initial_timestamp, sizeof(initial_timestamp))); + ASSERT(buf_write(&token, ×tamp, sizeof(timestamp))); + ASSERT(buf_write(&token, hmac_output, sizeof(hmac_output))); + + char *b64output; + openvpn_base64_encode(BPTR(&token), BLEN(&token), &b64output); + + struct buffer session_token = alloc_buf_gc( + strlen(SESSION_ID_PREFIX) + strlen(b64output) + 1, &gc); + + ASSERT(buf_write(&session_token, SESSION_ID_PREFIX, strlen(SESSION_ID_PREFIX))); + ASSERT(buf_write(&session_token, b64output, (int)strlen(b64output))); + ASSERT(buf_write_u8(&session_token, 0)); + + free(b64output); + + multi->auth_token = strdup((char *)BPTR(&session_token)); + + dmsg(D_SHOW_KEYS, "Generated token for client: %s (%s)", + multi->auth_token, up->username); + + gc_free(&gc); +} + + +static bool +check_hmac_token(hmac_ctx_t *ctx, const uint8_t *b64decoded, const char *username) +{ + ASSERT(hmac_ctx_size(ctx) == 256/8); + + uint8_t hmac_output[256/8]; + + hmac_ctx_reset(ctx); + hmac_ctx_update(ctx, (uint8_t *) username, (int)strlen(username)); + hmac_ctx_update(ctx, b64decoded, TOKEN_DATA_LEN - 256/8); + hmac_ctx_final(ctx, hmac_output); + + const uint8_t *hmac = b64decoded + TOKEN_DATA_LEN - 256/8; + return memcmp_constant_time(&hmac_output, hmac, 32) == 0; +} + +unsigned int +verify_auth_token(struct user_pass *up, struct tls_multi *multi, + struct tls_session *session) +{ + /* + * Base64 is <= input and input is < USER_PASS_LEN, so using USER_PASS_LEN + * is safe here but a bit overkill + */ + uint8_t b64decoded[USER_PASS_LEN]; + int decoded_len = openvpn_base64_decode(up->password + strlen(SESSION_ID_PREFIX), + b64decoded, USER_PASS_LEN); + + /* + * Ensure that the decoded data is the size of the + * timestamp + hmac + session id + */ + if (decoded_len != TOKEN_DATA_LEN) + { + msg(M_WARN, "ERROR: --auth-token wrong size (%d!=%d)", + decoded_len, (int) TOKEN_DATA_LEN); + return 0; + } + + unsigned int ret = 0; + + const uint8_t *sessid = b64decoded; + const uint8_t *tstamp_initial = sessid + AUTH_TOKEN_SESSION_ID_LEN; + const uint8_t *tstamp = tstamp_initial + sizeof(int64_t); + + uint64_t timestamp = ntohll(*((uint64_t *) (tstamp))); + uint64_t timestamp_initial = ntohll(*((uint64_t *) (tstamp_initial))); + + hmac_ctx_t *ctx = multi->opt.auth_token_key.hmac; + if (check_hmac_token(ctx, b64decoded, up->username)) + { + ret |= AUTH_TOKEN_HMAC_OK; + } + else if (check_hmac_token(ctx, b64decoded, "")) + { + ret |= AUTH_TOKEN_HMAC_OK; + ret |= AUTH_TOKEN_VALID_EMPTYUSER; + /* overwrite the username of the client with the empty one */ + strcpy(up->username, ""); + } + else + { + msg(M_WARN, "--auth-token-gen: HMAC on token from client failed (%s)", + up->username); + return 0; + } + + /* Accept session tokens that not expired are in the acceptable range + * for renogiations */ + bool in_renog_time = now >= timestamp + && now < timestamp + 2 * session->opt->renegotiate_seconds; + + /* We could still have a client that does not update + * its auth-token, so also allow the initial auth-token */ + bool initialtoken = multi->auth_token_initial + && memcmp_constant_time(up->password, multi->auth_token_initial, + strlen(multi->auth_token_initial)) == 0; + + if (!in_renog_time && !initialtoken) + { + ret |= AUTH_TOKEN_EXPIRED; + } + + /* Sanity check the initial timestamp */ + if (timestamp < timestamp_initial) + { + msg(M_WARN, "Initial timestamp (%" PRIu64 " in token from client earlier than " + "current timestamp %" PRIu64 ". Broken/unsynchronised clock?", + timestamp_initial, timestamp); + ret |= AUTH_TOKEN_EXPIRED; + } + + if (multi->opt.auth_token_lifetime + && now > timestamp_initial + multi->opt.auth_token_lifetime) + { + ret |= AUTH_TOKEN_EXPIRED; + } + + if (ret & AUTH_TOKEN_EXPIRED) + { + /* Tell client that the session token is expired */ + auth_set_client_reason(multi, "SESSION: token expired"); + msg(M_INFO, "--auth-token-gen: auth-token from client expired"); + } + return ret; +} + +void +wipe_auth_token(struct tls_multi *multi) +{ + if (multi) + { + if (multi->auth_token) + { + secure_memzero(multi->auth_token, strlen(multi->auth_token)); + free(multi->auth_token); + } + if (multi->auth_token_initial) + { + secure_memzero(multi->auth_token_initial, + strlen(multi->auth_token_initial)); + free(multi->auth_token_initial); + } + multi->auth_token = NULL; + multi->auth_token_initial = NULL; + } +} diff --git a/src/openvpn/auth_token.h b/src/openvpn/auth_token.h new file mode 100644 index 0000000..fe07945 --- /dev/null +++ b/src/openvpn/auth_token.h @@ -0,0 +1,132 @@ +/* + * 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-2018 OpenVPN Inc <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef AUTH_TOKEN_H +#define AUTH_TOKEN_H + +/** + * Generate an auth token based on username and timestamp + * + * The idea of auth token is to be stateless, so that we can verify use it + * even after we have forgotten about it or server has been restarted. + * + * To achieve this even though we cannot trust the client we use HMAC + * to be able to verify the information. + * + * Format of the auth-token (before base64 encode) + * + * session id(12 bytes)|uint64 timestamp (8 bytes)| + * uint64 timestamp (8 bytes)|sha256-hmac(32 bytes) + * + * The first timestamp is the time the token was initially created and is used to + * determine the maximum renewable time of the token. We always include this even + * if tokens do not expire (this value is not used) to keep the code cleaner. + * + * The second timestamp is the time the token was renewed/regenerated and is used + * to determine if this token has been renewed in the acceptable time range + * (2 * renogiation timeout) + * + * The session id is a random string of 12 byte (or 16 in base64) that is not + * used by OpenVPN itself but kept intact so that external logging/managment + * can track the session multiple reconnects/servers. It is delibrately chosen + * be a multiple of 3 bytes to have a base64 encoding without padding. + * + * The hmac is calculated over the username contactinated with the + * raw auth-token bytes to include authentication of the username in the token + * + * We encode the auth-token with base64 and then prepend "SESS_ID_" before + * sending it to the client. + * + * This function will free() an existing multi->auth_token and keep the + * existing initial timestamp and session id contained in that token. + */ +void +generate_auth_token(const struct user_pass *up, struct tls_multi *multi); + +/** + * Verifies the auth token to be in the format that generate_auth_token + * create and checks if the token is valid. + * + */ +unsigned +verify_auth_token(struct user_pass *up, struct tls_multi *multi, + struct tls_session *session); + + + +/** + * Loads an HMAC secret from a file or if no file is present generates a + * epheremal secret for the run time of the server and stores it into ctx + */ +void +auth_token_init_secret(struct key_ctx *key_ctx, const char *key_file, + bool key_inline); + + +/** + * Generate a auth-token server secret key, and write to file. + * + * @param filename Filename of the server key file to create. + */ +void auth_token_write_server_key_file(const char *filename); + + +/** + * Put the session id, and auth token status into the environment + * if auth-token is enabled + * + */ +void add_session_token_env(struct tls_session *session, struct tls_multi *multi, + const struct user_pass *up); + +/** + * Wipes the authentication token out of the memory, frees and cleans up + * related buffers and flags + * + * @param multi Pointer to a multi object holding the auth_token variables + */ +void wipe_auth_token(struct tls_multi *multi); + +/** + * The prefix given to auth tokens start with, this prefix is special + * cased to not show up in log files in OpenVPN 2 and 3 + * + * We also prefix this with _AT_ to only act on auth token generated by us. + */ +#define SESSION_ID_PREFIX "SESS_ID_AT_" + +/** + * Return if the password string has the format of a password. + * + * This fuction will always read as many bytes as SESSION_ID_PREFIX is longer + * the caller needs ensure that password memory is at least that long (true for + * calling with struct user_pass) + * @param password + * @return whether the password string starts with the session token prefix + */ +static inline bool +is_auth_token(const char *password) +{ + return (memcmp_constant_time(SESSION_ID_PREFIX, password, + strlen(SESSION_ID_PREFIX)) == 0); +} +#endif /* AUTH_TOKEN_H */ diff --git a/src/openvpn/base64.h b/src/openvpn/base64.h index 5679bc9..f49860f 100644 --- a/src/openvpn/base64.h +++ b/src/openvpn/base64.h @@ -34,6 +34,10 @@ #ifndef _BASE64_H_ #define _BASE64_H_ +/** Compute resulting base64 length. 6 bits per byte, padded to 4 bytes. */ +#define OPENVPN_BASE64_LENGTH(binary_length) \ + ((((8 * binary_length) / 6) + 3) & ~3) + int openvpn_base64_encode(const void *data, int size, char **str); int openvpn_base64_decode(const char *str, void *data, int size); diff --git a/src/openvpn/block_dns.c b/src/openvpn/block_dns.c index 889d6bb..f4718fc 100644 --- a/src/openvpn/block_dns.c +++ b/src/openvpn/block_dns.c @@ -109,9 +109,6 @@ DEFINE_GUID( static WCHAR *FIREWALL_NAME = L"OpenVPN"; -VOID NETIOAPI_API_ -InitializeIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row); - /* * Default msg handler does nothing */ diff --git a/src/openvpn/block_dns.h b/src/openvpn/block_dns.h index 50b383f..f9b1e5d 100644 --- a/src/openvpn/block_dns.h +++ b/src/openvpn/block_dns.h @@ -65,5 +65,5 @@ DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric); -#endif -#endif +#endif /* ifndef OPENVPN_BLOCK_DNS_H */ +#endif /* ifdef _WIN32 */ diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c index f9c76b1..b32bc8b 100644 --- a/src/openvpn/buffer.c +++ b/src/openvpn/buffer.c @@ -37,6 +37,8 @@ #include "memdbg.h" +#include <wchar.h> + size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra) { @@ -44,7 +46,7 @@ array_mult_safe(const size_t m1, const size_t m2, const size_t extra) unsigned long long res = (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra; if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit) || unlikely(res > (unsigned long long)limit)) { - msg(M_FATAL, "attemped allocation of excessively large array"); + msg(M_FATAL, "attempted allocation of excessively large array"); } return (size_t) res; } @@ -179,14 +181,6 @@ buf_assign(struct buffer *dest, const struct buffer *src) return buf_write(dest, BPTR(src), BLEN(src)); } -struct buffer -clear_buf(void) -{ - struct buffer buf; - CLEAR(buf); - return buf; -} - void free_buf(struct buffer *buf) { @@ -197,6 +191,34 @@ free_buf(struct buffer *buf) CLEAR(*buf); } +static void +free_buf_gc(struct buffer *buf, struct gc_arena *gc) +{ + if (gc) + { + struct gc_entry **e = &gc->list; + + while (*e) + { + /* check if this object is the one we want to delete */ + if ((uint8_t *)(*e + 1) == buf->data) + { + struct gc_entry *to_delete = *e; + + /* remove element from linked list and free it */ + *e = (*e)->next; + free(to_delete); + + break; + } + + e = &(*e)->next; + } + } + + CLEAR(*buf); +} + /* * Return a buffer for write that is a subset of another buffer */ @@ -289,6 +311,29 @@ openvpn_snprintf(char *str, size_t size, const char *format, ...) } /* + * openvpn_swprintf() is currently only used by Windows code paths + * and when enabled for all platforms it will currently break older + * OpenBSD versions lacking vswprintf(3) support in their libc. + */ + +#ifdef _WIN32 +bool +openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...) +{ + va_list arglist; + int len = -1; + if (size > 0) + { + va_start(arglist, format); + len = vswprintf(str, size, format, arglist); + va_end(arglist); + str[size - 1] = L'\0'; + } + return (len >= 0 && len < size); +} +#endif + +/* * write a string to the end of a buffer that was * truncated by buf_printf */ @@ -323,16 +368,33 @@ convert_to_one_line(struct buffer *buf) } } -/* NOTE: requires that string be null terminated */ -void -buf_write_string_file(const struct buffer *buf, const char *filename, int fd) +bool +buffer_write_file(const char *filename, const struct buffer *buf) { - const int len = strlen((char *) BPTR(buf)); - const int size = write(fd, BPTR(buf), len); - if (size != len) + bool ret = false; + int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, + S_IRUSR | S_IWUSR); + if (fd == -1) { - msg(M_ERR, "Write error on file '%s'", filename); + msg(M_ERRNO, "Cannot open file '%s' for write", filename); + return false; } + + const int size = write(fd, BPTR(buf), BLEN(buf)); + if (size != BLEN(buf)) + { + msg(M_ERRNO, "Write error on file '%s'", filename); + goto cleanup; + } + + ret = true; +cleanup: + if (close(fd) < 0) + { + msg(M_ERRNO, "Close error on file %s", filename); + ret = false; + } + return ret; } /* @@ -412,7 +474,7 @@ x_gc_freespecial(struct gc_arena *a) } void -gc_addspecial(void *addr, void(free_function)(void *), struct gc_arena *a) +gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a) { ASSERT(a); struct gc_entry_special *e; @@ -1335,3 +1397,36 @@ buffer_list_file(const char *fn, int max_line_len) } return bl; } + +struct buffer +buffer_read_from_file(const char *filename, struct gc_arena *gc) +{ + struct buffer ret = { 0 }; + + platform_stat_t file_stat = {0}; + if (platform_stat(filename, &file_stat) < 0) + { + return ret; + } + + FILE *fp = platform_fopen(filename, "r"); + if (!fp) + { + return ret; + } + + const size_t size = file_stat.st_size; + ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */ + ssize_t read_size = fread(BPTR(&ret), 1, size, fp); + if (read_size < 0) + { + free_buf_gc(&ret, gc); + goto cleanup; + } + ASSERT(buf_inc_len(&ret, read_size)); + buf_null_terminate(&ret); + +cleanup: + fclose(fp); + return ret; +} diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h index c510c00..1722ffd 100644 --- a/src/openvpn/buffer.h +++ b/src/openvpn/buffer.h @@ -131,8 +131,6 @@ struct gc_arena void buf_clear(struct buffer *buf); -struct buffer clear_buf(void); - void free_buf(struct buffer *buf); bool buf_assign(struct buffer *dest, const struct buffer *src); @@ -206,6 +204,13 @@ gc_freeaddrinfo_callback(void *addr) freeaddrinfo((struct addrinfo *) addr); } +/** Return an empty struct buffer */ +static inline struct buffer +clear_buf(void) +{ + return (struct buffer) { 0 }; +} + static inline bool buf_defined(const struct buffer *buf) { @@ -342,9 +347,9 @@ buf_set_read(struct buffer *buf, const uint8_t *data, int size) static inline void strncpynt(char *dest, const char *src, size_t maxlen) { - strncpy(dest, src, maxlen); if (maxlen > 0) { + strncpy(dest, src, maxlen-1); dest[maxlen - 1] = 0; } } @@ -443,6 +448,23 @@ __attribute__ ((format(__printf__, 3, 4))) #endif ; + +#ifdef _WIN32 +/* + * Like swprintf but guarantees null termination for size > 0 + * + * This is under #ifdef because only Windows-specific code in tun.c + * uses this function and its implementation breaks OpenBSD <= 4.9 + */ +bool +openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...); + +/* + * Unlike in openvpn_snprintf, we cannot use format attributes since + * GCC doesn't support wprintf as archetype. + */ +#endif + /* * remove/add trailing characters */ @@ -464,11 +486,15 @@ const char *skip_leading_whitespace(const char *str); void string_null_terminate(char *str, int len, int capacity); -/* - * Write string in buf to file descriptor fd. - * NOTE: requires that string be null terminated. +/** + * Write buffer contents to file. + * + * @param filename The filename to write the buffer to. + * @param buf The buffer to write to the file. + * + * @return true on success, false otherwise. */ -void buf_write_string_file(const struct buffer *buf, const char *filename, int fd); +bool buffer_write_file(const char *filename, const struct buffer *buf); /* * write a string to the end of a buffer that was @@ -828,6 +854,13 @@ buf_read_u32(struct buffer *buf, bool *good) } } +/** Return true if buffer contents are equal */ +static inline bool +buf_equal(const struct buffer *a, const struct buffer *b) +{ + return BLEN(a) == BLEN(b) && 0 == memcmp(BPTR(a), BPTR(b), BLEN(a)); +} + /** * Compare src buffer contents with match. * *NOT* constant time. Do not use when comparing HMACs. @@ -1174,4 +1207,16 @@ void buffer_list_aggregate_separator(struct buffer_list *bl, struct buffer_list *buffer_list_file(const char *fn, int max_line_len); +/** + * buffer_read_from_file - copy the content of a file into a buffer + * + * @param file path to the file to read + * @param gc the garbage collector to use when allocating the buffer. It + * is passed to alloc_buf_gc() and therefore can be NULL. + * + * @return the buffer storing the file content or an invalid buffer in case of + * error + */ +struct buffer buffer_read_from_file(const char *filename, struct gc_arena *gc); + #endif /* BUFFER_H */ diff --git a/src/openvpn/common.h b/src/openvpn/common.h index 0f73200..623b3e0 100644 --- a/src/openvpn/common.h +++ b/src/openvpn/common.h @@ -57,12 +57,10 @@ typedef int interval_t; #else #define ptr_format "0x%08lx" #endif -#define time_format "%lu" #define fragment_header_format "0x%08x" /* these are used to cast the arguments * and MUST match the formats above */ -typedef unsigned long time_type; #ifdef _WIN64 typedef unsigned long long ptr_type; #else @@ -91,12 +89,6 @@ typedef unsigned long ptr_type; #define PUSH_REQUEST_INTERVAL 5 /* - * A sort of pseudo-filename for data provided inline within - * the configuration file. - */ -#define INLINE_FILE_TAG "[[INLINE]]" - -/* * Script security warning */ #define SCRIPT_SECURITY_WARNING "WARNING: External program may not be called unless '--script-security 2' or higher is enabled. See --help text or man page for detailed info." diff --git a/src/openvpn/comp-lz4.c b/src/openvpn/comp-lz4.c index f2916bd..30e6da9 100644 --- a/src/openvpn/comp-lz4.c +++ b/src/openvpn/comp-lz4.c @@ -35,7 +35,7 @@ #if defined(NEED_COMPAT_LZ4) #include "compat-lz4.h" #else -#include "lz4.h" +#include <lz4.h> #endif #include "comp.h" @@ -70,8 +70,9 @@ do_lz4_compress(struct buffer *buf, { /* * In order to attempt compression, length must be at least COMPRESS_THRESHOLD. + * and asymmetric compression must be disabled */ - if (buf->len >= COMPRESS_THRESHOLD) + if (buf->len >= COMPRESS_THRESHOLD && (compctx->flags & COMP_F_ALLOW_COMPRESS)) { const size_t ps = PAYLOAD_SIZE(frame); int zlen_max = ps + COMP_EXTRA_BUFFER(ps); diff --git a/src/openvpn/comp.c b/src/openvpn/comp.c index a945913..9b13113 100644 --- a/src/openvpn/comp.c +++ b/src/openvpn/comp.c @@ -127,7 +127,7 @@ 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 */ + * where data is totally incompressible */ frame_add_to_extra_buffer(frame, COMP_EXTRA_BUFFER(EXPANDED_SIZE(frame))); } diff --git a/src/openvpn/comp.h b/src/openvpn/comp.h index 0dadd1e..5c0322c 100644 --- a/src/openvpn/comp.h +++ b/src/openvpn/comp.h @@ -52,10 +52,12 @@ */ /* 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_ADAPTIVE (1<<0) /* COMP_ALG_LZO only */ +#define COMP_F_ALLOW_COMPRESS (1<<1) /* not only downlink is compressed but also 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 */ +#define COMP_F_ALLOW_STUB_ONLY (1<<4) /* Only accept stub compression, even with COMP_F_ADVERTISE_STUBS_ONLY + * we still accept other compressions to be pushed */ /* @@ -189,6 +191,14 @@ comp_enabled(const struct compress_options *info) } static inline bool +comp_non_stub_enabled(const struct compress_options *info) +{ + return info->alg != COMP_ALGV2_UNCOMPRESSED + && info->alg != COMP_ALG_STUB + && info->alg != COMP_ALG_UNDEF; +} + +static inline bool comp_unswapped_prefix(const struct compress_options *info) { return !(info->flags & COMP_F_SWAP); diff --git a/src/openvpn/console.h b/src/openvpn/console.h index 5a70e5f..f948168 100644 --- a/src/openvpn/console.h +++ b/src/openvpn/console.h @@ -21,7 +21,7 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + */ #ifndef CONSOLE_H #define CONSOLE_H @@ -33,9 +33,9 @@ */ struct _query_user { char *prompt; /**< Prompt to present to the user */ - size_t prompt_len; /**< Lenght of the prompt string */ + size_t prompt_len; /**< Length of the prompt string */ char *response; /**< The user's response */ - size_t response_len; /**< Lenght the of the user reposone */ + size_t response_len; /**< Length the of the user response */ bool echo; /**< True: The user should see what is being typed, otherwise mask it */ }; @@ -55,7 +55,7 @@ void query_user_clear(void); * @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 resp_len Length of the response string * @param echo Should the user input be echoed to the user? If False, input will be masked * */ diff --git a/src/openvpn/console_systemd.c b/src/openvpn/console_systemd.c index e7a72ae..c7cf1ad 100644 --- a/src/openvpn/console_systemd.c +++ b/src/openvpn/console_systemd.c @@ -33,6 +33,7 @@ #include "syshead.h" #include "console.h" #include "misc.h" +#include "run_command.h" #include <systemd/sd-daemon.h> @@ -84,7 +85,7 @@ get_console_input_systemd(const char *prompt, const bool echo, char *input, cons } close(std_out); - argv_reset(&argv); + argv_free(&argv); return ret; } diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 7e7dead..3a0bfbe 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -30,8 +30,6 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - #include "crypto.h" #include "error.h" #include "integer.h" @@ -66,7 +64,6 @@ 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; @@ -77,7 +74,6 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work, /* 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)); gc_init(&gc); @@ -155,9 +151,6 @@ err: buf->len = 0; gc_free(&gc); return; -#else /* HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ } static void @@ -192,10 +185,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, if (cipher_kt_mode_cbc(cipher_kt)) { /* generate pseudo-random IV */ - if (opt->flags & CO_USE_IV) - { - prng_bytes(iv_buf, iv_size); - } + prng_bytes(iv_buf, iv_size); /* Put packet ID in plaintext buffer */ if (packet_id_initialized(&opt->packet_id) @@ -211,8 +201,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, { struct buffer b; - /* IV and packet-ID required for this mode. */ - ASSERT(opt->flags & CO_USE_IV); + /* packet-ID required for this mode. */ ASSERT(packet_id_initialized(&opt->packet_id)); buf_set_write(&b, iv_buf, iv_size); @@ -224,11 +213,8 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, } /* set the IV pseudo-randomly */ - if (opt->flags & CO_USE_IV) - { - ASSERT(buf_write(&work, iv_buf, iv_size)); - 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)); @@ -358,20 +344,19 @@ crypto_check_replay(struct crypto_options *opt, return ret; } -/* - * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. +/** + * Unwrap (authenticate, decrypt and check replay protection) AEAD-mode data + * channel packets. * * Set buf->len to 0 and return false on decrypt error. * - * On success, buf is set to point to plaintext, true - * is returned. + * On success, buf is set to point to plaintext, true is returned. */ 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; @@ -398,7 +383,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, /* 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 */ { @@ -439,13 +423,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, 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) { @@ -500,19 +477,15 @@ error_exit: buf->len = 0; gc_free(&gc); return false; -#else /* HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); - return false; -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ } /* - * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. + * Unwrap (authenticate, decrypt and check replay protection) CBC, OFB or CFB + * mode data channel packets. * * Set buf->len to 0 and return false on decrypt error. * - * On success, buf is set to point to plaintext, true - * is returned. + * On success, buf is set to point to plaintext, true is returned. */ static bool openvpn_decrypt_v1(struct buffer *buf, struct buffer work, @@ -572,22 +545,14 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work, /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT(buf_init(&work, FRAME_HEADROOM_ADJ(frame, FRAME_HEADROOM_MARKER_DECRYPT))); - /* use IV if user requested it */ - if (opt->flags & CO_USE_IV) - { - if (buf->len < iv_size) - { - CRYPT_ERROR("missing IV info"); - } - memcpy(iv_buf, BPTR(buf), iv_size); - ASSERT(buf_advance(buf, iv_size)); - } - - /* show the IV's initial state */ - if (opt->flags & CO_USE_IV) + /* read the IV from the packet */ + if (buf->len < iv_size) { - dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); + CRYPT_ERROR("missing IV info"); } + memcpy(iv_buf, BPTR(buf), iv_size); + ASSERT(buf_advance(buf, iv_size)); + dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); if (buf->len < 1) { @@ -640,8 +605,7 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work, { struct buffer b; - /* IV and packet-ID required for this mode. */ - ASSERT(opt->flags & CO_USE_IV); + /* packet-ID required for this mode. */ ASSERT(packet_id_initialized(&opt->packet_id)); buf_set_read(&b, iv_buf, iv_size); @@ -717,7 +681,6 @@ openvpn_decrypt(struct buffer *buf, struct buffer work, void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type *kt, - bool use_iv, bool packet_id, bool packet_id_long_form) { @@ -730,10 +693,7 @@ crypto_adjust_frame_parameters(struct frame *frame, if (kt->cipher) { - if (use_iv) - { - crypto_overhead += cipher_kt_iv_size(kt->cipher); - } + crypto_overhead += cipher_kt_iv_size(kt->cipher); if (cipher_kt_mode_aead(kt->cipher)) { @@ -760,6 +720,20 @@ crypto_max_overhead(void) +max_int(OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH); } +static void +warn_insecure_key_type(const char *ciphername, const cipher_kt_t *cipher) +{ + if (cipher_kt_insecure(cipher)) + { + msg(M_WARN, "WARNING: INSECURE cipher (%s) 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). " + "Support for these insecure ciphers will be removed in " + "OpenVPN 2.6.", + ciphername, cipher_kt_block_size(cipher)*8); + } +} + /* * Build a struct key_type. */ @@ -775,7 +749,7 @@ init_key_type(struct key_type *kt, const char *ciphername, CLEAR(*kt); if (strcmp(ciphername, "none") != 0) { - kt->cipher = cipher_kt_get(translate_cipher_name_from_openvpn(ciphername)); + kt->cipher = cipher_kt_get(ciphername); if (!kt->cipher) { msg(M_FATAL, "Cipher %s not supported", ciphername); @@ -803,6 +777,10 @@ init_key_type(struct key_type *kt, const char *ciphername, { msg(M_FATAL, "Cipher '%s' not allowed: block size too big.", ciphername); } + if (warn) + { + warn_insecure_key_type(ciphername, kt->cipher); + } } else { @@ -855,9 +833,10 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, cipher_ctx_init(ctx->cipher, key->cipher, kt->cipher_length, kt->cipher, enc); + const char *ciphername = cipher_kt_name(kt->cipher); msg(D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", prefix, - translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)), + ciphername, kt->cipher_length *8); dmsg(D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, @@ -865,13 +844,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, 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)); - 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); - } + warn_insecure_key_type(ciphername, kt->cipher); } if (kt->digest && kt->hmac_length > 0) { @@ -943,10 +916,12 @@ key_is_zero(struct key *key, const struct key_type *kt) { int i; for (i = 0; i < kt->cipher_length; ++i) + { if (key->cipher[i]) { return false; } + } msg(D_CRYPT_ERRORS, "CRYPTO INFO: WARNING: zero key detected"); return true; } @@ -1025,15 +1000,14 @@ fixup_key(struct key *key, const struct key_type *kt) } void -check_replay_iv_consistency(const struct key_type *kt, bool packet_id, bool use_iv) +check_replay_consistency(const struct key_type *kt, bool packet_id) { ASSERT(kt); - if (!(packet_id && use_iv) && (cipher_kt_mode_ofb_cfb(kt->cipher) - || cipher_kt_mode_aead(kt->cipher))) + if (!packet_id && (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"); + msg(M_FATAL, "--no-replay cannot be used with a CFB, OFB or AEAD mode cipher"); } } @@ -1123,7 +1097,6 @@ test_crypto(struct crypto_options *co, struct frame *frame) /* init work */ ASSERT(buf_init(&work, FRAME_HEADROOM(frame))); -#ifdef HAVE_AEAD_CIPHER_MODES /* init implicit IV */ { const cipher_kt_t *cipher = @@ -1145,7 +1118,6 @@ test_crypto(struct crypto_options *co, struct frame *frame) co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len; } } -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ msg(M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode."); for (i = 1; i <= TUN_MTU_SIZE(frame); ++i) @@ -1196,27 +1168,38 @@ test_crypto(struct crypto_options *co, struct frame *frame) gc_free(&gc); } +const char * +print_key_filename(const char *str, bool is_inline) +{ + if (is_inline) + { + return "[[INLINE]]"; + } + + return np(str); +} + 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) + struct key_ctx_bi *ctx, const char *key_file, + bool key_inline, const int key_direction, + const char *key_name, const char *opt_name) { struct key2 key2; struct key_direction_state kds; + unsigned int flags = RKF_MUST_SUCCEED; if (key_inline) { - read_key_file(&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE); - } - else - { - read_key_file(&key2, key_file, RKF_MUST_SUCCEED); + flags |= RKF_INLINE; } + read_key_file(&key2, key_file, flags); 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); + "free-form passphrase file is not supported anymore.", + print_key_filename(key_file, key_inline)); } /* check for and fix highly unlikely key problems */ @@ -1248,9 +1231,8 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { struct gc_arena gc = gc_new(); struct buffer in; - int fd, size; + int size; uint8_t hex_byte[3] = {0, 0, 0}; - const char *error_filename = file; /* parse info */ const unsigned char *cp; @@ -1288,26 +1270,16 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { size = strlen(file) + 1; buf_set_read(&in, (const uint8_t *)file, size); - error_filename = INLINE_FILE_TAG; } else /* 'file' is a filename which refers to a file containing the ascii key */ { - in = alloc_buf_gc(2048, &gc); - fd = platform_open(file, O_RDONLY, 0); - if (fd == -1) - { - msg(M_ERR, "Cannot open key file '%s'", file); - } - size = read(fd, in.data, in.capacity); - if (size < 0) + in = buffer_read_from_file(file, &gc); + if (!buf_valid(&in)) { msg(M_FATAL, "Read error on key file ('%s')", file); } - if (size == in.capacity) - { - msg(M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity); - } - close(fd); + + size = in.len; } cp = (unsigned char *)in.data; @@ -1393,7 +1365,9 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { msg(M_FATAL, (isprint(c) ? printable_char_fmt : unprintable_char_fmt), - c, line_num, error_filename, count, onekeylen, keylen); + c, line_num, + print_key_filename(file, flags & RKF_INLINE), count, + onekeylen, keylen); } } ++line_index; @@ -1414,13 +1388,15 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) if (!key2->n) { msg(M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)", - error_filename, count, onekeylen, keylen); + print_key_filename(file, flags & RKF_INLINE), count, onekeylen, + keylen); } if (state != PARSE_FINISHED) { msg(M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)", - error_filename, count, onekeylen, keylen); + print_key_filename(file, flags & RKF_INLINE), count, onekeylen, + keylen); } } @@ -1453,36 +1429,24 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) gc_free(&gc); } -/* - * Write key to file, return number of random bits - * written. - */ int write_key_file(const int nkeys, const char *filename) { struct gc_arena gc = gc_new(); - int fd, i; - int nbits = 0; + int nbits = nkeys * sizeof(struct key) * 8; /* must be large enough to hold full key file */ struct buffer out = alloc_buf_gc(2048, &gc); - struct buffer nbits_head_text = alloc_buf_gc(128, &gc); /* how to format the ascii file representation of key */ const int bytes_per_line = 16; - /* open key file */ - fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); - - if (fd == -1) - { - msg(M_ERR, "Cannot open shared secret file '%s' for write", filename); - } - + /* write header */ + buf_printf(&out, "#\n# %d bit OpenVPN static key\n#\n", nbits); buf_printf(&out, "%s\n", static_key_head); - for (i = 0; i < nkeys; ++i) + for (int i = 0; i < nkeys; ++i) { struct key key; char *fmt; @@ -1498,9 +1462,6 @@ write_key_file(const int nkeys, const char *filename) "\n", &gc); - /* increment random bits counter */ - nbits += sizeof(key) * 8; - /* write to holding buffer */ buf_printf(&out, "%s\n", fmt); @@ -1511,16 +1472,15 @@ write_key_file(const int nkeys, const char *filename) buf_printf(&out, "%s\n", static_key_foot); - /* write number of bits */ - buf_printf(&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits); - buf_write_string_file(&nbits_head_text, filename, fd); - + /* write key file to stdout if no filename given */ + if (!filename || strcmp(filename, "")==0) + { + printf("%.*s\n", BLEN(&out), BPTR(&out)); + } /* write key file, now formatted in out, to file */ - buf_write_string_file(&out, filename, fd); - - if (close(fd)) + else if (!buffer_write_file(filename, &out)) { - msg(M_ERR, "Close error on shared secret file %s", filename); + nbits = -1; } /* zero memory which held file content (memory will be freed by GC) */ @@ -1540,7 +1500,7 @@ must_have_n_keys(const char *filename, const char *option, const struct key2 *ke #ifdef ENABLE_SMALL msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d]", filename, option, key2->n, n); #else - msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey --secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option); + msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option); #endif } } @@ -1748,7 +1708,9 @@ prng_reset_nonce(void) { int i; for (i = 0; i < size; ++i) + { nonce_data[i] = (uint8_t) i; + } } #endif } @@ -1825,6 +1787,33 @@ get_random(void) return l; } +void +print_cipher(const cipher_kt_t *cipher) +{ + const char *var_key_size = cipher_kt_var_key_size(cipher) ? + " by default" : ""; + + printf("%s (%d bit key%s, ", + cipher_kt_name(cipher), + cipher_kt_key_size(cipher) * 8, var_key_size); + + if (cipher_kt_block_size(cipher) == 1) + { + printf("stream cipher"); + } + else + { + printf("%d bit block", cipher_kt_block_size(cipher) * 8); + } + + if (!cipher_kt_mode_cbc(cipher)) + { + printf(", TLS client/server mode only"); + } + + printf(")\n"); +} + static const cipher_name_pair * get_cipher_name_pair(const char *cipher_name) { @@ -1872,4 +1861,97 @@ translate_cipher_name_to_openvpn(const char *cipher_name) return pair->openvpn_name; } -#endif /* ENABLE_CRYPTO */ +void +write_pem_key_file(const char *filename, const char *pem_name) +{ + struct gc_arena gc = gc_new(); + struct key server_key = { 0 }; + struct buffer server_key_buf = clear_buf(); + struct buffer server_key_pem = clear_buf(); + + if (!rand_bytes((void *)&server_key, sizeof(server_key))) + { + msg(M_NONFATAL, "ERROR: could not generate random key"); + goto cleanup; + } + buf_set_read(&server_key_buf, (void *)&server_key, sizeof(server_key)); + if (!crypto_pem_encode(pem_name, &server_key_pem, + &server_key_buf, &gc)) + { + msg(M_WARN, "ERROR: could not PEM-encode key"); + goto cleanup; + } + + if (!filename || strcmp(filename, "")==0) + { + printf("%.*s", BLEN(&server_key_pem), BPTR(&server_key_pem)); + } + else if (!buffer_write_file(filename, &server_key_pem)) + { + msg(M_ERR, "ERROR: could not write key file"); + goto cleanup; + } + +cleanup: + secure_memzero(&server_key, sizeof(server_key)); + buf_clear(&server_key_pem); + gc_free(&gc); + return; +} + +bool +generate_ephemeral_key(struct buffer *key, const char *key_name) +{ + const int len = BCAP(key); + + msg(M_INFO, "Using random %s.", key_name); + + if (!rand_bytes(BEND(key), len)) + { + msg(M_WARN, "ERROR: could not generate random key"); + return false; + } + + buf_inc_len(key, len); + + return true; +} + +bool +read_pem_key_file(struct buffer *key, const char *pem_name, + const char *key_file, bool key_inline) +{ + bool ret = false; + struct buffer key_pem = { 0 }; + struct gc_arena gc = gc_new(); + + if (!key_inline) + { + key_pem = buffer_read_from_file(key_file, &gc); + if (!buf_valid(&key_pem)) + { + msg(M_WARN, "ERROR: failed to read %s file (%s)", + pem_name, key_file); + goto cleanup; + } + } + else + { + buf_set_read(&key_pem, (const void *)key_file, strlen(key_file) + 1); + } + + if (!crypto_pem_decode(pem_name, key, &key_pem)) + { + msg(M_WARN, "ERROR: %s pem decode failed", pem_name); + goto cleanup; + } + + ret = true; +cleanup: + if (!key_inline) + { + buf_clear(&key_pem); + } + gc_free(&gc); + return ret; +} diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h index 185bfd3..999f643 100644 --- a/src/openvpn/crypto.h +++ b/src/openvpn/crypto.h @@ -38,8 +38,7 @@ * - \b HMAC, covering the ciphertext IV + ciphertext. The HMAC size depends * on the \c \-\-auth option. If \c \-\-auth \c none is specified, there is no * HMAC at all. - * - \b Ciphertext \b IV, if not disabled by \c \-\-no-iv. The IV size depends on - * the \c \-\-cipher option. + * - \b Ciphertext \b IV. The IV size depends on the \c \-\-cipher option. * - \b Packet \b ID, a 32-bit incrementing packet counter that provides replay * protection (if not disabled by \c \-\-no-replay). * - \b Timestamp, a 32-bit timestamp of the current time. @@ -123,8 +122,6 @@ #ifndef CRYPTO_H #define CRYPTO_H -#ifdef ENABLE_CRYPTO - #include "crypto_backend.h" #include "basic.h" #include "buffer.h" @@ -248,17 +245,13 @@ struct crypto_options #define CO_PACKET_ID_LONG_FORM (1<<0) /**< Bit-flag indicating whether to use * OpenVPN's long packet ID format. */ -#define CO_USE_IV (1<<1) - /**< Bit-flag indicating whether to - * generate a pseudo-random IV for each - * packet being encrypted. */ -#define CO_IGNORE_PACKET_ID (1<<2) +#define CO_IGNORE_PACKET_ID (1<<1) /**< Bit-flag indicating whether to ignore * the packet ID of a received packet. * This flag is used during processing * of the first packet received from a * client. */ -#define CO_MUTE_REPLAY_WARNINGS (1<<3) +#define CO_MUTE_REPLAY_WARNINGS (1<<2) /**< Bit-flag indicating not to display * replay warnings. */ unsigned int flags; /**< Bit-flags determining behavior of @@ -278,16 +271,16 @@ struct crypto_options #define RKF_INLINE (1<<1) void read_key_file(struct key2 *key2, const char *file, const unsigned int flags); +/** + * Write nkeys 1024-bits keys to file. + * + * @returns number of random bits written, or -1 on failure. + */ int write_key_file(const int nkeys, const char *filename); -int read_passphrase_hash(const char *passphrase_file, - const md_kt_t *digest, - uint8_t *output, - int len); - void generate_key_random(struct key *key, const struct key_type *kt); -void check_replay_iv_consistency(const struct key_type *kt, bool packet_id, bool use_iv); +void check_replay_consistency(const struct key_type *kt, bool packet_id); bool check_key(struct key *key, const struct key_type *kt); @@ -306,7 +299,7 @@ int read_key(struct key *key, const struct key_type *kt, struct buffer *buf); * @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 + * @param tls_mode Specifies whether we are running in TLS mode, which allows * more ciphers than static key mode. * @param warn Print warnings when null cipher / auth is used. */ @@ -325,7 +318,7 @@ void free_key_ctx(struct key_ctx *ctx); void init_key_ctx_bi(struct key_ctx_bi *ctx, const struct key2 *key2, int key_direction, const struct key_type *kt, - const char *name); + const char *name); void free_key_ctx_bi(struct key_ctx_bi *ctx); @@ -421,13 +414,46 @@ bool crypto_check_replay(struct crypto_options *opt, /** Calculate crypto overhead and adjust frame to account for that */ void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type *kt, - bool use_iv, bool packet_id, bool packet_id_long_form); /** Return the worst-case OpenVPN crypto overhead (in bytes) */ unsigned int crypto_max_overhead(void); +/** + * Generate a server key with enough randomness to fill a key struct + * and write to file. + * + * @param filename Filename of the server key file to create. + * @param pem_name The name to use in the PEM header/footer. + */ +void +write_pem_key_file(const char *filename, const char *key_name); + +/** + * Generate ephermal key material into the key structure + * + * @param key the key structure that will hold the key material + * @param pem_name the name used for logging + * @return true if key generation was successful + */ +bool +generate_ephemeral_key(struct buffer *key, const char *pem_name); + +/** + * Read key material from a PEM encoded files into the key structure + * @param key the key structure that will hold the key material + * @param pem_name the name used in the pem encoding start/end lines + * @param key_file name of the file to read or the key itself if + * key_inline is true + * @param key_inline True if key_file contains an inline key, False + * otherwise. + * @return true if reading into key was successful + */ +bool +read_pem_key_file(struct buffer *key, const char *pem_name, + const char *key_file, bool key_inline); + /* Minimum length of the nonce used by the PRNG */ #define NONCE_SECRET_LEN_MIN 16 @@ -465,6 +491,12 @@ void prng_bytes(uint8_t *output, int len); void prng_uninit(void); +/* an analogue to the random() function, but use prng_bytes */ +long int get_random(void); + +/** Print a cipher list entry */ +void print_cipher(const cipher_kt_t *cipher); + void test_crypto(struct crypto_options *co, struct frame *f); @@ -487,8 +519,9 @@ void key2_print(const struct key2 *k, const char *prefix1); 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); + struct key_ctx_bi *ctx, const char *key_file, + bool key_inline, const int key_direction, + const char *key_name, const char *opt_name); /* * Inline functions @@ -498,20 +531,7 @@ void crypto_read_openvpn_key(const struct key_type *key_type, * 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; -} +int memcmp_constant_time(const void *a, const void *b, size_t size); static inline bool key_ctx_bi_defined(const struct key_ctx_bi *key) @@ -519,6 +539,16 @@ key_ctx_bi_defined(const struct key_ctx_bi *key) return key->encrypt.cipher || key->encrypt.hmac || key->decrypt.cipher || key->decrypt.hmac; } +/** + * To be used when printing a string that may contain inline data. + * + * If "is_inline" is true, return the inline tag. + * If "is_inline" is false and "str" is not NULL, return "str". + * Return the constant string "[NULL]" otherwise. + * + * @param str the original string to return when is_inline is false + * @param is_inline true when str contains an inline data of some sort + */ +const char *print_key_filename(const char *str, bool is_inline); -#endif /* ENABLE_CRYPTO */ #endif /* CRYPTO_H */ diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h index b3db925..85cb084 100644 --- a/src/openvpn/crypto_backend.h +++ b/src/openvpn/crypto_backend.h @@ -36,6 +36,7 @@ #include "crypto_mbedtls.h" #endif #include "basic.h" +#include "buffer.h" /* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */ #define OPENVPN_AEAD_TAG_LENGTH 16 @@ -50,7 +51,7 @@ typedef enum { MD_SHA1, MD_SHA256 -} hash_algo_type ; +} hash_algo_type; /** Struct used in cipher name translation table */ typedef struct { @@ -105,6 +106,34 @@ void show_available_digests(void); void show_available_engines(void); +/** + * Encode binary data as PEM. + * + * @param name The name to use in the PEM header/footer. + * @param dst Destination buffer for PEM-encoded data. Must be a valid + * pointer to an uninitialized buffer structure. Iff this + * function returns true, the buffer will contain memory + * allocated through the supplied gc. + * @param src Source buffer. + * @param gc The garbage collector to use when allocating memory for dst. + * + * @return true iff PEM encode succeeded. + */ +bool crypto_pem_encode(const char *name, struct buffer *dst, + const struct buffer *src, struct gc_arena *gc); + +/** + * Decode a PEM buffer to binary data. + * + * @param name The name expected in the PEM header/footer. + * @param dst Destination buffer for decoded data. + * @param src Source buffer (PEM data). + * + * @return true iff PEM decode succeeded. + */ +bool crypto_pem_decode(const char *name, struct buffer *dst, + const struct buffer *src); + /* * * Random number functions, used in cases where we want @@ -198,7 +227,8 @@ void cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH], * initialise encryption/decryption. * * @param ciphername Name of the cipher to retrieve parameters for (e.g. - * \c AES-128-CBC). + * \c AES-128-CBC). Will be translated to the library name + * from the openvpn config name if needed. * * @return A statically allocated structure containing parameters * for the given cipher, or NULL if no matching parameters @@ -208,6 +238,8 @@ const cipher_kt_t *cipher_kt_get(const char *ciphername); /** * Retrieve a string describing the cipher (e.g. \c AES-128-CBC). + * The returned name is normalised to the OpenVPN config name in case the + * name differs from the name used by the crypto library. * * @param cipher_kt Static cipher parameters * @@ -256,6 +288,11 @@ int cipher_kt_block_size(const cipher_kt_t *cipher_kt); int cipher_kt_tag_size(const cipher_kt_t *cipher_kt); /** + * Returns true if we consider this cipher to be insecure. + */ +bool cipher_kt_insecure(const cipher_kt_t *cipher); + +/** * Returns the mode that the cipher runs in. * * @param cipher_kt Static cipher parameters. May not be NULL. @@ -384,7 +421,7 @@ const cipher_kt_t *cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx); * * @return \c 0 on failure, \c 1 on success. */ -int cipher_ctx_reset(cipher_ctx_t *ctx, uint8_t *iv_buf); +int cipher_ctx_reset(cipher_ctx_t *ctx, const uint8_t *iv_buf); /** * Updates the given cipher context, providing additional data (AD) for @@ -492,7 +529,7 @@ const char *md_kt_name(const md_kt_t *kt); * * @return Message digest size, in bytes, or 0 if ctx was NULL. */ -int md_kt_size(const md_kt_t *kt); +unsigned char md_kt_size(const md_kt_t *kt); /* @@ -593,7 +630,7 @@ void hmac_ctx_free(hmac_ctx_t *ctx); * Initialises the given HMAC context, using the given digest * and key. * - * @param ctx HMAC context to intialise + * @param ctx HMAC context to initialise * @param key The key to use for the HMAC * @param key_len The key length to use * @param kt Static message digest parameters diff --git a/src/openvpn/crypto_mbedtls.c b/src/openvpn/crypto_mbedtls.c index 748043e..fbb1f12 100644 --- a/src/openvpn/crypto_mbedtls.c +++ b/src/openvpn/crypto_mbedtls.c @@ -34,21 +34,24 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) +#if defined(ENABLE_CRYPTO_MBEDTLS) #include "errlevel.h" #include "basic.h" #include "buffer.h" +#include "crypto.h" #include "integer.h" #include "crypto_backend.h" #include "otime.h" #include "misc.h" +#include <mbedtls/base64.h> #include <mbedtls/des.h> #include <mbedtls/error.h> #include <mbedtls/md5.h> #include <mbedtls/cipher.h> #include <mbedtls/havege.h> +#include <mbedtls/pem.h> #include <mbedtls/entropy.h> @@ -138,26 +141,6 @@ const cipher_name_pair cipher_name_translation_table[] = { 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(void) { @@ -166,14 +149,16 @@ show_available_ciphers(void) #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"); + "parameter to the --data-ciphers (or --cipher) option. Using a\n" + "GCM or CBC mode is recommended. In static key mode only CBC\n" + "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) + if (info && !cipher_kt_insecure(info) + && (cipher_kt_mode_aead(info) || cipher_kt_mode_cbc(info))) { print_cipher(info); } @@ -186,7 +171,8 @@ show_available_ciphers(void) while (*ciphers != 0) { const cipher_kt_t *info = mbedtls_cipher_info_from_type(*ciphers); - if (info && cipher_kt_block_size(info) < 128/8) + if (info && cipher_kt_insecure(info) + && (cipher_kt_mode_aead(info) || cipher_kt_mode_cbc(info))) { print_cipher(info); } @@ -229,6 +215,84 @@ show_available_engines(void) "available\n"); } +bool +crypto_pem_encode(const char *name, struct buffer *dst, + const struct buffer *src, struct gc_arena *gc) +{ + /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */ + char header[1000+1] = { 0 }; + char footer[1000+1] = { 0 }; + + if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----\n", name)) + { + return false; + } + if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----\n", name)) + { + return false; + } + + size_t out_len = 0; + if (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL != + mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src), + NULL, 0, &out_len)) + { + return false; + } + + /* We set the size buf to out_len-1 to NOT include the 0 byte that + * mbedtls_pem_write_buffer in its length calculation */ + *dst = alloc_buf_gc(out_len, gc); + if (!mbed_ok(mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src), + BPTR(dst), BCAP(dst), &out_len)) + || !buf_inc_len(dst, out_len-1)) + { + CLEAR(*dst); + return false; + } + + return true; +} + +bool +crypto_pem_decode(const char *name, struct buffer *dst, + const struct buffer *src) +{ + /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */ + char header[1000+1] = { 0 }; + char footer[1000+1] = { 0 }; + + if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----", name)) + { + return false; + } + if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----", name)) + { + return false; + } + + /* mbed TLS requires the src to be null-terminated */ + /* allocate a new buffer to avoid modifying the src buffer */ + struct gc_arena gc = gc_new(); + struct buffer input = alloc_buf_gc(BLEN(src) + 1, &gc); + buf_copy(&input, src); + buf_null_terminate(&input); + + size_t use_len = 0; + mbedtls_pem_context ctx = { 0 }; + bool ret = mbed_ok(mbedtls_pem_read_buffer(&ctx, header, footer, BPTR(&input), + NULL, 0, &use_len)); + if (ret && !buf_write(dst, ctx.buf, ctx.buflen)) + { + ret = false; + msg(M_WARN, "PEM decode error: destination buffer too small"); + } + + mbedtls_pem_free(&ctx); + gc_free(&gc); + return ret; +} + /* * * Random number functions, used in cases where we want @@ -402,6 +466,7 @@ cipher_kt_get(const char *ciphername) ASSERT(ciphername); + ciphername = translate_cipher_name_from_openvpn(ciphername); cipher = mbedtls_cipher_info_from_string(ciphername); if (NULL == cipher) @@ -466,15 +531,23 @@ cipher_kt_block_size(const mbedtls_cipher_info_t *cipher_kt) 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; } +bool +cipher_kt_insecure(const mbedtls_cipher_info_t *cipher_kt) +{ + return !(cipher_kt_block_size(cipher_kt) >= 128 / 8 +#ifdef MBEDTLS_CHACHAPOLY_C + || cipher_kt->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 +#endif + ); +} + int cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt) { @@ -498,7 +571,11 @@ cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) bool cipher_kt_mode_aead(const cipher_kt_t *cipher) { - return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_GCM; + return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM +#ifdef MBEDTLS_CHACHAPOLY_C + || cipher_kt_mode(cipher) == MBEDTLS_MODE_CHACHAPOLY +#endif + ); } @@ -554,7 +631,6 @@ cipher_ctx_iv_length(const mbedtls_cipher_context_t *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; @@ -566,9 +642,6 @@ cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len) } return 1; -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif /* HAVE_AEAD_CIPHER_MODES */ } int @@ -592,7 +665,7 @@ cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx) } int -cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf) +cipher_ctx_reset(mbedtls_cipher_context_t *ctx, const uint8_t *iv_buf) { if (!mbed_ok(mbedtls_cipher_reset(ctx))) { @@ -610,7 +683,6 @@ cipher_ctx_reset(mbedtls_cipher_context_t *ctx, uint8_t *iv_buf) 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; @@ -622,9 +694,6 @@ cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len) } return 1; -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif /* HAVE_AEAD_CIPHER_MODES */ } int @@ -663,7 +732,6 @@ 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) @@ -695,9 +763,6 @@ cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst, } return 1; -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif /* HAVE_AEAD_CIPHER_MODES */ } void @@ -751,7 +816,7 @@ md_kt_name(const mbedtls_md_info_t *kt) return mbedtls_md_get_name(kt); } -int +unsigned char md_kt_size(const mbedtls_md_info_t *kt) { if (NULL == kt) @@ -781,7 +846,8 @@ md_ctx_new(void) return ctx; } -void md_ctx_free(mbedtls_md_context_t *ctx) +void +md_ctx_free(mbedtls_md_context_t *ctx) { free(ctx); } @@ -899,4 +965,23 @@ 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 */ +int +memcmp_constant_time(const void *a, const void *b, size_t size) +{ + /* mbed TLS has a no const time memcmp function that it exposes + * via its APIs like OpenSSL does with CRYPTO_memcmp + * Adapt the function that mbedtls itself uses in + * mbedtls_safer_memcmp as it considers that to be safe */ + volatile const unsigned char *A = (volatile const unsigned char *) a; + volatile const unsigned char *B = (volatile const unsigned char *) b; + volatile unsigned char diff = 0; + + for (size_t i = 0; i < size; i++) + { + unsigned char x = A[i], y = B[i]; + diff |= x ^ y; + } + + return diff; +} +#endif /* ENABLE_CRYPTO_MBEDTLS */ diff --git a/src/openvpn/crypto_mbedtls.h b/src/openvpn/crypto_mbedtls.h index 452b06e..c4b13b7 100644 --- a/src/openvpn/crypto_mbedtls.h +++ b/src/openvpn/crypto_mbedtls.h @@ -146,5 +146,10 @@ mbed_log_func_line_lite(unsigned int flags, int errval, #define mbed_ok(errval) \ mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__) +static inline bool +cipher_kt_var_key_size(const cipher_kt_t *cipher) +{ + return cipher->flags & MBEDTLS_CIPHER_VARIABLE_KEY_LEN; +} #endif /* CRYPTO_MBEDTLS_H_ */ diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index 3abcc99..c60d4a5 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -34,7 +34,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO_OPENSSL) #include "basic.h" #include "buffer.h" @@ -43,6 +43,7 @@ #include "crypto_backend.h" #include "openssl_compat.h" +#include <openssl/conf.h> #include <openssl/des.h> #include <openssl/err.h> #include <openssl/evp.h> @@ -63,6 +64,7 @@ #endif #if HAVE_OPENSSL_ENGINE +#include <openssl/ui.h> #include <openssl/engine.h> static bool engine_initialized = false; /* GLOBAL */ @@ -148,6 +150,11 @@ crypto_init_lib_engine(const char *engine_name) void crypto_init_lib(void) { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + OPENSSL_config(NULL); +#endif /* * If you build the OpenSSL library and OpenVPN with * CRYPTO_MDEBUG, you will get a listing of OpenSSL @@ -202,12 +209,12 @@ crypto_print_openssl_errors(const unsigned int flags) else if (ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL) { msg(D_CRYPT_ERRORS, "TLS error: Unsupported protocol. This typically " - "indicates that client and server have no common TLS version enabled. " - "This can be caused by mismatched tls-version-min and tls-version-max " - "options on client and server. " - "If your OpenVPN client is between v2.3.6 and v2.3.2 try adding " - "tls-version-min 1.0 to the client configuration to use TLS 1.0+ " - "instead of TLS 1.0 only"); + "indicates that client and server have no common TLS version enabled. " + "This can be caused by mismatched tls-version-min and tls-version-max " + "options on client and server. " + "If your OpenVPN client is between v2.3.6 and v2.3.2 try adding " + "tls-version-min 1.0 to the client configuration to use TLS 1.0+ " + "instead of TLS 1.0 only"); } msg(flags, "OpenSSL: %s", ERR_error_string(err, NULL)); } @@ -254,6 +261,7 @@ 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" }, + { "CHACHA20-POLY1305", "ChaCha20-Poly1305" }, }; const size_t cipher_name_translation_table_count = sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table); @@ -265,27 +273,7 @@ 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); -} - -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); + return strcmp(cipher_kt_name(*cipher_a), cipher_kt_name(*cipher_b)); } void @@ -299,11 +287,11 @@ show_available_ciphers(void) size_t num_ciphers = 0; #ifndef ENABLE_SMALL 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"); + "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n" + "parameter to the --data-ciphers (or --cipher) option. The\n" + "default key size is shown as well as whether or not it can be\n" + "changed with the --keysize directive. Using a GCM or CBC mode\n" + "is recommended. In static key mode only CBC mode is allowed.\n\n"); #endif for (nid = 0; nid < 10000; ++nid) @@ -313,9 +301,7 @@ show_available_ciphers(void) #ifdef ENABLE_OFB_CFB_MODE || cipher_kt_mode_ofb_cfb(cipher) #endif -#ifdef HAVE_AEAD_CIPHER_MODES || cipher_kt_mode_aead(cipher) -#endif )) { cipher_list[num_ciphers++] = cipher; @@ -327,10 +313,12 @@ show_available_ciphers(void) } } - qsort(cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp); + /* cast to non-const to prevent warning */ + qsort((EVP_CIPHER *)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) + for (i = 0; i < num_ciphers; i++) + { + if (!cipher_kt_insecure(cipher_list[i])) { print_cipher(cipher_list[i]); } @@ -338,8 +326,9 @@ show_available_ciphers(void) 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) + for (i = 0; i < num_ciphers; i++) + { + if (cipher_kt_insecure(cipher_list[i])) { print_cipher(cipher_list[i]); } @@ -396,6 +385,88 @@ show_available_engines(void) #endif } + +bool +crypto_pem_encode(const char *name, struct buffer *dst, + const struct buffer *src, struct gc_arena *gc) +{ + bool ret = false; + BIO *bio = BIO_new(BIO_s_mem()); + if (!bio || !PEM_write_bio(bio, name, "", BPTR(src), BLEN(src))) + { + ret = false; + goto cleanup; + } + + BUF_MEM *bptr; + BIO_get_mem_ptr(bio, &bptr); + + *dst = alloc_buf_gc(bptr->length, gc); + ASSERT(buf_write(dst, bptr->data, bptr->length)); + + ret = true; +cleanup: + if (!BIO_free(bio)) + { + ret = false; + } + + return ret; +} + +bool +crypto_pem_decode(const char *name, struct buffer *dst, + const struct buffer *src) +{ + bool ret = false; + + BIO *bio = BIO_new_mem_buf((char *)BPTR(src), BLEN(src)); + if (!bio) + { + crypto_msg(M_FATAL, "Cannot open memory BIO for PEM decode"); + } + + char *name_read = NULL; + char *header_read = NULL; + uint8_t *data_read = NULL; + long data_read_len = 0; + if (!PEM_read_bio(bio, &name_read, &header_read, &data_read, + &data_read_len)) + { + dmsg(D_CRYPT_ERRORS, "%s: PEM decode failed", __func__); + goto cleanup; + } + + if (strcmp(name, name_read)) + { + dmsg(D_CRYPT_ERRORS, + "%s: unexpected PEM name (got '%s', expected '%s')", + __func__, name_read, name); + goto cleanup; + } + + uint8_t *dst_data = buf_write_alloc(dst, data_read_len); + if (!dst_data) + { + dmsg(D_CRYPT_ERRORS, "%s: dst too small (%i, needs %li)", __func__, + BCAP(dst), data_read_len); + goto cleanup; + } + memcpy(dst_data, data_read, data_read_len); + + ret = true; +cleanup: + OPENSSL_free(name_read); + OPENSSL_free(header_read); + OPENSSL_free(data_read); + if (!BIO_free(bio)) + { + ret = false; + } + + return ret; +} + /* * * Random number functions, used in cases where we want @@ -515,6 +586,7 @@ cipher_kt_get(const char *ciphername) ASSERT(ciphername); + ciphername = translate_cipher_name_from_openvpn(ciphername); cipher = EVP_get_cipherbyname(ciphername); if (NULL == cipher) @@ -543,7 +615,9 @@ cipher_kt_name(const EVP_CIPHER *cipher_kt) { return "[null-cipher]"; } - return EVP_CIPHER_name(cipher_kt); + + const char *name = EVP_CIPHER_name(cipher_kt); + return translate_cipher_name_to_openvpn(name); } int @@ -574,7 +648,7 @@ cipher_kt_block_size(const EVP_CIPHER *cipher) int block_size = EVP_CIPHER_block_size(cipher); - orig_name = cipher_kt_name(cipher); + orig_name = EVP_CIPHER_name(cipher); if (!orig_name) { goto cleanup; @@ -613,6 +687,16 @@ cipher_kt_tag_size(const EVP_CIPHER *cipher_kt) } } +bool +cipher_kt_insecure(const EVP_CIPHER *cipher) +{ + return !(cipher_kt_block_size(cipher) >= 128 / 8 +#ifdef NID_chacha20_poly1305 + || EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305 +#endif + ); +} + int cipher_kt_mode(const EVP_CIPHER *cipher_kt) { @@ -624,11 +708,8 @@ bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) { return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC -#ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ - && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) -#endif - ; + && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER); } bool @@ -636,21 +717,28 @@ 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) -#ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ - && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) -#endif - ; + && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_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; + if (cipher) + { + switch (EVP_CIPHER_nid(cipher)) + { + case NID_aes_128_gcm: + case NID_aes_192_gcm: + case NID_aes_256_gcm: +#ifdef NID_chacha20_poly1305 + case NID_chacha20_poly1305: #endif + return true; + } + } + + return false; } /* @@ -708,11 +796,7 @@ cipher_ctx_iv_length(const EVP_CIPHER_CTX *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 @@ -735,7 +819,7 @@ cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx) int -cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) +cipher_ctx_reset(EVP_CIPHER_CTX *ctx, const uint8_t *iv_buf) { return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv_buf, -1); } @@ -743,16 +827,12 @@ cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) 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 /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif } int @@ -776,7 +856,6 @@ 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)) { @@ -784,9 +863,6 @@ cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, } return cipher_ctx_final(ctx, dst, dst_len); -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif } void @@ -837,10 +913,10 @@ md_kt_name(const EVP_MD *kt) return EVP_MD_name(kt); } -int +unsigned char md_kt_size(const EVP_MD *kt) { - return EVP_MD_size(kt); + return (unsigned char)EVP_MD_size(kt); } @@ -866,7 +942,8 @@ md_ctx_new(void) return ctx; } -void md_ctx_free(EVP_MD_CTX *ctx) +void +md_ctx_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); } @@ -972,4 +1049,70 @@ hmac_ctx_final(HMAC_CTX *ctx, uint8_t *dst) HMAC_Final(ctx, dst, &in_hmac_len); } -#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_OPENSSL */ +int +memcmp_constant_time(const void *a, const void *b, size_t size) +{ + return CRYPTO_memcmp(a, b, size); +} + +#if HAVE_OPENSSL_ENGINE +static int +ui_reader(UI *ui, UI_STRING *uis) +{ + SSL_CTX *ctx = UI_get0_user_data(ui); + + if (UI_get_string_type(uis) == UIT_PROMPT) + { + pem_password_cb *cb = SSL_CTX_get_default_passwd_cb(ctx); + void *d = SSL_CTX_get_default_passwd_cb_userdata(ctx); + char password[64]; + + cb(password, sizeof(password), 0, d); + UI_set_result(ui, uis, password); + + return 1; + } + return 0; +} +#endif + +EVP_PKEY * +engine_load_key(const char *file, SSL_CTX *ctx) +{ +#if HAVE_OPENSSL_ENGINE + UI_METHOD *ui; + EVP_PKEY *pkey; + + if (!engine_persist) + { + return NULL; + } + + /* this will print out the error from BIO_read */ + crypto_msg(M_INFO, "PEM_read_bio failed, now trying engine method to load private key"); + + ui = UI_create_method("openvpn"); + if (!ui) + { + crypto_msg(M_FATAL, "Engine UI creation failed"); + return NULL; + } + + UI_method_set_reader(ui, ui_reader); + + ENGINE_init(engine_persist); + pkey = ENGINE_load_private_key(engine_persist, file, ui, ctx); + ENGINE_finish(engine_persist); + if (!pkey) + { + crypto_msg(M_FATAL, "Engine could not load key file"); + } + + UI_destroy_method(ui); + return pkey; +#else /* if HAVE_OPENSSL_ENGINE */ + return NULL; +#endif /* if HAVE_OPENSSL_ENGINE */ +} + +#endif /* ENABLE_CRYPTO_OPENSSL */ diff --git a/src/openvpn/crypto_openssl.h b/src/openvpn/crypto_openssl.h index 0a41370..e6f8f53 100644 --- a/src/openvpn/crypto_openssl.h +++ b/src/openvpn/crypto_openssl.h @@ -61,13 +61,9 @@ 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 @@ -101,5 +97,22 @@ void crypto_print_openssl_errors(const unsigned int flags); msg((flags), __VA_ARGS__); \ } while (false) +static inline bool +cipher_kt_var_key_size(const cipher_kt_t *cipher) +{ + return EVP_CIPHER_flags(cipher) & EVP_CIPH_VARIABLE_LENGTH; +} + +/** + * Load a key file from an engine + * + * @param file The engine file to load + * @param ui The UI method for the password prompt + * @param data The data to pass to the UI method + * + * @return The private key if successful or NULL if not + */ +EVP_PKEY * +engine_load_key(const char *file, SSL_CTX *ctx); #endif /* CRYPTO_OPENSSL_H_ */ diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 0f95d00..6c4df9e 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com> + * Copyright (c) 2018 Selva Nair <selva.nair@gmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifi- @@ -103,6 +104,9 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { 0, NULL } }; +/* index for storing external data in EC_KEY: < 0 means uninitialized */ +static int ec_data_idx = -1; + /* Global EVP_PKEY_METHOD used to override the sign operation */ static EVP_PKEY_METHOD *pmethod; static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx); @@ -114,10 +118,10 @@ typedef struct _CAPI_DATA { HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov; DWORD key_spec; BOOL free_crypt_prov; + int ref_count; } CAPI_DATA; -/** - * Translate OpenSSL padding type to CNG padding type +/* Translate OpenSSL padding type to CNG padding type * Returns 0 for unknown/unsupported padding. */ static DWORD @@ -128,7 +132,6 @@ cng_padding_type(int padding) switch (padding) { case RSA_NO_PADDING: - pad = BCRYPT_PAD_NONE; break; case RSA_PKCS1_PADDING: @@ -147,7 +150,7 @@ cng_padding_type(int padding) return pad; } -/** +/* * Translate OpenSSL hash OID to CNG algorithm name. Returns * "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1 * mixed hash used in TLS 1.1 and earlier. @@ -190,6 +193,31 @@ cng_hash_algo(int md_type) return alg; } +static void +CAPI_DATA_free(CAPI_DATA *cd) +{ + if (!cd || cd->ref_count-- > 0) + { + return; + } + if (cd->free_crypt_prov && cd->crypt_prov) + { + if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) + { + NCryptFreeObject(cd->crypt_prov); + } + else + { + CryptReleaseContext(cd->crypt_prov, 0); + } + } + if (cd->cert_context) + { + CertFreeCertificateContext(cd->cert_context); + } + free(cd); +} + static char * ms_error_text(DWORD ms_err) { @@ -211,7 +239,8 @@ ms_error_text(DWORD ms_err) /* trim to the left */ if (rv) { - for (p = rv + strlen(rv) - 1; p >= rv; p--) { + for (p = rv + strlen(rv) - 1; p >= rv; p--) + { if (isspace(*p)) { *p = '\0'; @@ -250,7 +279,8 @@ err_put_ms_error(DWORD ms_err, int func, const char *file, int line) } /* since MS error codes are 32 bit, and the ones in the ERR_... system is * only 12, we must have a mapping table between them. */ - for (i = 0; i < ERR_MAP_SZ; i++) { + for (i = 0; i < ERR_MAP_SZ; i++) + { if (err_map[i].ms_err == ms_err) { ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line); @@ -299,7 +329,7 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, in * Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT * key handle in cd->crypt_prov. On return the signature is in 'to'. Returns * the length of the signature or 0 on error. - * Only RSA is supported and padding should be BCRYPT_PAD_PKCS1 or + * This is used only for RSA and padding should be BCRYPT_PAD_PKCS1 or * BCRYPT_PAD_PSS. * If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added * to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used @@ -363,12 +393,6 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i return 0; } - if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) - { - return priv_enc_CNG(cd, NULL, from, flen, to, RSA_size(rsa), - cng_padding_type(padding), 0); - } - if (padding != RSA_PKCS1_PADDING) { /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ @@ -376,6 +400,12 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i return 0; } + if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) + { + return priv_enc_CNG(cd, NULL, from, flen, to, RSA_size(rsa), + cng_padding_type(padding), 0); + } + /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash @@ -447,7 +477,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i */ static int rsa_sign_CNG(int type, const unsigned char *m, unsigned int m_len, - unsigned char *sig, unsigned int *siglen, const RSA *rsa) + unsigned char *sig, unsigned int *siglen, const RSA *rsa) { CAPI_DATA *cd = (CAPI_DATA *) RSA_meth_get0_app_data(RSA_get_method(rsa)); const wchar_t *alg = NULL; @@ -502,26 +532,206 @@ finish(RSA *rsa) { return 0; } - if (cd->crypt_prov && cd->free_crypt_prov) + CAPI_DATA_free(cd); + RSA_meth_free((RSA_METHOD *) rsa_meth); + return 1; +} + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC) + +static EC_KEY_METHOD *ec_method = NULL; + +/** EC_KEY_METHOD callback: called when the key is freed */ +static void +ec_finish(EC_KEY *ec) +{ + EC_KEY_METHOD_free(ec_method); + ec_method = NULL; + CAPI_DATA *cd = EC_KEY_get_ex_data(ec, ec_data_idx); + CAPI_DATA_free(cd); + EC_KEY_set_ex_data(ec, ec_data_idx, NULL); +} + +/** EC_KEY_METHOD callback sign_setup(): we do nothing here */ +static int +ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) +{ + return 1; +} + +/** + * Helper to convert ECDSA signature returned by NCryptSignHash + * to an ECDSA_SIG structure. + * On entry 'buf[]' of length len contains r and s concatenated. + * Returns a newly allocated ECDSA_SIG or NULL (on error). + */ +static ECDSA_SIG * +ecdsa_bin2sig(unsigned char *buf, int len) +{ + ECDSA_SIG *ecsig = NULL; + DWORD rlen = len/2; + BIGNUM *r = BN_bin2bn(buf, rlen, NULL); + BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL); + if (!r || !s) + { + goto err; + } + ecsig = ECDSA_SIG_new(); /* in openssl 1.1 this does not allocate r, s */ + if (!ecsig) { - if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) - { - NCryptFreeObject(cd->crypt_prov); - } - else + goto err; + } + if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */ + { + ECDSA_SIG_free(ecsig); + goto err; + } + return ecsig; +err: + BN_free(r); /* it is ok to free NULL BN */ + BN_free(s); + return NULL; +} + +/** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */ +static ECDSA_SIG * +ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, + const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec) +{ + ECDSA_SIG *ecsig = NULL; + CAPI_DATA *cd = (CAPI_DATA *)EC_KEY_get_ex_data(ec, ec_data_idx); + + ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC); + + NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; + BYTE buf[512]; /* large enough buffer for signature to avoid malloc */ + DWORD len = _countof(buf); + + msg(D_LOW, "Cryptoapi: signing hash using EC key: data size = %d", dgstlen); + + DWORD status = NCryptSignHash(hkey, NULL, (BYTE *)dgst, dgstlen, (BYTE *)buf, len, &len, 0); + if (status != ERROR_SUCCESS) + { + SetLastError(status); + CRYPTOAPIerr(CRYPTOAPI_F_NCRYPT_SIGN_HASH); + } + else + { + /* NCryptSignHash returns r, s concatenated in buf[] */ + ecsig = ecdsa_bin2sig(buf, len); + } + return ecsig; +} + +/** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */ +static int +ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, + unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) +{ + ECDSA_SIG *s; + + *siglen = 0; + s = ecdsa_sign_sig(dgst, dgstlen, NULL, NULL, ec); + if (s == NULL) + { + return 0; + } + + /* convert internal signature structure 's' to DER encoded byte array in sig */ + int len = i2d_ECDSA_SIG(s, NULL); + if (len > ECDSA_size(ec)) + { + ECDSA_SIG_free(s); + msg(M_NONFATAL,"Error: DER encoded ECDSA signature is too long (%d bytes)", len); + return 0; + } + *siglen = i2d_ECDSA_SIG(s, &sig); + ECDSA_SIG_free(s); + + return 1; +} + +static int +ssl_ctx_set_eckey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey) +{ + EC_KEY *ec = NULL; + EVP_PKEY *privkey = NULL; + + if (cd->key_spec != CERT_NCRYPT_KEY_SPEC) + { + msg(M_NONFATAL, "ERROR: cryptoapicert with only legacy private key handle available." + " EC certificate not supported."); + goto err; + } + /* create a method struct with default callbacks filled in */ + ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (!ec_method) + { + goto err; + } + + /* We only need to set finish among init methods, and sign methods */ + EC_KEY_METHOD_set_init(ec_method, NULL, ec_finish, NULL, NULL, NULL, NULL); + EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); + + ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) + { + goto err; + } + if (!EC_KEY_set_method(ec, ec_method)) + { + goto err; + } + + /* get an index to store cd as external data */ + if (ec_data_idx < 0) + { + ec_data_idx = EC_KEY_get_ex_new_index(0, "cryptapicert ec key", NULL, NULL, NULL); + if (ec_data_idx < 0) { - CryptReleaseContext(cd->crypt_prov, 0); + goto err; } } - if (cd->cert_context) + EC_KEY_set_ex_data(ec, ec_data_idx, cd); + + /* cd assigned to ec as ex_data, increase its refcount */ + cd->ref_count++; + + privkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) { - CertFreeCertificateContext(cd->cert_context); + EC_KEY_free(ec); + goto err; } - free(cd); - RSA_meth_free((RSA_METHOD*) rsa_meth); + /* from here on ec will get freed with privkey */ + + if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey)) + { + goto err; + } + EVP_PKEY_free(privkey); /* this will dn_ref or free ec as well */ return 1; + +err: + if (privkey) + { + EVP_PKEY_free(privkey); + } + else if (ec) + { + EC_KEY_free(ec); + } + if (ec_method) /* do always set ec_method = NULL after freeing it */ + { + EC_KEY_METHOD_free(ec_method); + ec_method = NULL; + } + return 0; } +#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */ + static const CERT_CONTEXT * find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) { @@ -599,7 +809,7 @@ find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) goto out; } - while(true) + while (true) { int validity = 1; /* this frees previous rv, if not NULL */ @@ -643,6 +853,8 @@ retrieve_capi_data(EVP_PKEY *pkey) static int pkey_rsa_sign_init(EVP_PKEY_CTX *ctx) { + msg(D_LOW, "cryptoapicert: enter pkey_rsa_sign_init"); + EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); if (pkey && retrieve_capi_data(pkey)) @@ -660,7 +872,7 @@ pkey_rsa_sign_init(EVP_PKEY_CTX *ctx) * Implementation of EVP_PKEY_sign() using CNG: sign the digest in |tbs| * and save the the signature in |sig| and its size in |*siglen|. * If |sig| is NULL the required buffer size is returned in |*siglen|. - * Returns 1 on success, 0 or a negative integer on error. + * Returns value is 1 on success, 0 or a negative integer on error. */ static int pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, @@ -671,9 +883,9 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, EVP_MD *md = NULL; const wchar_t *alg = NULL; - int padding; - int hashlen; - int saltlen; + int padding = 0; + int hashlen = 0; + int saltlen = 0; pkey = EVP_PKEY_CTX_get0_pkey(ctx); if (pkey) @@ -752,7 +964,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen)) { - msg(M_WARN|M_INFO, "cryptoapicert: unable to get the salt length from context." + msg(M_WARN, "cryptoapicert: unable to get the salt length from context." " Using the default value."); saltlen = -1; } @@ -784,6 +996,7 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen); } + msg(D_LOW, "cryptoapicert: calling priv_enc_CNG with alg = %ls", alg); *siglen = priv_enc_CNG(cd, alg, tbs, (int)tbslen, sig, *siglen, cng_padding_type(padding), (DWORD)saltlen); @@ -792,14 +1005,131 @@ pkey_rsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, #endif /* OPENSSL_VERSION >= 1.1.0 */ +static int +ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey) +{ + RSA *rsa = NULL, *pub_rsa; + RSA_METHOD *my_rsa_method = NULL; + bool rsa_method_set = false; + + my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method", + RSA_METHOD_FLAG_NO_CHECK); + check_malloc_return(my_rsa_method); + RSA_meth_set_pub_enc(my_rsa_method, rsa_pub_enc); + RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec); + RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc); + RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec); + RSA_meth_set_init(my_rsa_method, NULL); + RSA_meth_set_finish(my_rsa_method, finish); + RSA_meth_set0_app_data(my_rsa_method, cd); + + /* + * For CNG, set the RSA_sign method which gets priority over priv_enc(). + * This method is called with the raw hash without the digestinfo + * header and works better when using NCryptSignHash() with some tokens. + * However, if PSS padding is in use, openssl does not call this + * function but adds the padding and then calls rsa_priv_enc() + * with padding set to NONE which is not supported by CNG. + * So, when posisble (OpenSSL 1.1.0 and up), we hook on to the sign + * operation in EVP_PKEY_METHOD struct. + */ + if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) + { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG); +#else + /* pmethod is global -- initialize only if NULL */ + if (!pmethod) + { + pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0); + if (!pmethod) + { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); + goto err; + } + const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA); + EVP_PKEY_meth_copy(pmethod, default_pmethod); + + /* We want to override only sign_init() and sign() */ + EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign); + EVP_PKEY_meth_add0(pmethod); + + /* Keep a copy of the default sign and sign_init methods */ + +#if (OPENSSL_VERSION_NUMBER < 0x1010009fL) /* > version 1.1.0i */ + /* The function signature is not const-correct in these versions */ + EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD *)default_pmethod, &default_pkey_sign_init, + &default_pkey_sign); +#else + EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init, + &default_pkey_sign); + +#endif + } +#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */ + } + + rsa = RSA_new(); + if (rsa == NULL) + { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); + goto err; + } + + pub_rsa = EVP_PKEY_get0_RSA(pkey); + if (!pub_rsa) + { + goto err; + } + + /* Our private key is external, so we fill in only n and e from the public key */ + const BIGNUM *n = NULL; + const BIGNUM *e = NULL; + RSA_get0_key(pub_rsa, &n, &e, NULL); + BIGNUM *rsa_n = BN_dup(n); + BIGNUM *rsa_e = BN_dup(e); + if (!rsa_n || !rsa_e || !RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) + { + BN_free(rsa_n); /* ok to free even if NULL */ + BN_free(rsa_e); + msg(M_NONFATAL, "ERROR: %s: out of memory", __func__); + goto err; + } + RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); + if (!RSA_set_method(rsa, my_rsa_method)) + { + goto err; + } + rsa_method_set = true; /* flag that method pointer will get freed with the key */ + cd->ref_count++; /* with method, cd gets assigned to the key as well */ + + if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa)) + { + goto err; + } + /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so + * we decrease it here with RSA_free(), or it will never be cleaned up. */ + RSA_free(rsa); + return 1; + +err: + if (rsa) + { + RSA_free(rsa); + } + if (my_rsa_method && !rsa_method_set) + { + RSA_meth_free(my_rsa_method); + } + return 0; +} + int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) { HCERTSTORE cs; X509 *cert = NULL; - RSA *rsa = NULL, *pub_rsa; CAPI_DATA *cd = calloc(1, sizeof(*cd)); - RSA_METHOD *my_rsa_method = NULL; if (cd == NULL) { @@ -848,7 +1178,7 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) DWORD flags = CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG; if (!CryptAcquireCertificatePrivateKey(cd->cert_context, flags, NULL, - &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) + &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) { /* if we don't have a smart card reader here, and we try to access a * smart card certificate, we get: @@ -880,74 +1210,13 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) } } - my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method", - RSA_METHOD_FLAG_NO_CHECK); - check_malloc_return(my_rsa_method); - RSA_meth_set_pub_enc(my_rsa_method, rsa_pub_enc); - RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec); - RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc); - RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec); - RSA_meth_set_init(my_rsa_method, NULL); - RSA_meth_set_finish(my_rsa_method, finish); - RSA_meth_set0_app_data(my_rsa_method, cd); - - /* For CNG, set the RSA_sign method which gets priority over priv_enc(). - * This method is called with the raw hash without the digestinfo - * header and works better when using NCryptSignHash() with some tokens. - * However, if PSS padding is in use, openssl does not call this - * function but adds the padding and then calls rsa_priv_enc() - * with padding set to NONE which is not supported by CNG. - * So, when posisble (OpenSSL 1.1.0 and up), we hook on to the sign - * operation in EVP_PKEY_METHOD struct. - */ - if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) - { -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - RSA_meth_set_sign(my_rsa_method, rsa_sign_CNG); -#else - /* pmethod is global -- initialize only if NULL */ - if (!pmethod) - { - pmethod = EVP_PKEY_meth_new(EVP_PKEY_RSA, 0); - if (!pmethod) - { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); - goto err; - } - const EVP_PKEY_METHOD *default_pmethod = EVP_PKEY_meth_find(EVP_PKEY_RSA); - EVP_PKEY_meth_copy(pmethod, default_pmethod); - - /* We want to override only sign_init() and sign() */ - EVP_PKEY_meth_set_sign(pmethod, pkey_rsa_sign_init, pkey_rsa_sign); - EVP_PKEY_meth_add0(pmethod); - - /* Keep a copy of the default sign and sign_init methods */ - -#if (OPENSSL_VERSION_NUMBER < 0x1010009fL) /* < version 1.1.0i */ - /* The function signature is not const-correct in these versions */ - EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD *)default_pmethod, &default_pkey_sign_init, - &default_pkey_sign); -#else - EVP_PKEY_meth_get_sign(default_pmethod, &default_pkey_sign_init, - &default_pkey_sign); -#endif - } -#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */ - } - - rsa = RSA_new(); - if (rsa == NULL) - { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); - goto err; - } - /* Public key in cert is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ if (!SSL_CTX_use_certificate(ssl_ctx, cert)) { goto err; } + /* the public key */ EVP_PKEY *pkey = X509_get0_pubkey(cert); @@ -956,70 +1225,32 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) X509_free(cert); cert = NULL; - if (!(pub_rsa = EVP_PKEY_get0_RSA(pkey))) + if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { - msg(M_WARN, "cryptoapicert requires an RSA certificate"); - goto err; - } - - /* Our private key is external, so we fill in only n and e from the public key */ - const BIGNUM *n = NULL; - const BIGNUM *e = NULL; - RSA_get0_key(pub_rsa, &n, &e, NULL); - if (!RSA_set0_key(rsa, BN_dup(n), BN_dup(e), NULL)) - { - goto err; + if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey)) + { + goto err; + } } - RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); - if (!RSA_set_method(rsa, my_rsa_method)) +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC) + else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { - goto err; + if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey)) + { + goto err; + } } - - if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa)) +#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */ + else { + msg(M_WARN, "WARNING: cryptoapicert: certificate type not supported"); goto err; } - /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so - * we decrease it here with RSA_free(), or it will never be cleaned up. */ - RSA_free(rsa); + CAPI_DATA_free(cd); /* this will do a ref_count-- */ return 1; err: - if (cert) - { - X509_free(cert); - } - if (rsa) - { - RSA_free(rsa); - } - else - { - if (my_rsa_method) - { - free(my_rsa_method); - } - if (cd) - { - if (cd->free_crypt_prov && cd->crypt_prov) - { - if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) - { - NCryptFreeObject(cd->crypt_prov); - } - else - { - CryptReleaseContext(cd->crypt_prov, 0); - } - } - if (cd->cert_context) - { - CertFreeCertificateContext(cd->cert_context); - } - free(cd); - } - } + CAPI_DATA_free(cd); return 0; } diff --git a/src/openvpn/dhcp.c b/src/openvpn/dhcp.c index fb28b27..c19370e 100644 --- a/src/openvpn/dhcp.c +++ b/src/openvpn/dhcp.c @@ -147,49 +147,6 @@ do_extract(struct dhcp *dhcp, int optlen) return ret; } -static uint16_t -udp_checksum(const uint8_t *buf, - const int len_udp, - const uint8_t *src_addr, - const uint8_t *dest_addr) -{ - uint16_t word16; - uint32_t sum = 0; - int i; - - /* make 16 bit words out of every two adjacent 8 bit words and */ - /* calculate the sum of all 16 bit words */ - for (i = 0; i < len_udp; i += 2) - { - word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0); - sum += word16; - } - - /* add the UDP pseudo header which contains the IP source and destination addresses */ - for (i = 0; i < 4; i += 2) - { - word16 = ((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF); - sum += word16; - } - for (i = 0; i < 4; i += 2) - { - word16 = ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF); - sum += word16; - } - - /* the protocol number and the length of the UDP packet */ - sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp; - - /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */ - while (sum >> 16) - { - sum = (sum & 0xFFFF) + (sum >> 16); - } - - /* Take the one's complement of sum */ - return ((uint16_t) ~sum); -} - in_addr_t dhcp_extract_router_msg(struct buffer *ipbuf) { @@ -210,10 +167,10 @@ dhcp_extract_router_msg(struct buffer *ipbuf) /* recompute the UDP checksum */ df->udp.check = 0; - df->udp.check = htons(udp_checksum((uint8_t *) &df->udp, - sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen, - (uint8_t *)&df->ip.saddr, - (uint8_t *)&df->ip.daddr)); + df->udp.check = htons(ip_checksum(AF_INET, (uint8_t *)&df->udp, + sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen, + (uint8_t *)&df->ip.saddr, (uint8_t *)&df->ip.daddr, + OPENVPN_IPPROTO_UDP)); /* only return the extracted Router address if DHCPACK */ if (message_type == DHCPACK) diff --git a/src/openvpn/env_set.c b/src/openvpn/env_set.c new file mode 100644 index 0000000..0ab0262 --- /dev/null +++ b/src/openvpn/env_set.c @@ -0,0 +1,459 @@ +/* + * 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-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com> + * Copyright (C) 2016-2017 David Sommerseth <davids@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "env_set.h" + +#include "run_command.h" + +/* + * Set environmental variable (int or string). + * + * On Posix, we use putenv for portability, + * and put up with its painful semantics + * that require all the support code below. + */ + +/* General-purpose environmental variable set functions */ + +static char * +construct_name_value(const char *name, const char *value, struct gc_arena *gc) +{ + struct buffer out; + + ASSERT(name); + if (!value) + { + value = ""; + } + out = alloc_buf_gc(strlen(name) + strlen(value) + 2, gc); + buf_printf(&out, "%s=%s", name, value); + return BSTR(&out); +} + +static bool +env_string_equal(const char *s1, const char *s2) +{ + int c1, c2; + ASSERT(s1); + ASSERT(s2); + + while (true) + { + c1 = *s1++; + c2 = *s2++; + if (c1 == '=') + { + c1 = 0; + } + if (c2 == '=') + { + c2 = 0; + } + if (!c1 && !c2) + { + return true; + } + if (c1 != c2) + { + break; + } + } + return false; +} + +static bool +remove_env_item(const char *str, const bool do_free, struct env_item **list) +{ + struct env_item *current, *prev; + + ASSERT(str); + ASSERT(list); + + for (current = *list, prev = NULL; current != NULL; current = current->next) + { + if (env_string_equal(current->string, str)) + { + if (prev) + { + prev->next = current->next; + } + else + { + *list = current->next; + } + if (do_free) + { + secure_memzero(current->string, strlen(current->string)); + free(current->string); + free(current); + } + return true; + } + prev = current; + } + return false; +} + +static void +add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc) +{ + struct env_item *item; + + ASSERT(str); + ASSERT(list); + + ALLOC_OBJ_GC(item, struct env_item, gc); + item->string = do_alloc ? string_alloc(str, gc) : str; + item->next = *list; + *list = item; +} + +/* struct env_set functions */ + +static bool +env_set_del_nolock(struct env_set *es, const char *str) +{ + return remove_env_item(str, es->gc == NULL, &es->list); +} + +static void +env_set_add_nolock(struct env_set *es, const char *str) +{ + remove_env_item(str, es->gc == NULL, &es->list); + add_env_item((char *)str, true, &es->list, es->gc); +} + +struct env_set * +env_set_create(struct gc_arena *gc) +{ + struct env_set *es; + ALLOC_OBJ_CLEAR_GC(es, struct env_set, gc); + es->list = NULL; + es->gc = gc; + return es; +} + +void +env_set_destroy(struct env_set *es) +{ + if (es && es->gc == NULL) + { + struct env_item *e = es->list; + while (e) + { + struct env_item *next = e->next; + free(e->string); + free(e); + e = next; + } + free(es); + } +} + +bool +env_set_del(struct env_set *es, const char *str) +{ + bool ret; + ASSERT(es); + ASSERT(str); + ret = env_set_del_nolock(es, str); + return ret; +} + +void +env_set_add(struct env_set *es, const char *str) +{ + ASSERT(es); + ASSERT(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) +{ + if (check_debug_level(msglevel)) + { + const struct env_item *e; + int i; + + if (es) + { + e = es->list; + i = 0; + + while (e) + { + if (env_safe_to_print(e->string)) + { + msg(msglevel, "ENV [%d] '%s'", i, e->string); + } + ++i; + e = e->next; + } + } + } +} + +void +env_set_inherit(struct env_set *es, const struct env_set *src) +{ + const struct env_item *e; + + ASSERT(es); + + if (src) + { + e = src->list; + while (e) + { + env_set_add_nolock(es, e->string); + e = e->next; + } + } +} + + +/* add/modify/delete environmental strings */ + +void +setenv_counter(struct env_set *es, const char *name, counter_type value) +{ + char buf[64]; + openvpn_snprintf(buf, sizeof(buf), counter_format, value); + setenv_str(es, name, buf); +} + +void +setenv_int(struct env_set *es, const char *name, int value) +{ + char buf[64]; + openvpn_snprintf(buf, sizeof(buf), "%d", value); + setenv_str(es, name, buf); +} + +void +setenv_long_long(struct env_set *es, const char *name, long long value) +{ + char buf[64]; + openvpn_snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)value); + setenv_str(es, name, buf); +} + +void +setenv_str(struct env_set *es, const char *name, const char *value) +{ + setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0); +} + +void +setenv_str_safe(struct env_set *es, const char *name, const char *value) +{ + uint8_t b[64]; + struct buffer buf; + buf_set_write(&buf, b, sizeof(b)); + if (buf_printf(&buf, "OPENVPN_%s", name)) + { + setenv_str(es, BSTR(&buf), value); + } + else + { + 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) +{ + ASSERT(name); + setenv_str(es, name, NULL); +} + +void +setenv_str_ex(struct env_set *es, + const char *name, + const char *value, + const unsigned int name_include, + const unsigned int name_exclude, + const char name_replace, + const unsigned int value_include, + const unsigned int value_exclude, + const char value_replace) +{ + struct gc_arena gc = gc_new(); + const char *name_tmp; + const char *val_tmp = NULL; + + ASSERT(name && strlen(name) > 1); + + name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc); + + if (value) + { + val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc); + } + + ASSERT(es); + + if (val_tmp) + { + const char *str = construct_name_value(name_tmp, val_tmp, &gc); + env_set_add(es, str); +#if DEBUG_VERBOSE_SETENV + msg(M_INFO, "SETENV_ES '%s'", str); +#endif + } + else + { + env_set_del(es, name_tmp); + } + + gc_free(&gc); +} + +/* + * Setenv functions that append an integer index to the name + */ +static const char * +setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(strlen(name) + 16, gc); + if (i >= 0) + { + buf_printf(&out, "%s_%d", name, i); + } + else + { + buf_printf(&out, "%s", name); + } + return BSTR(&out); +} + +void +setenv_int_i(struct env_set *es, const char *name, const int value, const int i) +{ + struct gc_arena gc = gc_new(); + const char *name_str = setenv_format_indexed_name(name, i, &gc); + setenv_int(es, name_str, value); + gc_free(&gc); +} + +void +setenv_str_i(struct env_set *es, const char *name, const char *value, const int i) +{ + struct gc_arena gc = gc_new(); + const char *name_str = setenv_format_indexed_name(name, i, &gc); + setenv_str(es, name_str, value); + gc_free(&gc); +} + +bool +env_allowed(const char *str) +{ + return (script_security() >= SSEC_PW_ENV || !is_password_env_var(str)); +} + +/* Make arrays of strings */ + +const char ** +make_env_array(const struct env_set *es, + const bool check_allowed, + struct gc_arena *gc) +{ + char **ret = NULL; + struct env_item *e = NULL; + int i = 0, n = 0; + + /* figure length of es */ + if (es) + { + for (e = es->list; e != NULL; e = e->next) + { + ++n; + } + } + + /* alloc return array */ + ALLOC_ARRAY_CLEAR_GC(ret, char *, n+1, gc); + + /* fill return array */ + if (es) + { + i = 0; + for (e = es->list; e != NULL; e = e->next) + { + if (!check_allowed || env_allowed(e->string)) + { + ASSERT(i < n); + ret[i++] = e->string; + } + } + } + + ret[i] = NULL; + return (const char **)ret; +} diff --git a/src/openvpn/env_set.h b/src/openvpn/env_set.h new file mode 100644 index 0000000..cf8415c --- /dev/null +++ b/src/openvpn/env_set.h @@ -0,0 +1,123 @@ +/* + * 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-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ENV_SET_H +#define ENV_SET_H + +#include "argv.h" +#include "basic.h" +#include "buffer.h" +#include "common.h" + +/* + * Handle environmental variable lists + */ + +struct env_item { + char *string; + struct env_item *next; +}; + +struct env_set { + struct gc_arena *gc; + struct env_item *list; +}; + +/* set/delete environmental variable */ +void setenv_str_ex(struct env_set *es, + const char *name, + const char *value, + const unsigned int name_include, + const unsigned int name_exclude, + const char name_replace, + const unsigned int value_include, + const unsigned int value_exclude, + const char value_replace); + +void setenv_counter(struct env_set *es, const char *name, counter_type value); + +void setenv_int(struct env_set *es, const char *name, int value); + +void setenv_long_long(struct env_set *es, const char *name, long long value); + +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); + +/* struct env_set functions */ + +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); + +void env_set_inherit(struct env_set *es, const struct env_set *src); + +/* returns true if environmental variable name starts with 'password' */ +static inline bool +is_password_env_var(const char *str) +{ + return (strncmp(str, "password", 8) == 0); +} + +/* returns true if environmental variable safe to print to log */ +static inline bool +env_safe_to_print(const char *str) +{ +#ifndef UNSAFE_DEBUG + if (is_password_env_var(str)) + { + return false; + } +#endif + return true; +} + +/* returns true if environmental variable may be passed to an external program */ +bool env_allowed(const char *str); + +const char **make_env_array(const struct env_set *es, + const bool check_allowed, + struct gc_arena *gc); + +#endif /* ifndef ENV_SET_H */ diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h index 5ca4fa8..e448fc3 100644 --- a/src/openvpn/errlevel.h +++ b/src/openvpn/errlevel.h @@ -109,6 +109,7 @@ #define D_LOG_RW LOGLEV(5, 0, 0) /* Print 'R' or 'W' to stdout for read/write */ +#define D_RTNL LOGLEV(6, 68, M_DEBUG) /* show RTNL low level operations */ #define D_LINK_RW LOGLEV(6, 69, M_DEBUG) /* show TCP/UDP reads/writes (terse) */ #define D_TUN_RW LOGLEV(6, 69, M_DEBUG) /* show TUN/TAP reads/writes */ #define D_TAP_WIN_DEBUG LOGLEV(6, 69, M_DEBUG) /* show TAP-Windows driver debug info */ @@ -139,7 +140,6 @@ #define D_PACKET_TRUNC_DEBUG LOGLEV(7, 70, M_DEBUG) /* PACKET_TRUNCATION_CHECK verbose */ #define D_PING LOGLEV(7, 70, M_DEBUG) /* PING send/receive messages */ #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_parse_cmd */ #define D_CRYPTO_DEBUG LOGLEV(7, 70, M_DEBUG) /* show detailed info from crypto.c routines */ @@ -148,6 +148,8 @@ #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_VLAN_DEBUG LOGLEV(7, 74, M_DEBUG) /* show VLAN tagging/untagging debug 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 */ #define D_INTERVAL LOGLEV(8, 70, M_DEBUG) /* show interval.h debugging info */ diff --git a/src/openvpn/error.c b/src/openvpn/error.c index bc14e8c..d6247fe 100644 --- a/src/openvpn/error.c +++ b/src/openvpn/error.c @@ -31,6 +31,7 @@ #include "error.h" #include "buffer.h" +#include "init.h" #include "misc.h" #include "win32.h" #include "socket.h" @@ -342,9 +343,9 @@ x_msg_va(const unsigned int flags, const char *format, va_list arglist) struct timeval tv; gettimeofday(&tv, NULL); - fprintf(fp, "%"PRIi64".%06lu %x %s%s%s%s", + fprintf(fp, "%" PRIi64 ".%06ld %x %s%s%s%s", (int64_t)tv.tv_sec, - (unsigned long)tv.tv_usec, + (long)tv.tv_usec, flags, prefix, prefix_sep, @@ -687,7 +688,10 @@ x_check_status(int status, } #elif defined(_WIN32) /* get possible driver error from TAP-Windows driver */ - extended_msg = tap_win_getinfo(tt, &gc); + if (tuntap_defined(tt)) + { + extended_msg = tap_win_getinfo(tt, &gc); + } #endif if (!ignore_sys_error(my_errno)) { @@ -734,18 +738,12 @@ openvpn_exit(const int status) { if (!forked) { - void tun_abort(); - -#ifdef ENABLE_PLUGIN - void plugin_abort(void); - -#endif - tun_abort(); #ifdef _WIN32 uninit_win32(); #endif + remove_pid_file(); close_syslog(); diff --git a/src/openvpn/event.c b/src/openvpn/event.c index b22741f..49dfa86 100644 --- a/src/openvpn/event.c +++ b/src/openvpn/event.c @@ -1041,10 +1041,10 @@ se_wait_fast(struct event_set *es, const struct timeval *tv, struct event_set_re struct timeval tv_tmp = *tv; int stat; - dmsg(D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%d/%d", + dmsg(D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%" PRIi64 "/%ld", ses->maxfd, - (int)tv_tmp.tv_sec, - (int)tv_tmp.tv_usec); + (int64_t)tv_tmp.tv_sec, + (long)tv_tmp.tv_usec); stat = select(ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp); @@ -1065,8 +1065,8 @@ se_wait_scalable(struct event_set *es, const struct timeval *tv, struct event_se fd_set write = ses->writefds; int stat; - dmsg(D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%d/%d", - ses->maxfd, (int)tv_tmp.tv_sec, (int)tv_tmp.tv_usec); + dmsg(D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%" PRIi64 "/%ld", + ses->maxfd, (int64_t)tv_tmp.tv_sec, (long)tv_tmp.tv_usec); stat = select(ses->maxfd + 1, &read, &write, NULL, &tv_tmp); diff --git a/src/openvpn/forward-inline.h b/src/openvpn/forward-inline.h deleted file mode 100644 index 7d06b4e..0000000 --- a/src/openvpn/forward-inline.h +++ /dev/null @@ -1,341 +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-2018 OpenVPN Inc <sales@openvpn.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FORWARD_INLINE_H -#define FORWARD_INLINE_H - -/* - * Inline functions - */ - -/* - * Does TLS session need service? - */ -static inline void -check_tls(struct context *c) -{ -#if defined(ENABLE_CRYPTO) - void check_tls_dowork(struct context *c); - - if (c->c2.tls_multi) - { - check_tls_dowork(c); - } -#endif -} - -/* - * TLS errors are fatal in TCP mode. - * Also check for --tls-exit trigger. - */ -static inline void -check_tls_errors(struct context *c) -{ -#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) - { - if (link_socket_connection_oriented(c->c2.link_socket)) - { - if (c->c2.tls_multi->n_soft_errors) - { - check_tls_errors_co(c); - } - } - else - { - if (c->c2.tls_multi->n_hard_errors) - { - check_tls_errors_nco(c); - } - } - } -#endif /* if defined(ENABLE_CRYPTO) */ -} - -/* - * Check for possible incoming configuration - * messages on the control channel. - */ -static inline void -check_incoming_control_channel(struct context *c) -{ -#if P2MP - void check_incoming_control_channel_dowork(struct context *c); - - if (tls_test_payload_len(c->c2.tls_multi) > 0) - { - check_incoming_control_channel_dowork(c); - } -#endif -} - -/* - * Options like --up-delay need to be triggered by this function which - * checks for connection establishment. - */ -static inline void -check_connection_established(struct context *c) -{ - void check_connection_established_dowork(struct context *c); - - if (event_timeout_defined(&c->c2.wait_for_connect)) - { - check_connection_established_dowork(c); - } -} - -/* - * Should we add routes? - */ -static inline void -check_add_routes(struct context *c) -{ - void check_add_routes_dowork(struct context *c); - - if (event_timeout_trigger(&c->c2.route_wakeup, &c->c2.timeval, ETT_DEFAULT)) - { - check_add_routes_dowork(c); - } -} - -/* - * Should we exit due to inactivity timeout? - */ -static inline void -check_inactivity_timeout(struct context *c) -{ - void check_inactivity_timeout_dowork(struct context *c); - - if (c->options.inactivity_timeout - && event_timeout_trigger(&c->c2.inactivity_interval, &c->c2.timeval, ETT_DEFAULT)) - { - check_inactivity_timeout_dowork(c); - } -} - -#if P2MP - -static inline void -check_server_poll_timeout(struct context *c) -{ - void check_server_poll_timeout_dowork(struct context *c); - - 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); - } -} - -/* - * Scheduled exit? - */ -static inline void -check_scheduled_exit(struct context *c) -{ - void check_scheduled_exit_dowork(struct context *c); - - if (event_timeout_defined(&c->c2.scheduled_exit)) - { - if (event_timeout_trigger(&c->c2.scheduled_exit, &c->c2.timeval, ETT_DEFAULT)) - { - check_scheduled_exit_dowork(c); - } - } -} -#endif /* if P2MP */ - -/* - * Should we write timer-triggered status file. - */ -static inline void -check_status_file(struct context *c) -{ - void check_status_file_dowork(struct context *c); - - if (c->c1.status_output) - { - if (status_trigger_tv(c->c1.status_output, &c->c2.timeval)) - { - check_status_file_dowork(c); - } - } -} - -#ifdef ENABLE_FRAGMENT -/* - * Should we deliver a datagram fragment to remote? - */ -static inline void -check_fragment(struct context *c) -{ - void check_fragment_dowork(struct context *c); - - if (c->c2.fragment) - { - check_fragment_dowork(c); - } -} -#endif - -#if P2MP - -/* - * see if we should send a push_request in response to --pull - */ -static inline void -check_push_request(struct context *c) -{ - void check_push_request_dowork(struct context *c); - - if (event_timeout_trigger(&c->c2.push_request_interval, &c->c2.timeval, ETT_DEFAULT)) - { - check_push_request_dowork(c); - } -} - -#endif - -#ifdef ENABLE_CRYPTO -/* - * Should we persist our anti-replay packet ID state to disk? - */ -static inline void -check_packet_id_persist_flush(struct context *c) -{ - if (packet_id_persist_enabled(&c->c1.pid_persist) - && event_timeout_trigger(&c->c2.packet_id_persist_interval, &c->c2.timeval, ETT_DEFAULT)) - { - packet_id_persist_save(&c->c1.pid_persist); - } -} -#endif - -/* - * Set our wakeup to 0 seconds, so we will be rescheduled - * immediately. - */ -static inline void -context_immediate_reschedule(struct context *c) -{ - c->c2.timeval.tv_sec = 0; /* ZERO-TIMEOUT */ - c->c2.timeval.tv_usec = 0; -} - -static inline void -context_reschedule_sec(struct context *c, int sec) -{ - if (sec < 0) - { - sec = 0; - } - if (sec < c->c2.timeval.tv_sec) - { - c->c2.timeval.tv_sec = sec; - c->c2.timeval.tv_usec = 0; - } -} - -static inline struct link_socket_info * -get_link_socket_info(struct context *c) -{ - if (c->c2.link_socket_info) - { - return c->c2.link_socket_info; - } - else - { - return &c->c2.link_socket->info; - } -} - -static inline void -register_activity(struct context *c, const int size) -{ - if (c->options.inactivity_timeout) - { - c->c2.inactivity_bytes += size; - if (c->c2.inactivity_bytes >= c->options.inactivity_minimum_bytes) - { - c->c2.inactivity_bytes = 0; - event_timeout_reset(&c->c2.inactivity_interval); - } - } -} - -/* - * Return the io_wait() flags appropriate for - * a point-to-point tunnel. - */ -static inline unsigned int -p2p_iow_flags(const struct context *c) -{ - unsigned int flags = (IOW_SHAPER|IOW_CHECK_RESIDUAL|IOW_FRAG|IOW_READ|IOW_WAIT_SIGNAL); - if (c->c2.to_link.len > 0) - { - flags |= IOW_TO_LINK; - } - if (c->c2.to_tun.len > 0) - { - flags |= IOW_TO_TUN; - } - return flags; -} - -/* - * This is the core I/O wait function, used for all I/O waits except - * for TCP in server mode. - */ -static inline void -io_wait(struct context *c, const unsigned int flags) -{ - void io_wait_dowork(struct context *c, const unsigned int flags); - - if (c->c2.fast_io && (flags & (IOW_TO_TUN|IOW_TO_LINK|IOW_MBUF))) - { - /* fast path -- only for TUN/TAP/UDP writes */ - unsigned int ret = 0; - if (flags & IOW_TO_TUN) - { - ret |= TUN_WRITE; - } - if (flags & (IOW_TO_LINK|IOW_MBUF)) - { - ret |= SOCKET_WRITE; - } - c->c2.event_set_status = ret; - } - else - { - /* slow path */ - io_wait_dowork(c, flags); - } -} - -#define CONNECTION_ESTABLISHED(c) (get_link_socket_info(c)->connection_established) - -#endif /* EVENT_INLINE_H */ diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index 84bb584..7ed8d0d 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -35,6 +35,9 @@ #include "gremlin.h" #include "mss.h" #include "event.h" +#include "occ.h" +#include "pf.h" +#include "ping.h" #include "ps.h" #include "dhcp.h" #include "common.h" @@ -42,9 +45,6 @@ #include "memdbg.h" -#include "forward-inline.h" -#include "occ-inline.h" -#include "ping-inline.h" #include "mstats.h" counter_type link_read_bytes_global; /* GLOBAL */ @@ -78,6 +78,57 @@ show_wait_status(struct context *c) #endif /* ifdef ENABLE_DEBUG */ /* + * TLS errors are fatal in TCP mode. + * Also check for --tls-exit trigger. + */ +static inline void +check_tls_errors(struct context *c) +{ + if (c->c2.tls_multi && c->c2.tls_exit_signal) + { + if (link_socket_connection_oriented(c->c2.link_socket)) + { + if (c->c2.tls_multi->n_soft_errors) + { + check_tls_errors_co(c); + } + } + else + { + if (c->c2.tls_multi->n_hard_errors) + { + check_tls_errors_nco(c); + } + } + } +} + +/* + * Set our wakeup to 0 seconds, so we will be rescheduled + * immediately. + */ +static inline void +context_immediate_reschedule(struct context *c) +{ + c->c2.timeval.tv_sec = 0; /* ZERO-TIMEOUT */ + c->c2.timeval.tv_usec = 0; +} + +static inline void +context_reschedule_sec(struct context *c, int sec) +{ + if (sec < 0) + { + sec = 0; + } + if (sec < c->c2.timeval.tv_sec) + { + c->c2.timeval.tv_sec = sec; + c->c2.timeval.tv_usec = 0; + } +} + +/* * In TLS mode, let TLS level respond to any control-channel * packets which were received, or prepare any packets for * transmission. @@ -87,9 +138,8 @@ show_wait_status(struct context *c) * traffic on the control-channel. * */ -#ifdef ENABLE_CRYPTO void -check_tls_dowork(struct context *c) +check_tls(struct context *c) { interval_t wakeup = BIG_TIMEOUT; @@ -131,7 +181,6 @@ check_tls_errors_nco(struct context *c) { register_signal(c, c->c2.tls_exit_signal, "tls-error"); /* SOFT-SIGUSR1 -- TLS error */ } -#endif /* ENABLE_CRYPTO */ #if P2MP @@ -140,56 +189,68 @@ check_tls_errors_nco(struct context *c) * messages on the control channel. */ void -check_incoming_control_channel_dowork(struct context *c) +check_incoming_control_channel(struct context *c) { - const int len = tls_test_payload_len(c->c2.tls_multi); - if (len) + int len = tls_test_payload_len(c->c2.tls_multi); + /* We should only be called with len >0 */ + ASSERT(len > 0); + + struct gc_arena gc = gc_new(); + struct buffer buf = alloc_buf_gc(len, &gc); + if (tls_rec_payload(c->c2.tls_multi, &buf)) { - struct gc_arena gc = gc_new(); - struct buffer buf = alloc_buf_gc(len, &gc); - if (tls_rec_payload(c->c2.tls_multi, &buf)) - { - /* force null termination of message */ - buf_null_terminate(&buf); + /* force null termination of message */ + buf_null_terminate(&buf); - /* enforce character class restrictions */ - string_mod(BSTR(&buf), CC_PRINT, CC_CRLF, 0); + /* enforce character class restrictions */ + string_mod(BSTR(&buf), CC_PRINT, CC_CRLF, 0); - if (buf_string_match_head_str(&buf, "AUTH_FAILED")) - { - receive_auth_failed(c, &buf); - } - else if (buf_string_match_head_str(&buf, "PUSH_")) - { - incoming_push_message(c, &buf); - } - else if (buf_string_match_head_str(&buf, "RESTART")) - { - server_pushed_signal(c, &buf, true, 7); - } - else if (buf_string_match_head_str(&buf, "HALT")) - { - server_pushed_signal(c, &buf, false, 4); - } - else - { - msg(D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR(&buf)); - } + if (buf_string_match_head_str(&buf, "AUTH_FAILED")) + { + receive_auth_failed(c, &buf); + } + else if (buf_string_match_head_str(&buf, "PUSH_")) + { + incoming_push_message(c, &buf); + } + else if (buf_string_match_head_str(&buf, "RESTART")) + { + server_pushed_signal(c, &buf, true, 7); + } + else if (buf_string_match_head_str(&buf, "HALT")) + { + server_pushed_signal(c, &buf, false, 4); + } + else if (buf_string_match_head_str(&buf, "INFO_PRE")) + { + server_pushed_info(c, &buf, 8); + } + else if (buf_string_match_head_str(&buf, "INFO")) + { + server_pushed_info(c, &buf, 4); + } + else if (buf_string_match_head_str(&buf, "CR_RESPONSE")) + { + receive_cr_response(c, &buf); } else { - msg(D_PUSH_ERRORS, "WARNING: Receive control message failed"); + msg(D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR(&buf)); } - - gc_free(&gc); } + else + { + msg(D_PUSH_ERRORS, "WARNING: Receive control message failed"); + } + + gc_free(&gc); } /* * Periodically resend PUSH_REQUEST until PUSH message received */ void -check_push_request_dowork(struct context *c) +check_push_request(struct context *c) { send_push_request(c); @@ -201,83 +262,89 @@ check_push_request_dowork(struct context *c) /* * Things that need to happen immediately after connection initiation should go here. + * + * Options like --up-delay need to be triggered by this function which + * checks for connection establishment. + * + * Note: The process_incoming_push_reply currently assumes that this function + * only sets up the pull request timer when pull is enabled. */ void -check_connection_established_dowork(struct context *c) +check_connection_established(struct context *c) { - if (event_timeout_trigger(&c->c2.wait_for_connect, &c->c2.timeval, ETT_DEFAULT)) + + if (CONNECTION_ESTABLISHED(c)) { - if (CONNECTION_ESTABLISHED(c)) - { #if P2MP - /* if --pull was specified, send a push request to server */ - if (c->c2.tls_multi && c->options.pull) - { + /* if --pull was specified, send a push request to server */ + if (c->c2.tls_multi && c->options.pull) + { #ifdef ENABLE_MANAGEMENT - if (management) - { - management_set_state(management, - OPENVPN_STATE_GET_CONFIG, - NULL, - NULL, - NULL, - NULL, - NULL); - } -#endif - /* fire up push request right away (already 1s delayed) */ - event_timeout_init(&c->c2.push_request_interval, 0, now); - reset_coarse_timers(c); - } - else -#endif /* if P2MP */ + if (management) { - do_up(c, false, 0); + management_set_state(management, + OPENVPN_STATE_GET_CONFIG, + NULL, + NULL, + NULL, + NULL, + NULL); } - - event_timeout_clear(&c->c2.wait_for_connect); +#endif + /* fire up push request right away (already 1s delayed) */ + event_timeout_init(&c->c2.push_request_interval, 0, now); + reset_coarse_timers(c); + } + else +#endif /* if P2MP */ + { + do_up(c, false, 0); } + + event_timeout_clear(&c->c2.wait_for_connect); } + +} + +bool +send_control_channel_string_dowork(struct tls_multi *multi, + const char *str, int msglevel) +{ + struct gc_arena gc = gc_new(); + bool stat; + + /* buffered cleartext write onto TLS control channel */ + stat = tls_send_payload(multi, (uint8_t *) str, strlen(str) + 1); + + msg(msglevel, "SENT CONTROL [%s]: '%s' (status=%d)", + tls_common_name(multi, false), + sanitize_control_message(str, &gc), + (int) stat); + + gc_free(&gc); + return stat; } -/* - * Send a string to remote over the TLS control channel. - * Used for push/pull messages, passing username/password, - * etc. - */ bool send_control_channel_string(struct context *c, const char *str, int msglevel) { -#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { - struct gc_arena gc = gc_new(); - bool stat; - - /* buffered cleartext write onto TLS control channel */ - stat = tls_send_payload(c->c2.tls_multi, (uint8_t *) str, strlen(str) + 1); - + bool ret = send_control_channel_string_dowork(c->c2.tls_multi, + str, msglevel); /* * Reschedule tls_multi_process. * NOTE: in multi-client mode, usually the below two statements are * insufficient to reschedule the client instance object unless * multi_schedule_context_wakeup(m, mi) is also called. */ + interval_action(&c->c2.tmp_int); context_immediate_reschedule(c); /* ZERO-TIMEOUT */ - - msg(msglevel, "SENT CONTROL [%s]: '%s' (status=%d)", - tls_common_name(c->c2.tls_multi, false), - sanitize_control_message(str, &gc), - (int) stat); - - gc_free(&gc); - return stat; + return ret; } -#endif /* ENABLE_CRYPTO */ return true; } - /* * Add routes. */ @@ -286,7 +353,7 @@ static void check_add_routes_action(struct context *c, const bool errors) { do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list, - c->c1.tuntap, c->plugins, c->c2.es); + c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx); update_time(); event_timeout_clear(&c->c2.route_wakeup); event_timeout_clear(&c->c2.route_wakeup_expire); @@ -294,7 +361,7 @@ check_add_routes_action(struct context *c, const bool errors) } void -check_add_routes_dowork(struct context *c) +check_add_routes(struct context *c) { if (test_routes(c->c1.route_list, c->c1.tuntap)) { @@ -332,7 +399,7 @@ check_add_routes_dowork(struct context *c) * Should we exit due to inactivity timeout? */ void -check_inactivity_timeout_dowork(struct context *c) +check_inactivity_timeout(struct context *c) { msg(M_INFO, "Inactivity timeout (--inactive), exiting"); register_signal(c, SIGTERM, "inactive"); @@ -348,7 +415,7 @@ get_server_poll_remaining_time(struct event_timeout *server_poll_timeout) #if P2MP void -check_server_poll_timeout_dowork(struct context *c) +check_server_poll_timeout(struct context *c) { event_timeout_reset(&c->c2.server_poll_interval); ASSERT(c->c2.tls_multi); @@ -378,7 +445,7 @@ schedule_exit(struct context *c, const int n_seconds, const int signal) * Scheduled exit? */ void -check_scheduled_exit_dowork(struct context *c) +check_scheduled_exit(struct context *c) { register_signal(c, c->c2.scheduled_exit_signal, "delayed-exit"); } @@ -389,7 +456,7 @@ check_scheduled_exit_dowork(struct context *c) * Should we write timer-triggered status file. */ void -check_status_file_dowork(struct context *c) +check_status_file(struct context *c) { if (c->c1.status_output) { @@ -402,7 +469,7 @@ check_status_file_dowork(struct context *c) * Should we deliver a datagram fragment to remote? */ void -check_fragment_dowork(struct context *c) +check_fragment(struct context *c) { struct link_socket_info *lsi = get_link_socket_info(c); @@ -457,7 +524,6 @@ encrypt_sign(struct context *c, bool comp_frag) const uint8_t *orig_buf = c->c2.buf.data; struct crypto_options *co = NULL; -#if P2MP_SERVER /* * Drop non-TLS outgoing packet if client-connect script/plugin * has not yet succeeded. @@ -466,7 +532,6 @@ encrypt_sign(struct context *c, bool comp_frag) { c->c2.buf.len = 0; } -#endif if (comp_frag) { @@ -485,7 +550,6 @@ encrypt_sign(struct context *c, bool comp_frag) #endif } -#ifdef ENABLE_CRYPTO /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT(buf_init(&b->encrypt_buf, FRAME_HEADROOM(&c->c2.frame))); @@ -518,7 +582,6 @@ encrypt_sign(struct context *c, bool comp_frag) } tls_post_encrypt(c->c2.tls_multi, &c->c2.buf); } -#endif /* ifdef ENABLE_CRYPTO */ /* * Get the address we will be sending the packet to. @@ -536,32 +599,55 @@ encrypt_sign(struct context *c, bool comp_frag) static void process_coarse_timers(struct context *c) { -#ifdef ENABLE_CRYPTO /* flush current packet-id to file once per 60 - * seconds if --replay-persist was specified */ - check_packet_id_persist_flush(c); -#endif + * seconds if --replay-persist was specified */ + if (packet_id_persist_enabled(&c->c1.pid_persist) + && event_timeout_trigger(&c->c2.packet_id_persist_interval, &c->c2.timeval, ETT_DEFAULT)) + { + packet_id_persist_save(&c->c1.pid_persist); + } - /* should we update status file? */ - check_status_file(c); + /* Should we write timer-triggered status file */ + if (c->c1.status_output + && event_timeout_trigger(&c->c1.status_output->et, &c->c2.timeval, ETT_DEFAULT)) + { + check_status_file(c); + } /* process connection establishment items */ - check_connection_established(c); - + if (event_timeout_trigger(&c->c2.wait_for_connect, &c->c2.timeval, ETT_DEFAULT)) + { + check_connection_established(c); + } #if P2MP - /* see if we should send a push_request in response to --pull */ - check_push_request(c); + /* see if we should send a push_request (option --pull) */ + if (event_timeout_trigger(&c->c2.push_request_interval, &c->c2.timeval, ETT_DEFAULT)) + { + check_push_request(c); + } #endif #ifdef PLUGIN_PF - pf_check_reload(c); + if (c->c2.pf.enabled + && event_timeout_trigger(&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT)) + { + pf_check_reload(c); + } #endif /* process --route options */ - check_add_routes(c); + if (event_timeout_trigger(&c->c2.route_wakeup, &c->c2.timeval, ETT_DEFAULT)) + { + check_add_routes(c); + } /* possibly exit due to --inactive */ - check_inactivity_timeout(c); + if (c->options.inactivity_timeout + && event_timeout_trigger(&c->c2.inactivity_interval, &c->c2.timeval, ETT_DEFAULT)) + { + check_inactivity_timeout(c); + } + if (c->sig->signal_received) { return; @@ -577,13 +663,19 @@ process_coarse_timers(struct context *c) #if P2MP if (c->c2.tls_multi) { - check_server_poll_timeout(c); + if (c->options.ce.connect_timeout + && event_timeout_trigger(&c->c2.server_poll_interval, &c->c2.timeval, ETT_DEFAULT)) + { + check_server_poll_timeout(c); + } if (c->sig->signal_received) { return; } - - check_scheduled_exit(c); + if (event_timeout_trigger(&c->c2.scheduled_exit, &c->c2.timeval, ETT_DEFAULT)) + { + check_scheduled_exit(c); + } if (c->sig->signal_received) { return; @@ -591,7 +683,6 @@ process_coarse_timers(struct context *c) } #endif -#ifdef ENABLE_OCC /* Should we send an OCC_REQUEST message? */ check_send_occ_req(c); @@ -603,22 +694,27 @@ process_coarse_timers(struct context *c) { process_explicit_exit_notification_timer_wakeup(c); } -#endif /* Should we ping the remote? */ check_ping_send(c); } static void -check_coarse_timers_dowork(struct context *c) +check_coarse_timers(struct context *c) { + if (now < c->c2.coarse_timer_wakeup) + { + context_reschedule_sec(c, c->c2.coarse_timer_wakeup - now); + return; + } + const struct timeval save = c->c2.timeval; c->c2.timeval.tv_sec = BIG_TIMEOUT; c->c2.timeval.tv_usec = 0; process_coarse_timers(c); c->c2.coarse_timer_wakeup = now + c->c2.timeval.tv_sec; - dmsg(D_INTERVAL, "TIMER: coarse timer wakeup %d seconds", (int) c->c2.timeval.tv_sec); + dmsg(D_INTERVAL, "TIMER: coarse timer wakeup %" PRIi64 " seconds", (int64_t)c->c2.timeval.tv_sec); /* Is the coarse timeout NOT the earliest one? */ if (c->c2.timeval.tv_sec > save.tv_sec) @@ -627,20 +723,6 @@ check_coarse_timers_dowork(struct context *c) } } -static inline void -check_coarse_timers(struct context *c) -{ - const time_t local_now = now; - if (local_now >= c->c2.coarse_timer_wakeup) - { - check_coarse_timers_dowork(c); - } - else - { - context_reschedule_sec(c, c->c2.coarse_timer_wakeup - local_now); - } -} - static void check_timeout_random_component_dowork(struct context *c) { @@ -649,7 +731,7 @@ check_timeout_random_component_dowork(struct context *c) c->c2.timeout_random_component.tv_usec = (time_t) get_random() & 0x0003FFFF; c->c2.timeout_random_component.tv_sec = 0; - dmsg(D_INTERVAL, "RANDOM USEC=%d", (int) c->c2.timeout_random_component.tv_usec); + dmsg(D_INTERVAL, "RANDOM USEC=%ld", (long) c->c2.timeout_random_component.tv_usec); } static inline void @@ -752,14 +834,12 @@ read_incoming_link(struct context *c) } else { -#ifdef ENABLE_OCC if (event_timeout_defined(&c->c2.explicit_exit_notification_interval)) { msg(D_STREAM_ERRORS, "Connection reset during exit notification period, ignoring [%d]", status); management_sleep(1); } else -#endif { register_signal(c, SIGUSR1, "connection-reset"); /* SOFT-SIGUSR1 -- TCP connection reset */ msg(D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); @@ -852,7 +932,6 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo link_socket_bad_incoming_addr(&c->c2.buf, lsi, &c->c2.from); } -#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { /* @@ -870,7 +949,7 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo floated, &ad_start)) { /* Restore pre-NCP frame parameters */ - if (is_hard_reset(opcode, c->options.key_method)) + if (is_hard_reset_method2(opcode)) { c->c2.frame = c->c2.frame_initial; #ifdef ENABLE_FRAGMENT @@ -891,16 +970,15 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo { co = &c->c2.crypto_options; } -#if P2MP_SERVER + /* - * Drop non-TLS packet if client-connect script/plugin has not - * yet succeeded. + * Drop non-TLS packet if client-connect script/plugin and cipher selection + * has not yet succeeded. */ if (c->c2.context_auth != CAS_SUCCEEDED) { c->c2.buf.len = 0; } -#endif /* authenticate and decrypt the incoming packet */ decrypt_status = openvpn_decrypt(&c->c2.buf, c->c2.buffers->decrypt_buf, @@ -912,9 +990,6 @@ process_incoming_link_part1(struct context *c, struct link_socket_info *lsi, boo register_signal(c, SIGUSR1, "decryption-error"); /* SOFT-SIGUSR1 -- decryption error in TCP mode */ msg(D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); } -#else /* ENABLE_CRYPTO */ - decrypt_status = true; -#endif /* ENABLE_CRYPTO */ } else { @@ -963,9 +1038,9 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, con * * Also, update the persisted version of our packet-id. */ - if (!TLS_MODE(c)) + if (!TLS_MODE(c) && c->c2.buf.len > 0) { - link_socket_set_outgoing_addr(&c->c2.buf, lsi, &c->c2.from, NULL, c->c2.es); + link_socket_set_outgoing_addr(lsi, &c->c2.from, NULL, c->c2.es); } /* reset packet received timer */ @@ -988,13 +1063,11 @@ process_incoming_link_part2(struct context *c, struct link_socket_info *lsi, con c->c2.buf.len = 0; /* drop packet */ } -#ifdef ENABLE_OCC /* Did we just receive an OCC packet? */ if (is_occ_msg(&c->c2.buf)) { process_received_occ_msg(c); } -#endif buffer_turnover(orig_buf, &c->c2.to_tun, &c->c2.buf, &c->c2.buffers->read_link_buf); @@ -1039,13 +1112,29 @@ read_incoming_tun(struct context *c) perf_push(PERF_READ_IN_TUN); c->c2.buf = c->c2.buffers->read_tun_buf; -#ifdef TUN_PASS_BUFFER - read_tun_buffered(c->c1.tuntap, &c->c2.buf); -#else + +#ifdef _WIN32 + if (c->c1.tuntap->windows_driver == WINDOWS_DRIVER_WINTUN) + { + read_wintun(c->c1.tuntap, &c->c2.buf); + if (c->c2.buf.len == -1) + { + register_signal(c, SIGHUP, "tun-abort"); + c->persist.restart_sleep_seconds = 1; + msg(M_INFO, "Wintun read error, restarting"); + perf_pop(); + return; + } + } + else + { + read_tun_buffered(c->c1.tuntap, &c->c2.buf); + } +#else /* ifdef _WIN32 */ ASSERT(buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame))); ASSERT(buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame))); c->c2.buf.len = read_tun(c->c1.tuntap, BPTR(&c->c2.buf), MAX_RW_SIZE_TUN(&c->c2.frame)); -#endif +#endif /* ifdef _WIN32 */ #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.buf), @@ -1201,7 +1290,9 @@ process_incoming_tun(struct context *c) * The --passtos and --mssfix options require * us to examine the IP header (IPv4 or IPv6). */ - process_ip_header(c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf); + unsigned int flags = PIPV4_PASSTOS | PIP_MSSFIX | PIPV4_CLIENT_NAT + | PIPV6_IMCP_NOHOST_CLIENT; + process_ip_header(c, flags, &c->c2.buf); #ifdef PACKET_TRUNCATION_CHECK /* if (c->c2.buf.len > 1) --c->c2.buf.len; */ @@ -1212,6 +1303,9 @@ process_incoming_tun(struct context *c) &c->c2.n_trunc_pre_encrypt); #endif + } + if (c->c2.buf.len > 0) + { encrypt_sign(c, true); } else @@ -1222,6 +1316,142 @@ process_incoming_tun(struct context *c) gc_free(&gc); } +/** + * Forges a IPv6 ICMP packet with a no route to host error code from the + * IPv6 packet in buf and sends it directly back to the client via the tun + * device when used on a client and via the link if used on the server. + * + * @param buf - The buf containing the packet for which the icmp6 + * unreachable should be constructed. + * + * @param client - determines whether to the send packet back via tun or link + */ +void +ipv6_send_icmp_unreachable(struct context *c, struct buffer *buf, bool client) +{ +#define MAX_ICMPV6LEN 1280 + struct openvpn_icmp6hdr icmp6out; + CLEAR(icmp6out); + + /* + * Get a buffer to the ip packet, is_ipv6 automatically forwards + * the buffer to the ip packet + */ + struct buffer inputipbuf = *buf; + + is_ipv6(TUNNEL_TYPE(c->c1.tuntap), &inputipbuf); + + if (BLEN(&inputipbuf) < (int)sizeof(struct openvpn_ipv6hdr)) + { + return; + } + + const struct openvpn_ipv6hdr *pip6 = (struct openvpn_ipv6hdr *)BPTR(&inputipbuf); + + /* Copy version, traffic class, flow label from input packet */ + struct openvpn_ipv6hdr pip6out = *pip6; + + pip6out.version_prio = pip6->version_prio; + pip6out.daddr = pip6->saddr; + + /* + * Use the IPv6 remote address if we have one, otherwise use a fake one + * using the remote address is preferred since it makes debugging and + * understanding where the ICMPv6 error originates easier + */ + if (c->options.ifconfig_ipv6_remote) + { + inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &pip6out.saddr); + } + else + { + inet_pton(AF_INET6, "fe80::7", &pip6out.saddr); + } + + pip6out.nexthdr = OPENVPN_IPPROTO_ICMPV6; + + /* + * The ICMPv6 unreachable code worked best in my (arne) tests with Windows, + * Linux and Android. Windows did not like the administratively prohibited + * return code (no fast fail) + */ + icmp6out.icmp6_type = OPENVPN_ICMP6_DESTINATION_UNREACHABLE; + icmp6out.icmp6_code = OPENVPN_ICMP6_DU_NOROUTE; + + int icmpheader_len = sizeof(struct openvpn_ipv6hdr) + + sizeof(struct openvpn_icmp6hdr); + int totalheader_len = icmpheader_len; + + if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP) + { + totalheader_len += sizeof(struct openvpn_ethhdr); + } + + /* + * Calculate size for payload, defined in the standard that the resulting + * frame should be <= 1280 and have as much as possible of the original + * packet + */ + int max_payload_size = min_int(MAX_ICMPV6LEN, + TUN_MTU_SIZE(&c->c2.frame) - icmpheader_len); + int payload_len = min_int(max_payload_size, BLEN(&inputipbuf)); + + pip6out.payload_len = htons(sizeof(struct openvpn_icmp6hdr) + payload_len); + + /* Construct the packet as outgoing packet back to the client */ + struct buffer *outbuf; + if (client) + { + c->c2.to_tun = c->c2.buffers->aux_buf; + outbuf = &(c->c2.to_tun); + } + else + { + c->c2.to_link = c->c2.buffers->aux_buf; + outbuf = &(c->c2.to_link); + } + ASSERT(buf_init(outbuf, totalheader_len)); + + /* Fill the end of the buffer with original packet */ + ASSERT(buf_safe(outbuf, payload_len)); + ASSERT(buf_copy_n(outbuf, &inputipbuf, payload_len)); + + /* ICMP Header, copy into buffer to allow checksum calculation */ + ASSERT(buf_write_prepend(outbuf, &icmp6out, sizeof(struct openvpn_icmp6hdr))); + + /* Calculate checksum over the packet and write to header */ + + uint16_t new_csum = ip_checksum(AF_INET6, BPTR(outbuf), BLEN(outbuf), + (const uint8_t *)&pip6out.saddr, + (uint8_t *)&pip6out.daddr, OPENVPN_IPPROTO_ICMPV6); + ((struct openvpn_icmp6hdr *) BPTR(outbuf))->icmp6_cksum = htons(new_csum); + + + /* IPv6 Header */ + ASSERT(buf_write_prepend(outbuf, &pip6out, sizeof(struct openvpn_ipv6hdr))); + + /* + * Tap mode, we also need to create an Ethernet header. + */ + if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP) + { + if (BLEN(buf) < (int)sizeof(struct openvpn_ethhdr)) + { + return; + } + + const struct openvpn_ethhdr *orig_ethhdr = (struct openvpn_ethhdr *) BPTR(buf); + + /* Copy frametype and reverse source/destination for the response */ + struct openvpn_ethhdr ethhdr; + memcpy(ethhdr.source, orig_ethhdr->dest, OPENVPN_ETH_ALEN); + memcpy(ethhdr.dest, orig_ethhdr->source, OPENVPN_ETH_ALEN); + ethhdr.proto = htons(OPENVPN_ETH_P_IPV6); + ASSERT(buf_write_prepend(outbuf, ðhdr, sizeof(struct openvpn_ethhdr))); + } +#undef MAX_ICMPV6LEN +} + void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) { @@ -1243,6 +1473,10 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) { flags &= ~PIPV4_EXTRACT_DHCP_ROUTER; } + if (!c->options.block_ipv6) + { + flags &= ~(PIPV6_IMCP_NOHOST_CLIENT | PIPV6_IMCP_NOHOST_SERVER); + } if (buf->len > 0) { @@ -1278,7 +1512,7 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) /* possibly do NAT on packet */ if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat) { - const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING; + const int direction = (flags & PIP_OUTGOING) ? CN_INCOMING : CN_OUTGOING; client_nat_transform(c->options.client_nat, &ipbuf, direction); } /* possibly extract a DHCP router message */ @@ -1296,8 +1530,18 @@ process_ip_header(struct context *c, unsigned int flags, struct buffer *buf) /* possibly alter the TCP MSS */ if (flags & PIP_MSSFIX) { - mss_fixup_ipv6(&ipbuf, MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame))); + mss_fixup_ipv6(&ipbuf, + MTU_TO_MSS(TUN_MTU_SIZE_DYNAMIC(&c->c2.frame))); } + if (!(flags & PIP_OUTGOING) && (flags + &(PIPV6_IMCP_NOHOST_CLIENT | PIPV6_IMCP_NOHOST_SERVER))) + { + ipv6_send_icmp_unreachable(c, buf, + (bool)(flags & PIPV6_IMCP_NOHOST_CLIENT)); + /* Drop the IPv6 packet */ + buf->len = 0; + } + } } } @@ -1429,8 +1673,6 @@ process_outgoing_link(struct context *c) 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) @@ -1438,7 +1680,6 @@ process_outgoing_link(struct context *c) msg(M_INFO, "Network unreachable, restarting"); register_signal(c, SIGUSR1, "network-unreachable"); } -#endif } else { @@ -1481,7 +1722,9 @@ process_outgoing_tun(struct context *c) * The --mssfix option requires * us to examine the IP header (IPv4 or IPv6). */ - process_ip_header(c, PIP_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun); + process_ip_header(c, + PIP_MSSFIX | PIPV4_EXTRACT_DHCP_ROUTER | PIPV4_CLIENT_NAT | PIP_OUTGOING, + &c->c2.to_tun); if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN(&c->c2.frame)) { @@ -1506,7 +1749,7 @@ process_outgoing_tun(struct context *c) &c->c2.n_trunc_tun_write); #endif -#ifdef TUN_PASS_BUFFER +#ifdef _WIN32 size = write_tun_buffered(c->c1.tuntap, &c->c2.to_tun); #else size = write_tun(c->c1.tuntap, BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun)); @@ -1583,8 +1826,11 @@ pre_select(struct context *c) return; } - /* Does TLS need service? */ - check_tls(c); + /* If tls is enabled, do tls control channel packet processing. */ + if (c->c2.tls_multi) + { + check_tls(c); + } /* In certain cases, TLS errors will require a restart */ check_tls_errors(c); @@ -1593,17 +1839,24 @@ pre_select(struct context *c) return; } - /* check for incoming configuration info on the control channel */ - check_incoming_control_channel(c); +#if P2MP + /* check for incoming control messages on the control channel like + * push request/reply, or authentication failure and 2FA messages */ + if (tls_test_payload_len(c->c2.tls_multi) > 0) + { + check_incoming_control_channel(c); + } +#endif -#ifdef ENABLE_OCC /* Should we send an OCC message? */ check_send_occ_msg(c); -#endif #ifdef ENABLE_FRAGMENT /* Should we deliver a datagram fragment to remote? */ - check_fragment(c); + if (c->c2.fragment) + { + check_fragment(c); + } #endif /* Update random component of timeout */ @@ -1728,6 +1981,17 @@ io_wait_dowork(struct context *c, const unsigned int flags) tuntap |= EVENT_READ; } +#ifdef _WIN32 + if (tuntap_is_wintun(c->c1.tuntap)) + { + /* + * With wintun we are only interested in read event. Ring buffer is + * always ready for write, so we don't do wait. + */ + tuntap = EVENT_READ; + } +#endif + /* * Configure event wait based on socket, tuntap flags. */ diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index 924cc5e..a8b19f6 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -31,9 +31,9 @@ #ifndef FORWARD_H #define FORWARD_H -#include "openvpn.h" -#include "occ.h" -#include "ping.h" +/* the following macros must be defined before including any other header + * file + */ #define TUN_OUT(c) (BLEN(&(c)->c2.to_tun) > 0) #define LINK_OUT(c) (BLEN(&(c)->c2.to_link) > 0) @@ -47,6 +47,10 @@ #define TO_LINK_DEF(c) (LINK_OUT(c) || TO_LINK_FRAG(c)) +#include "openvpn.h" +#include "occ.h" +#include "ping.h" + #define IOW_TO_TUN (1<<0) #define IOW_TO_LINK (1<<1) #define IOW_READ_TUN (1<<2) @@ -60,6 +64,41 @@ #define IOW_READ (IOW_READ_TUN|IOW_READ_LINK) +extern counter_type link_read_bytes_global; + +extern counter_type link_write_bytes_global; + +void check_tls(struct context *c); + +void check_tls_errors_co(struct context *c); + +void check_tls_errors_nco(struct context *c); + +#if P2MP +void check_incoming_control_channel(struct context *c); + +void check_scheduled_exit(struct context *c); + +void check_push_request(struct context *c); + +#endif /* P2MP */ + +#ifdef ENABLE_FRAGMENT +void check_fragment(struct context *c); + +#endif /* ENABLE_FRAGMENT */ + +void check_connection_established(struct context *c); + +void check_add_routes(struct context *c); + +void check_inactivity_timeout(struct context *c); + +void check_server_poll_timeout(struct context *c); + +void check_status_file(struct context *c); + +void io_wait_dowork(struct context *c, const unsigned int flags); void pre_select(struct context *c); @@ -247,13 +286,44 @@ void process_outgoing_tun(struct context *c); /**************************************************************************/ -bool send_control_channel_string(struct context *c, const char *str, int msglevel); +/* + * Send a string to remote over the TLS control channel. + * Used for push/pull messages, passing username/password, + * etc. + * @param c - The context structure of the VPN tunnel associated with + * the packet. + * @param str - The message to be sent + * @param msglevel - Message level to use for logging + */ +bool +send_control_channel_string(struct context *c, const char *str, int msglevel); -#define PIPV4_PASSTOS (1<<0) -#define PIP_MSSFIX (1<<1) /* v4 and v6 */ -#define PIPV4_OUTGOING (1<<2) -#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3) -#define PIPV4_CLIENT_NAT (1<<4) +/* + * Send a string to remote over the TLS control channel. + * Used for push/pull messages, passing username/password, + * etc. + * + * This variant does not schedule the actual sending of the message + * The caller needs to ensure that it is scheduled or call + * send_control_channel_string + * + * @param multi - The tls_multi structure of the VPN tunnel associated + * with the packet. + * @param str - The message to be sent + * @param msglevel - Message level to use for logging + */ + +bool +send_control_channel_string_dowork(struct tls_multi *multi, + const char *str, int msglevel); + +#define PIPV4_PASSTOS (1<<0) +#define PIP_MSSFIX (1<<1) /* v4 and v6 */ +#define PIP_OUTGOING (1<<2) +#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3) +#define PIPV4_CLIENT_NAT (1<<4) +#define PIPV6_IMCP_NOHOST_CLIENT (1<<5) +#define PIPV6_IMCP_NOHOST_SERVER (1<<6) void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf); @@ -262,4 +332,116 @@ void schedule_exit(struct context *c, const int n_seconds, const int signal); #endif +static inline struct link_socket_info * +get_link_socket_info(struct context *c) +{ + if (c->c2.link_socket_info) + { + return c->c2.link_socket_info; + } + else + { + return &c->c2.link_socket->info; + } +} + +static inline void +register_activity(struct context *c, const int size) +{ + if (c->options.inactivity_timeout) + { + c->c2.inactivity_bytes += size; + if (c->c2.inactivity_bytes >= c->options.inactivity_minimum_bytes) + { + c->c2.inactivity_bytes = 0; + event_timeout_reset(&c->c2.inactivity_interval); + } + } +} + +/* + * Return the io_wait() flags appropriate for + * a point-to-point tunnel. + */ +static inline unsigned int +p2p_iow_flags(const struct context *c) +{ + unsigned int flags = (IOW_SHAPER|IOW_CHECK_RESIDUAL|IOW_FRAG|IOW_READ|IOW_WAIT_SIGNAL); + if (c->c2.to_link.len > 0) + { + flags |= IOW_TO_LINK; + } + if (c->c2.to_tun.len > 0) + { + flags |= IOW_TO_TUN; + } +#ifdef _WIN32 + if (tuntap_ring_empty(c->c1.tuntap)) + { + flags &= ~IOW_READ_TUN; + } +#endif + return flags; +} + +/* + * This is the core I/O wait function, used for all I/O waits except + * for TCP in server mode. + */ +static inline void +io_wait(struct context *c, const unsigned int flags) +{ + void io_wait_dowork(struct context *c, const unsigned int flags); + + if (c->c2.fast_io && (flags & (IOW_TO_TUN|IOW_TO_LINK|IOW_MBUF))) + { + /* fast path -- only for TUN/TAP/UDP writes */ + unsigned int ret = 0; + if (flags & IOW_TO_TUN) + { + ret |= TUN_WRITE; + } + if (flags & (IOW_TO_LINK|IOW_MBUF)) + { + ret |= SOCKET_WRITE; + } + c->c2.event_set_status = ret; + } + else + { +#ifdef _WIN32 + bool skip_iowait = flags & IOW_TO_TUN; + if (flags & IOW_READ_TUN) + { + /* + * don't read from tun if we have pending write to link, + * since every tun read overwrites to_link buffer filled + * by previous tun read + */ + skip_iowait = !(flags & IOW_TO_LINK); + } + if (tuntap_is_wintun(c->c1.tuntap) && skip_iowait) + { + unsigned int ret = 0; + if (flags & IOW_TO_TUN) + { + ret |= TUN_WRITE; + } + if (flags & IOW_READ_TUN) + { + ret |= TUN_READ; + } + c->c2.event_set_status = ret; + } + else +#endif /* ifdef _WIN32 */ + { + /* slow path */ + io_wait_dowork(c, flags); + } + } +} + +#define CONNECTION_ESTABLISHED(c) (get_link_socket_info(c)->connection_established) + #endif /* FORWARD_H */ diff --git a/src/openvpn/fragment.c b/src/openvpn/fragment.c index 4eb1dd2..6df71d0 100644 --- a/src/openvpn/fragment.c +++ b/src/openvpn/fragment.c @@ -31,6 +31,7 @@ #ifdef ENABLE_FRAGMENT +#include "crypto.h" #include "misc.h" #include "fragment.h" #include "integer.h" @@ -177,7 +178,7 @@ fragment_incoming(struct fragment_master *f, struct buffer *buf, if (flags & (FRAG_SEQ_ID_MASK | FRAG_ID_MASK)) { - FRAG_ERR("spurrious FRAG_WHOLE flags"); + FRAG_ERR("spurious FRAG_WHOLE flags"); } } else if (frag_type == FRAG_YES_NOTLAST || frag_type == FRAG_YES_LAST) diff --git a/src/openvpn/gremlin.c b/src/openvpn/gremlin.c index 114cb19..3f2bded 100644 --- a/src/openvpn/gremlin.c +++ b/src/openvpn/gremlin.c @@ -38,6 +38,7 @@ #include "error.h" #include "common.h" +#include "crypto.h" #include "misc.h" #include "otime.h" #include "gremlin.h" diff --git a/src/openvpn/helper.c b/src/openvpn/helper.c index ff9df50..a1d0307 100644 --- a/src/openvpn/helper.c +++ b/src/openvpn/helper.c @@ -36,7 +36,6 @@ #include "memdbg.h" -#if P2MP_SERVER static const char * print_netmask(int netbits, struct gc_arena *gc) @@ -139,7 +138,6 @@ verify_common_subnet(const char *opt, const in_addr_t a, const in_addr_t b, cons gc_free(&gc); } -#endif /* if P2MP_SERVER */ /* * Process server, server-bridge, and client helper @@ -152,7 +150,6 @@ helper_client_server(struct options *o) struct gc_arena gc = gc_new(); #if P2MP -#if P2MP_SERVER /* * Get tun/tap/null device type @@ -177,10 +174,11 @@ helper_client_server(struct options *o) */ if (o->server_ipv6_defined) { - if (!o->server_defined) + if (o->client) { - msg(M_USAGE, "--server-ipv6 must be used together with --server"); + msg(M_USAGE, "--server-ipv6 and --client cannot be used together"); } + if (o->server_flags & SF_NOPOOL) { msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" ); @@ -190,6 +188,9 @@ helper_client_server(struct options *o) msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly"); } + o->mode = MODE_SERVER; + o->tls_server = true; + /* local ifconfig is "base address + 1" and "+2" */ o->ifconfig_ipv6_local = print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc ); @@ -197,12 +198,17 @@ helper_client_server(struct options *o) print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc ); o->ifconfig_ipv6_netbits = o->server_netbits_ipv6; - /* pool starts at "base address + 0x1000" - leave enough room */ - ASSERT( o->server_netbits_ipv6 <= 112 ); /* want 16 bits */ + /* basic sanity check */ + ASSERT(o->server_netbits_ipv6 >= 64 && o->server_netbits_ipv6 <= 124); o->ifconfig_ipv6_pool_defined = true; - o->ifconfig_ipv6_pool_base = - add_in6_addr( o->server_network_ipv6, 0x1000 ); + /* For large enough pools we keep the original behaviour of adding + * 0x1000 when computing the base. + * + * Smaller pools can't get that far, therefore we just increase by 2 + */ + o->ifconfig_ipv6_pool_base = add_in6_addr(o->server_network_ipv6, + o->server_netbits_ipv6 < 112 ? 0x1000 : 2); o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6; push_option( o, "tun-ipv6", M_USAGE ); @@ -353,6 +359,14 @@ helper_client_server(struct options *o) } push_option(o, print_opt_topology(topology, &o->gc), M_USAGE); + + if (topology == TOP_NET30 && !(o->server_flags & SF_NOPOOL)) + { + msg(M_WARN, "WARNING: --topology net30 support for server " + "configs with IPv4 pools will be removed in a future " + "release. Please migrate to --topology subnet as soon " + "as possible."); + } } else if (dev == DEV_TYPE_TAP) { @@ -464,8 +478,6 @@ helper_client_server(struct options *o) } } else -#endif /* P2MP_SERVER */ - /* * HELPER DIRECTIVE: * @@ -478,11 +490,6 @@ helper_client_server(struct options *o) */ if (o->client) { - if (o->key_method != 2) - { - msg(M_USAGE, "--client requires --key-method 2"); - } - o->pull = true; o->tls_client = true; } @@ -541,7 +548,6 @@ helper_keepalive(struct options *o) o->ping_send_timeout = o->keepalive_ping; o->ping_rec_timeout = o->keepalive_timeout; } -#if P2MP_SERVER else if (o->mode == MODE_SERVER) { o->ping_rec_timeout_action = PING_RESTART; @@ -550,7 +556,6 @@ helper_keepalive(struct options *o) push_option(o, print_str_int("ping", o->keepalive_ping, &o->gc), M_USAGE); push_option(o, print_str_int("ping-restart", o->keepalive_timeout, &o->gc), M_USAGE); } -#endif else { ASSERT(0); @@ -573,7 +578,6 @@ helper_keepalive(struct options *o) void helper_tcp_nodelay(struct options *o) { -#if P2MP_SERVER if (o->server_flags & SF_TCP_NODELAY_HELPER) { if (o->mode == MODE_SERVER) @@ -586,5 +590,4 @@ helper_tcp_nodelay(struct options *o) o->sockflags |= SF_TCP_NODELAY; } } -#endif } diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 8bac74f..dfa045b 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -35,26 +35,30 @@ #include "win32.h" #include "init.h" +#include "run_command.h" #include "sig.h" #include "occ.h" #include "list.h" #include "otime.h" #include "pool.h" #include "gremlin.h" +#include "occ.h" #include "pkcs11.h" #include "ps.h" #include "lladdr.h" #include "ping.h" #include "mstats.h" #include "ssl_verify.h" +#include "ssl_ncp.h" #include "tls_crypt.h" -#include "forward-inline.h" +#include "forward.h" +#include "auth_token.h" #include "memdbg.h" -#include "occ-inline.h" static struct context *static_context; /* GLOBAL */ +static const char *saved_pid_file_name; /* GLOBAL */ /* * Crypto initialization flags @@ -162,7 +166,7 @@ run_up_down(const char *command, msg(M_FATAL, "ERROR: up/down plugin call failed"); } - argv_reset(&argv); + argv_free(&argv); } if (command) @@ -175,7 +179,7 @@ run_up_down(const char *command, ifconfig_local, ifconfig_remote, context); argv_msg(M_INFO, &argv); openvpn_run_script(&argv, es, S_FATAL, "--up/--down"); - argv_reset(&argv); + argv_free(&argv); } gc_free(&gc); @@ -269,6 +273,7 @@ ce_management_query_proxy(struct context *c) buf_printf(&out, ">PROXY:%u,%s,%s", (l ? l->current : 0) + 1, (proto_is_udp(ce->proto) ? "UDP" : "TCP"), np(ce->remote)); management_notify_generic(management, BSTR(&out)); + management->persist.special_state_msg = BSTR(&out); } ce->flags |= CE_MAN_QUERY_PROXY; while (ce->flags & CE_MAN_QUERY_PROXY) @@ -280,12 +285,51 @@ ce_management_query_proxy(struct context *c) break; } } + management->persist.special_state_msg = NULL; gc_free(&gc); } return ret; } +/** + * This method sends a custom control channel message + * + * This will write the control message + * + * command parm1,parm2,.. + * . + * to the control channel. + * + * @param arg The context struct + * @param command The command being sent + * @param parameters the parameters to the command + * @return if sending was successful + */ +static bool +management_callback_send_cc_message(void *arg, + const char *command, + const char *parameters) +{ + struct context *c = (struct context *) arg; + size_t len = strlen(command) + 1 + sizeof(parameters) + 1; + if (len > PUSH_BUNDLE_SIZE) + { + return false; + } + + struct gc_arena gc = gc_new(); + struct buffer buf = alloc_buf_gc(len, &gc); + ASSERT(buf_printf(&buf, "%s", command)); + if (parameters) + { + ASSERT(buf_printf(&buf, ",%s", parameters)); + } + bool status = send_control_channel_string(c, BSTR(&buf), D_PUSH); + + gc_free(&gc); + return status; +} static bool management_callback_remote_cmd(void *arg, const char **p) @@ -349,6 +393,7 @@ ce_management_query_remote(struct context *c) 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)); + management->persist.special_state_msg = BSTR(&out); ce->flags &= ~(CE_MAN_QUERY_REMOTE_MASK << CE_MAN_QUERY_REMOTE_SHIFT); ce->flags |= (CE_MAN_QUERY_REMOTE_QUERY << CE_MAN_QUERY_REMOTE_SHIFT); @@ -362,6 +407,7 @@ ce_management_query_remote(struct context *c) break; } } + management->persist.special_state_msg = NULL; } gc_free(&gc); @@ -529,19 +575,17 @@ next_connection_entry(struct context *c) void init_query_passwords(const struct context *c) { -#ifdef ENABLE_CRYPTO /* Certificate password input */ if (c->options.key_pass_file) { pem_password_setup(c->options.key_pass_file); } -#endif #if P2MP /* Auth user/pass input */ if (c->options.auth_user_pass_file) { -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT auth_user_pass_setup(c->options.auth_user_pass_file, &c->options.sc_info); #else auth_user_pass_setup(c->options.auth_user_pass_file, NULL); @@ -620,22 +664,18 @@ uninit_proxy(struct context *c) static void save_ncp_options(struct context *c) { -#ifdef ENABLE_CRYPTO c->c1.ciphername = c->options.ciphername; c->c1.authname = c->options.authname; c->c1.keysize = c->options.keysize; -#endif } /* Restores NCP-negotiable options to original values */ static void restore_ncp_options(struct context *c) { -#ifdef ENABLE_CRYPTO c->options.ciphername = c->c1.ciphername; c->options.authname = c->c1.authname; c->options.keysize = c->c1.keysize; -#endif } void @@ -731,7 +771,7 @@ init_static(void) { /* configure_path (); */ -#if defined(ENABLE_CRYPTO) && defined(DMALLOC) +#if defined(DMALLOC) crypto_init_dmalloc(); #endif @@ -768,14 +808,12 @@ init_static(void) update_time(); -#ifdef ENABLE_CRYPTO init_ssl_lib(); /* init PRNG used for IV generation */ /* When forking, copy this to more places in the code to avoid fork * random-state predictability */ prng_init(NULL, 0); -#endif #ifdef PID_TEST packet_id_interactive_test(); /* test the sequence number code */ @@ -838,7 +876,7 @@ init_static(void) #ifdef STATUS_PRINTF_TEST { struct gc_arena gc = gc_new(); - const char *tmp_file = create_temp_file("/tmp", "foo", &gc); + const char *tmp_file = platform_create_temp_file("/tmp", "foo", &gc); struct status_output *so = status_open(tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); status_printf(so, "%s", "foo"); status_printf(so, "%s", "bar"); @@ -851,15 +889,6 @@ init_static(void) return false; #endif -#ifdef ARGV_TEST - { - void argv_test(void); - - argv_test(); - return false; - } -#endif - #ifdef PRNG_TEST { struct gc_arena gc = gc_new(); @@ -969,9 +998,7 @@ init_static(void) void uninit_static(void) { -#ifdef ENABLE_CRYPTO free_ssl_lib(); -#endif #ifdef ENABLE_PKCS11 pkcs11_terminate(); @@ -981,7 +1008,7 @@ uninit_static(void) close_port_share(); #endif -#if defined(MEASURE_TLS_HANDSHAKE_STATS) && defined(ENABLE_CRYPTO) +#if defined(MEASURE_TLS_HANDSHAKE_STATS) show_tls_performance_stats(); #endif } @@ -1014,7 +1041,7 @@ init_options_dev(struct options *options) { if (!options->dev && options->dev_node) { - char *dev_node = string_alloc(options->dev_node, NULL); /* POSIX basename() implementaions may modify its arguments */ + char *dev_node = string_alloc(options->dev_node, NULL); /* POSIX basename() implementations may modify its arguments */ options->dev = basename(dev_node); } } @@ -1025,7 +1052,6 @@ print_openssl_info(const struct options *options) /* * OpenSSL info print mode? */ -#ifdef ENABLE_CRYPTO if (options->show_ciphers || options->show_digests || options->show_engines || options->show_tls_ciphers || options->show_curves) { @@ -1053,7 +1079,6 @@ print_openssl_info(const struct options *options) } return true; } -#endif /* ifdef ENABLE_CRYPTO */ return false; } @@ -1063,35 +1088,88 @@ print_openssl_info(const struct options *options) bool do_genkey(const struct options *options) { -#ifdef ENABLE_CRYPTO - if (options->genkey) + /* should we disable paging? */ + if (options->mlock && (options->genkey)) { - int nbits_written; + platform_mlockall(true); + } - notnull(options->shared_secret_file, - "shared secret output file (--secret)"); + /* + * We do not want user to use --genkey with --secret. In the transistion + * phase we for secret. + */ + if (options->genkey && options->genkey_type != GENKEY_SECRET + && options->shared_secret_file) + { + msg(M_USAGE, "Using --genkey type with --secret filename is " + "not supported. Use --genkey type filename instead."); + } + if (options->genkey && options->genkey_type == GENKEY_SECRET) + { + int nbits_written; + const char *genkey_filename = options->genkey_filename; + if (options->shared_secret_file && options->genkey_filename) + { + msg(M_USAGE, "You must provide a filename to either --genkey " + "or --secret, not both"); + } - if (options->mlock) /* should we disable paging? */ + /* + * Copy filename from shared_secret_file to genkey_filename to support + * the old --genkey --secret foo.file syntax. + */ + if (options->shared_secret_file) { - platform_mlockall(true); + msg(M_WARN, "WARNING: Using --genkey --secret filename is " + "DEPRECATED. Use --genkey secret filename instead."); + genkey_filename = options->shared_secret_file; } - nbits_written = write_key_file(2, options->shared_secret_file); + nbits_written = write_key_file(2, genkey_filename); + if (nbits_written < 0) + { + msg(M_FATAL, "Failed to write key file"); + } msg(D_GENKEY | M_NOPREFIX, "Randomly generated %d bit key written to %s", nbits_written, options->shared_secret_file); return true; } -#endif - return false; + else if (options->genkey && options->genkey_type == GENKEY_TLS_CRYPTV2_SERVER) + { + tls_crypt_v2_write_server_key_file(options->genkey_filename); + return true; + } + else if (options->genkey && options->genkey_type == GENKEY_TLS_CRYPTV2_CLIENT) + { + if (!options->tls_crypt_v2_file) + { + msg(M_USAGE, + "--genkey tls-crypt-v2-client requires a server key to be set via --tls-crypt-v2 to create a client key"); + } + + tls_crypt_v2_write_client_key_file(options->genkey_filename, + options->genkey_extra_data, options->tls_crypt_v2_file, + options->tls_crypt_v2_file_inline); + return true; + } + else if (options->genkey && options->genkey_type == GENKEY_AUTH_TOKEN) + { + auth_token_write_server_key_file(options->genkey_filename); + return true; + } + else + { + return false; + } } /* * Persistent TUN/TAP device management mode? */ bool -do_persist_tuntap(const struct options *options) +do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx) { if (options->persist_config) { @@ -1099,10 +1177,8 @@ do_persist_tuntap(const struct options *options) notnull(options->dev, "TUN/TAP device (--dev)"); if (options->ce.remote || options->ifconfig_local || options->ifconfig_remote_netmask -#ifdef ENABLE_CRYPTO || options->shared_secret_file || options->tls_server || options->tls_client -#endif ) { msg(M_FATAL|M_OPTERR, @@ -1111,7 +1187,8 @@ do_persist_tuntap(const struct options *options) #ifdef ENABLE_FEATURE_TUN_PERSIST tuncfg(options->dev, options->dev_type, options->dev_node, options->persist_mode, - options->username, options->groupname, &options->tuntap_options); + options->username, options->groupname, &options->tuntap_options, + ctx); if (options->persist_mode && options->lladdr) { set_lladdr(options->dev, options->lladdr, NULL); @@ -1122,7 +1199,7 @@ do_persist_tuntap(const struct options *options) "options --mktun and --rmtun are not available on your operating " "system. Please check 'man tun' (or 'tap'), whether your system " "supports using 'ifconfig %s create' / 'destroy' to create/remove " - "persistant tunnel interfaces.", options->dev ); + "persistent tunnel interfaces.", options->dev ); #endif } return false; @@ -1254,12 +1331,10 @@ const char * format_common_name(struct context *c, struct gc_arena *gc) { struct buffer out = alloc_buf_gc(256, gc); -#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { buf_printf(&out, "[%s] ", tls_common_name(c->c2.tls_multi, false)); } -#endif return BSTR(&out); } @@ -1344,7 +1419,6 @@ do_init_timers(struct context *c, bool deferred) /* initialize connection establishment timer */ event_timeout_init(&c->c2.wait_for_connect, 1, now); -#ifdef ENABLE_OCC /* initialize occ timers */ if (c->options.occ @@ -1358,10 +1432,8 @@ do_init_timers(struct context *c, bool deferred) { event_timeout_init(&c->c2.occ_mtu_load_test_interval, OCC_MTU_LOAD_INTERVAL_SECONDS, now); } -#endif /* initialize packet_id persistence timer */ -#ifdef ENABLE_CRYPTO if (c->options.packet_id_file) { event_timeout_init(&c->c2.packet_id_persist_interval, 60, now); @@ -1370,7 +1442,6 @@ do_init_timers(struct context *c, bool deferred) /* 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); -#endif } } @@ -1417,7 +1488,8 @@ static void do_init_route_list(const struct options *options, struct route_list *route_list, const struct link_socket_info *link_socket_info, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { const char *gw = NULL; int dev = dev_type_enum(options->dev, options->dev_type); @@ -1441,7 +1513,8 @@ do_init_route_list(const struct options *options, gw, metric, link_socket_current_remote(link_socket_info), - es)) + es, + ctx)) { /* copy routes to environment */ setenv_routes(es, route_list); @@ -1452,18 +1525,17 @@ static void do_init_route_ipv6_list(const struct options *options, struct route_ipv6_list *route_ipv6_list, const struct link_socket_info *link_socket_info, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { const char *gw = NULL; int metric = -1; /* no metric set */ gw = options->ifconfig_ipv6_remote; /* default GW = remote end */ -#if 0 /* not yet done for IPv6 - TODO!*/ - if (options->route_ipv6_default_gateway) /* override? */ + if (options->route_ipv6_default_gateway) { gw = options->route_ipv6_default_gateway; } -#endif if (options->route_default_metric) { @@ -1490,7 +1562,8 @@ do_init_route_ipv6_list(const struct options *options, gw, metric, link_socket_current_remote_ipv6(link_socket_info), - es)) + es, + ctx)) { /* copy routes to environment */ setenv_routes_ipv6(es, route_ipv6_list); @@ -1513,7 +1586,6 @@ initialization_sequence_completed(struct context *c, const unsigned int flags) do_uid_gid_chroot(c, true); -#ifdef ENABLE_CRYPTO /* * In some cases (i.e. when receiving auth-token via * push-reply) the auth-nocache option configured on the @@ -1525,7 +1597,6 @@ initialization_sequence_completed(struct context *c, const unsigned int flags) { delayed_auth_pass_purge(); } -#endif /* ENABLE_CRYPTO */ /* Test if errors */ if (flags & ISC_ERRORS) @@ -1628,11 +1699,13 @@ do_route(const struct options *options, struct route_ipv6_list *route_ipv6_list, const struct tuntap *tt, const struct plugin_list *plugins, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { if (!options->route_noexec && ( route_list || route_ipv6_list ) ) { - add_routes(route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS(options), es); + add_routes(route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS(options), + es, ctx); setenv_int(es, "redirect_gateway", route_did_redirect_default_gateway(route_list)); } #ifdef ENABLE_MANAGEMENT @@ -1656,7 +1729,7 @@ do_route(const struct options *options, setenv_str(es, "script_type", "route-up"); argv_parse_cmd(&argv, options->route_script); openvpn_run_script(&argv, es, 0, "--route-up"); - argv_reset(&argv); + argv_free(&argv); } #ifdef _WIN32 @@ -1690,7 +1763,12 @@ do_init_tun(struct context *c) c->c1.link_socket_addr.bind_local, c->c1.link_socket_addr.remote_list, !c->options.ifconfig_nowarn, - c->c2.es); + c->c2.es, + &c->net_ctx); + +#ifdef _WIN32 + c->c1.tuntap->windows_driver = c->options.windows_driver; +#endif init_tun_post(c->c1.tuntap, &c->c2.frame, @@ -1733,7 +1811,7 @@ do_open_tun(struct context *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); + msg(D_ROUTE, "interactive service msg_channel=%" PRIu64, (unsigned long long) c->options.msg_channel); #endif /* allocate route list structure */ @@ -1744,12 +1822,13 @@ do_open_tun(struct context *c) 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); + &c->c2.link_socket->info, c->c2.es, &c->net_ctx); } 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); + &c->c2.link_socket->info, c->c2.es, + &c->net_ctx); } /* do ifconfig */ @@ -1762,7 +1841,8 @@ do_open_tun(struct context *c) c->options.dev_type, c->options.dev_node, &gc); - do_ifconfig(c->c1.tuntap, guess, TUN_MTU_SIZE(&c->c2.frame), c->c2.es); + do_ifconfig(c->c1.tuntap, guess, TUN_MTU_SIZE(&c->c2.frame), c->c2.es, + &c->net_ctx); } /* possibly add routes */ @@ -1770,7 +1850,7 @@ do_open_tun(struct context *c) { /* 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); + c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx); } #ifdef TARGET_ANDROID /* Store the old fd inside the fd so open_tun can use it */ @@ -1790,7 +1870,8 @@ do_open_tun(struct context *c) if (!c->options.ifconfig_noexec && ifconfig_order() == IFCONFIG_AFTER_TUN_OPEN) { - do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name, TUN_MTU_SIZE(&c->c2.frame), c->c2.es); + do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name, + TUN_MTU_SIZE(&c->c2.frame), c->c2.es, &c->net_ctx); } /* run the up script */ @@ -1826,7 +1907,7 @@ do_open_tun(struct context *c) 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); + c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx); } /* @@ -1896,8 +1977,11 @@ static void do_close_tun_simple(struct context *c) { msg(D_CLOSE, "Closing TUN/TAP interface"); - close_tun(c->c1.tuntap); - c->c1.tuntap = NULL; + if (c->c1.tuntap) + { + close_tun(c->c1.tuntap, &c->net_ctx); + c->c1.tuntap = NULL; + } c->c1.tuntap_owned = false; #if P2MP CLEAR(c->c1.pulled_options_digest_save); @@ -1952,7 +2036,8 @@ do_close_tun(struct context *c, bool force) c->c2.es); delete_routes(c->c1.route_list, c->c1.route_ipv6_list, - c->c1.tuntap, ROUTE_OPTION_FLAGS(&c->options), c->c2.es); + c->c1.tuntap, ROUTE_OPTION_FLAGS(&c->options), + c->c2.es, &c->net_ctx); } /* actually close tun/tap device based on --down-pre flag */ @@ -2167,12 +2252,10 @@ pull_permission_mask(const struct context *c) flags |= (OPT_P_ROUTE | OPT_P_IPWIN32); } -#ifdef ENABLE_CRYPTO if (c->options.ncp_enabled) { flags |= OPT_P_NCP; } -#endif return flags; } @@ -2194,7 +2277,6 @@ do_deferred_options(struct context *c, const unsigned int found) msg(D_PUSH, "OPTIONS IMPORT: timers and/or timeouts modified"); } -#ifdef ENABLE_OCC if (found & OPT_P_EXPLICIT_NOTIFY) { if (!proto_is_udp(c->options.ce.proto) && c->options.ce.explicit_exit_notification) @@ -2207,7 +2289,6 @@ do_deferred_options(struct context *c, const unsigned int found) msg(D_PUSH, "OPTIONS IMPORT: explicit notify parm(s) modified"); } } -#endif #ifdef USE_COMP if (found & OPT_P_COMP) @@ -2261,7 +2342,6 @@ do_deferred_options(struct context *c, const unsigned int found) msg(D_PUSH, "OPTIONS IMPORT: environment modified"); } -#ifdef ENABLE_CRYPTO if (found & OPT_P_PEER_ID) { msg(D_PUSH, "OPTIONS IMPORT: peer-id set"); @@ -2285,14 +2365,9 @@ do_deferred_options(struct context *c, const unsigned int found) /* 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"); - } - else if (c->options.ncp_enabled) + if (!check_pull_client_ncp(c, found)) { - tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername); + return false; } struct frame *frame_fragment = NULL; #ifdef ENABLE_FRAGMENT @@ -2302,6 +2377,7 @@ do_deferred_options(struct context *c, const unsigned int found) } #endif + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame, frame_fragment)) { @@ -2309,7 +2385,7 @@ do_deferred_options(struct context *c, const unsigned int found) return false; } } -#endif /* ifdef ENABLE_CRYPTO */ + return true; } @@ -2394,7 +2470,7 @@ 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 */ + /* do management hold on context restart, i.e. second, third, fourth, etc. initialization */ if (do_hold(sec)) { sec = 0; @@ -2461,19 +2537,15 @@ frame_finalize_options(struct context *c, const struct options *o) static void key_schedule_free(struct key_schedule *ks, bool free_ssl_ctx) { -#ifdef ENABLE_CRYPTO free_key_ctx_bi(&ks->static_key); if (tls_ctx_initialised(&ks->ssl_ctx) && free_ssl_ctx) { tls_ctx_free(&ks->ssl_ctx); - free_key_ctx_bi(&ks->tls_wrap_key); + free_key_ctx(&ks->tls_crypt_v2_server_key); } -#endif /* ENABLE_CRYPTO */ CLEAR(*ks); } -#ifdef ENABLE_CRYPTO - static void init_crypto_pre(struct context *c, const unsigned int flags) { @@ -2497,7 +2569,6 @@ init_crypto_pre(struct context *c, const unsigned int flags) rand_ctx_enable_prediction_resistance(); } #endif - } /* @@ -2512,11 +2583,6 @@ 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; @@ -2557,13 +2623,90 @@ do_init_crypto_static(struct context *c, const unsigned int flags) 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->use_iv, options->replay, true); + crypto_adjust_frame_parameters(&c->c2.frame, &c->c1.ks.key_type, + options->replay, true); + + /* Sanity check on sequence number, and cipher mode options */ + check_replay_consistency(&c->c1.ks.key_type, options->replay); +} + +/* + * Initialize the tls-auth/crypt key context + */ +static void +do_init_tls_wrap_key(struct context *c) +{ + const struct options *options = &c->options; + + /* TLS handshake authentication (--tls-auth) */ + if (options->ce.tls_auth_file) + { + /* Initialize key_type for tls-auth with auth only */ + CLEAR(c->c1.ks.tls_auth_key_type); + if (!streq(options->authname, "none")) + { + 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); + } + 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->ce.tls_auth_file, + options->ce.tls_auth_file_inline, + options->ce.key_direction, + "Control Channel Authentication", "tls-auth"); + } + + /* TLS handshake encryption+authentication (--tls-crypt) */ + if (options->ce.tls_crypt_file) + { + tls_crypt_init_key(&c->c1.ks.tls_wrap_key, + options->ce.tls_crypt_file, + options->ce.tls_crypt_file_inline, + options->tls_server); + } + + /* tls-crypt with client-specific keys (--tls-crypt-v2) */ + if (options->ce.tls_crypt_v2_file) + { + if (options->tls_server) + { + tls_crypt_v2_init_server_key(&c->c1.ks.tls_crypt_v2_server_key, + true, options->ce.tls_crypt_v2_file, + options->ce.tls_crypt_v2_file_inline); + } + else + { + tls_crypt_v2_init_client_key(&c->c1.ks.tls_wrap_key, + &c->c1.ks.tls_crypt_v2_wkc, + options->ce.tls_crypt_v2_file, + options->ce.tls_crypt_v2_file_inline); + } + } + - /* Sanity check on IV, sequence number, and cipher mode options */ - check_replay_iv_consistency(&c->c1.ks.key_type, options->replay, - options->use_iv); +} + +/* + * Initialise the auth-token key context + */ +static void +do_init_auth_token_key(struct context *c) +{ + if (!c->options.auth_token_generate) + { + return; + } + + auth_token_init_secret(&c->c1.ks.auth_token_key, + c->options.auth_token_secret_file, + c->options.auth_token_secret_file_inline); } /* @@ -2605,45 +2748,25 @@ do_init_crypto_tls_c1(struct context *c) return; #else /* if P2MP */ msg(M_FATAL, "Error: private key password verification failed"); -#endif +#endif /* if P2MP */ } + /* Do not warn if we only have BF-CBC in options->ciphername + * because it is still the default cipher */ + bool warn = !streq(options->ciphername, "BF-CBC") + || options->enable_ncp_fallback; /* Get cipher & hash algorithms */ init_key_type(&c->c1.ks.key_type, options->ciphername, options->authname, - options->keysize, true, true); + options->keysize, true, warn); /* Initialize PRNG with config-specified digest */ prng_init(options->prng_hash, options->prng_nonce_secret_len); - /* TLS handshake authentication (--tls-auth) */ - if (options->tls_auth_file) - { - /* Initialize key_type for tls-auth with auth only */ - CLEAR(c->c1.ks.tls_auth_key_type); - if (!streq(options->authname, "none")) - { - 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); - } - else - { - msg(M_FATAL, "ERROR: tls-auth enabled, but no valid --auth " - "algorithm specified ('%s')", options->authname); - } + /* initialize tls-auth/crypt/crypt-v2 key */ + do_init_tls_wrap_key(c); - 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); - } + /* initialise auth-token crypto support */ + do_init_auth_token_key(c); #if 0 /* was: #if ENABLE_INLINE_FILES -- Note that enabling this code will break restarts */ if (options->priv_key_file_inline) @@ -2656,6 +2779,12 @@ do_init_crypto_tls_c1(struct context *c) else { msg(D_INIT_MEDIUM, "Re-using SSL/TLS context"); + + /* + * tls-auth/crypt key can be configured per connection block, therefore + * we must reload it as it may have changed + */ + do_init_tls_wrap_key(c); } } @@ -2681,9 +2810,8 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) return; } - /* Sanity check on IV, sequence number, and cipher mode options */ - check_replay_iv_consistency(&c->c1.ks.key_type, options->replay, - options->use_iv); + /* Sanity check on sequence number, and cipher mode options */ + check_replay_consistency(&c->c1.ks.key_type, options->replay); /* 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); @@ -2697,18 +2825,13 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) else { crypto_adjust_frame_parameters(&c->c2.frame, &c->c1.ks.key_type, - options->use_iv, options->replay, packet_id_long_form); + 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; @@ -2723,24 +2846,35 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.ssl_ctx = c->c1.ks.ssl_ctx; to.key_type = c->c1.ks.key_type; to.server = options->tls_server; - to.key_method = options->key_method; to.replay = options->replay; to.replay_window = options->replay_window; to.replay_time = options->replay_time; to.tcp_mode = link_socket_proto_connection_oriented(options->ce.proto); to.config_ciphername = c->c1.ciphername; - to.config_authname = c->c1.authname; + to.config_ncp_ciphers = options->ncp_ciphers; to.ncp_enabled = options->ncp_enabled; to.transition_window = options->transition_window; to.handshake_window = options->handshake_window; to.packet_timeout = options->tls_timeout; to.renegotiate_bytes = options->renegotiate_bytes; to.renegotiate_packets = options->renegotiate_packets; - to.renegotiate_seconds = options->renegotiate_seconds; + if (options->renegotiate_seconds_min < 0) + { + /* Add 10% jitter to reneg-sec by default (server side only) */ + int auto_jitter = options->mode != MODE_SERVER ? 0 : + get_random() % max_int(options->renegotiate_seconds / 10, 1); + to.renegotiate_seconds = options->renegotiate_seconds - auto_jitter; + } + else + { + /* Add user-specified jitter to reneg-sec */ + to.renegotiate_seconds = options->renegotiate_seconds + -(get_random() % max_int(options->renegotiate_seconds + - options->renegotiate_seconds_min, 1)); + } 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; @@ -2753,7 +2887,6 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) { to.push_peer_info_detail = 0; } -#endif /* should we not xmit any packets until we get an initial * response from client? */ @@ -2762,9 +2895,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.xmit_hold = true; } -#ifdef ENABLE_OCC to.disable_occ = !options->occ; -#endif to.verify_command = options->tls_verify; to.verify_export_cert = options->tls_export_cert; @@ -2784,6 +2915,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.x509_username_field = X509_USERNAME_FIELD_DEFAULT; #endif to.es = c->c2.es; + to.net_ctx = &c->net_ctx; #ifdef ENABLE_DEBUG to.gremlin = c->options.gremlin; @@ -2795,7 +2927,6 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.mda_context = &c->c2.mda_context; #endif -#if P2MP_SERVER to.auth_user_pass_verify_script = options->auth_user_pass_verify_script; to.auth_user_pass_verify_script_via_file = options->auth_user_pass_verify_script_via_file; to.tmp_dir = options->tmp_dir; @@ -2806,12 +2937,13 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) 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 + to.auth_token_call_auth = options->auth_token_call_auth; + to.auth_token_key = c->c1.ks.auth_token_key; to.x509_track = options->x509_track; #if P2MP -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT to.sci = &options->sc_info; #endif #endif @@ -2820,7 +2952,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) to.comp_options = options->comp; #endif -#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 +#ifdef HAVE_EXPORT_KEYING_MATERIAL if (options->keying_material_exporter_label) { to.ekm_size = options->keying_material_exporter_length; @@ -2836,28 +2968,43 @@ do_init_crypto_tls(struct context *c, const unsigned int flags) { to.ekm_size = 0; } -#endif +#endif /* HAVE_EXPORT_KEYING_MATERIAL */ /* TLS handshake authentication (--tls-auth) */ - if (options->tls_auth_file) + if (options->ce.tls_auth_file) { 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.tls_auth_key_type, - false, true, true); + crypto_adjust_frame_parameters(&to.frame, &c->c1.ks.tls_auth_key_type, + true, true); } /* TLS handshake encryption (--tls-crypt) */ - if (options->tls_crypt_file) + if (options->ce.tls_crypt_file + || (options->ce.tls_crypt_v2_file && options->tls_client)) { 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 (options->ce.tls_crypt_v2_file) + { + to.tls_wrap.tls_crypt_v2_wkc = &c->c1.ks.tls_crypt_v2_wkc; + } + } + + if (options->ce.tls_crypt_v2_file) + { + to.tls_crypt_v2 = true; + if (options->tls_server) + { + to.tls_wrap.tls_crypt_v2_server_key = c->c1.ks.tls_crypt_v2_server_key; + to.tls_crypt_v2_verify_script = c->options.tls_crypt_v2_verify_script; + } } /* If we are running over TCP, allow for @@ -2910,12 +3057,10 @@ do_init_crypto_none(const struct context *c) "protected against man-in-the-middle changes. " "PLEASE DO RECONSIDER THIS CONFIGURATION!"); } -#endif /* ifdef ENABLE_CRYPTO */ static void 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); @@ -2928,11 +3073,6 @@ do_init_crypto(struct context *c, const unsigned int flags) { do_init_crypto_none(c); } -#else /* ENABLE_CRYPTO */ - msg(M_WARN, - "******* WARNING *******: " PACKAGE_NAME - " built without crypto library -- encryption and authentication features disabled -- all data will be tunnelled as cleartext"); -#endif /* ENABLE_CRYPTO */ } static void @@ -3026,7 +3166,7 @@ do_init_frame(struct context *c) /* 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, which has the side effect of + * accommodate 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. * @@ -3045,7 +3185,7 @@ do_init_frame(struct context *c) c->c2.frame_fragment_initial = c->c2.frame_fragment; #endif -#if defined(ENABLE_FRAGMENT) && defined(ENABLE_OCC) +#if defined(ENABLE_FRAGMENT) /* * MTU advisories */ @@ -3108,7 +3248,6 @@ do_option_warnings(struct context *c) msg(M_WARN, "WARNING: using --pull/--client and --ifconfig together is probably not what you want"); } -#if P2MP_SERVER if (o->server_bridge_defined | o->server_bridge_proxy_dhcp) { msg(M_WARN, "NOTE: when bridging your LAN adapter with the TAP adapter, note that the new bridge adapter will often take on its own IP address that is different from what the LAN adapter was previously set to"); @@ -3129,22 +3268,16 @@ do_option_warnings(struct context *c) msg(M_WARN, "WARNING: --keepalive option is missing from server config"); } } -#endif /* if P2MP_SERVER */ #endif /* if P2MP */ -#ifdef ENABLE_CRYPTO if (!o->replay) { msg(M_WARN, "WARNING: You have disabled Replay Protection (--no-replay) which may make " PACKAGE_NAME " less secure"); } - if (!o->use_iv) - { - msg(M_WARN, "WARNING: You have disabled Crypto IVs (--no-iv) which may make " PACKAGE_NAME " less secure"); - } if (o->tls_server) { - warn_on_use_of_common_subnets(); + warn_on_use_of_common_subnets(&c->net_ctx); } if (o->tls_client && !o->tls_verify @@ -3158,16 +3291,15 @@ do_option_warnings(struct context *c) { msg(M_WARN, "WARNING: --ns-cert-type is DEPRECATED. Use --remote-cert-tls instead."); } -#endif /* ifdef ENABLE_CRYPTO */ - /* If a script is used, print appropiate warnings */ + /* If a script is used, print appropriate warnings */ if (o->user_script_used) { - if (script_security >= SSEC_SCRIPTS) + if (script_security() >= SSEC_SCRIPTS) { msg(M_WARN, "NOTE: the current --script-security setting may allow this configuration to call user-defined scripts"); } - else if (script_security >= SSEC_PW_ENV) + else if (script_security() >= SSEC_PW_ENV) { msg(M_WARN, "WARNING: the current --script-security setting may allow passwords to be passed to scripts via environmental variables"); } @@ -3181,9 +3313,7 @@ do_option_warnings(struct context *c) static void do_init_frame_tls(struct context *c) { -#ifdef ENABLE_CRYPTO do_init_finalize_tls_frame(c); -#endif } struct context_buffers * @@ -3198,10 +3328,8 @@ init_context_buffers(const struct frame *frame) b->aux_buf = alloc_buf(BUF_SIZE(frame)); -#ifdef ENABLE_CRYPTO b->encrypt_buf = alloc_buf(BUF_SIZE(frame)); b->decrypt_buf = alloc_buf(BUF_SIZE(frame)); -#endif #ifdef USE_COMP b->compress_buf = alloc_buf(BUF_SIZE(frame)); @@ -3225,10 +3353,8 @@ free_context_buffers(struct context_buffers *b) free_buf(&b->decompress_buf); #endif -#ifdef ENABLE_CRYPTO free_buf(&b->encrypt_buf); free_buf(&b->decrypt_buf); -#endif free(b); } @@ -3313,6 +3439,7 @@ do_init_socket_1(struct context *c, const int mode) c->options.rcvbuf, c->options.sndbuf, c->options.mark, + c->options.bind_dev, &c->c2.server_poll_interval, sockflags); } @@ -3343,7 +3470,6 @@ do_print_data_channel_mtu_parms(struct context *c) #endif } -#ifdef ENABLE_OCC /* * Get local and remote options compatibility strings. */ @@ -3353,9 +3479,11 @@ do_compute_occ_strings(struct context *c) struct gc_arena gc = gc_new(); c->c2.options_string_local = - options_string(&c->options, &c->c2.frame, c->c1.tuntap, false, &gc); + options_string(&c->options, &c->c2.frame, c->c1.tuntap, &c->net_ctx, + false, &gc); c->c2.options_string_remote = - options_string(&c->options, &c->c2.frame, c->c1.tuntap, true, &gc); + options_string(&c->options, &c->c2.frame, c->c1.tuntap, &c->net_ctx, + true, &gc); msg(D_SHOW_OCC, "Local Options String (VER=%s): '%s'", options_string_version(c->c2.options_string_local, &gc), @@ -3364,18 +3492,15 @@ do_compute_occ_strings(struct context *c) options_string_version(c->c2.options_string_remote, &gc), c->c2.options_string_remote); -#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { tls_multi_init_set_options(c->c2.tls_multi, c->c2.options_string_local, c->c2.options_string_remote); } -#endif gc_free(&gc); } -#endif /* ifdef ENABLE_OCC */ /* * These things can only be executed once per program instantiation. @@ -3445,14 +3570,12 @@ do_close_free_buf(struct context *c) static void do_close_tls(struct context *c) { -#ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { tls_multi_free(c->c2.tls_multi, true); c->c2.tls_multi = NULL; } -#ifdef ENABLE_OCC /* free options compatibility strings */ if (c->c2.options_string_local) { @@ -3463,14 +3586,12 @@ do_close_tls(struct context *c) free(c->c2.options_string_remote); } c->c2.options_string_local = c->c2.options_string_remote = NULL; -#endif if (c->c2.pulled_options_state) { md_ctx_cleanup(c->c2.pulled_options_state); md_ctx_free(c->c2.pulled_options_state); } -#endif } /* @@ -3479,6 +3600,15 @@ do_close_tls(struct context *c) static void do_close_free_key_schedule(struct context *c, bool free_ssl_ctx) { + /* + * always free the tls_auth/crypt key. If persist_key is true, the key will + * be reloaded from memory (pre-cached) + */ + free_key_ctx_bi(&c->c1.ks.tls_wrap_key); + CLEAR(c->c1.ks.tls_wrap_key); + buf_clear(&c->c1.ks.tls_crypt_v2_wkc); + free_buf(&c->c1.ks.tls_crypt_v2_wkc); + if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_key)) { key_schedule_free(&c->c1.ks, free_ssl_ctx); @@ -3530,19 +3660,17 @@ do_close_link_socket(struct context *c) } /* - * Close packet-id persistance file + * Close packet-id persistence file */ static void do_close_packet_id(struct context *c) { -#ifdef ENABLE_CRYPTO 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); } -#endif } #ifdef ENABLE_FRAGMENT @@ -3627,25 +3755,22 @@ do_close_status_output(struct context *c) } /* - * Handle ifconfig-pool persistance object. + * Handle ifconfig-pool persistence object. */ static void do_open_ifconfig_pool_persist(struct context *c) { -#if P2MP_SERVER if (!c->c1.ifconfig_pool_persist && c->options.ifconfig_pool_persist_filename) { c->c1.ifconfig_pool_persist = ifconfig_pool_persist_init(c->options.ifconfig_pool_persist_filename, c->options.ifconfig_pool_persist_refresh_freq); c->c1.ifconfig_pool_persist_owned = true; } -#endif } static void do_close_ifconfig_pool_persist(struct context *c) { -#if P2MP_SERVER if (!(c->sig->signal_received == SIGUSR1)) { if (c->c1.ifconfig_pool_persist && c->c1.ifconfig_pool_persist_owned) @@ -3655,7 +3780,6 @@ do_close_ifconfig_pool_persist(struct context *c) c->c1.ifconfig_pool_persist_owned = false; } } -#endif } /* @@ -3721,7 +3845,6 @@ do_setup_fast_io(struct context *c) static void do_signal_on_tls_errors(struct context *c) { -#ifdef ENABLE_CRYPTO if (c->options.tls_exit) { c->c2.tls_exit_signal = SIGTERM; @@ -3730,7 +3853,6 @@ do_signal_on_tls_errors(struct context *c) { c->c2.tls_exit_signal = SIGUSR1; } -#endif } #ifdef ENABLE_PLUGIN @@ -3880,6 +4002,7 @@ 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; + cb.send_cc_message = management_callback_send_cc_message; #ifdef TARGET_ANDROID cb.network_change = management_callback_network_change; #endif @@ -4121,13 +4244,11 @@ init_instance(struct context *c, const struct env_set *env, const unsigned int f do_open_ifconfig_pool_persist(c); } -#ifdef ENABLE_OCC /* reset OCC state */ if (c->mode == CM_P2P || child) { c->c2.occ_op = occ_reset_op(); } -#endif /* our wait-for-i/o objects, different for posix vs. win32 */ if (c->mode == CM_P2P) @@ -4227,13 +4348,11 @@ init_instance(struct context *c, const struct env_set *env, const unsigned int f /* print MTU info */ do_print_data_channel_mtu_parms(c); -#ifdef ENABLE_OCC /* get local and remote options compatibility strings */ if (c->mode == CM_P2P || child) { do_compute_occ_strings(c); } -#endif /* initialize output speed limiter */ if (c->mode == CM_P2P) @@ -4241,7 +4360,7 @@ init_instance(struct context *c, const struct env_set *env, const unsigned int f do_init_traffic_shaper(c); } - /* do one-time inits, and possibily become a daemon here */ + /* do one-time inits, and possibly become a daemon here */ do_init_first_time(c); #ifdef ENABLE_PLUGIN @@ -4371,7 +4490,7 @@ close_instance(struct context *c) do_close_plugins(c); #endif - /* close packet-id persistance file */ + /* close packet-id persistence file */ do_close_packet_id(c); /* close --status file */ @@ -4412,17 +4531,19 @@ inherit_context_child(struct context *dest, /* c1 init */ packet_id_persist_init(&dest->c1.pid_persist); -#ifdef ENABLE_CRYPTO dest->c1.ks.key_type = src->c1.ks.key_type; /* inherit SSL context */ dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx; 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; + dest->c1.ks.tls_crypt_v2_server_key = src->c1.ks.tls_crypt_v2_server_key; /* inherit pre-NCP ciphers */ dest->c1.ciphername = src->c1.ciphername; dest->c1.authname = src->c1.authname; dest->c1.keysize = src->c1.keysize; -#endif + + /* inherit auth-token */ + dest->c1.ks.auth_token_key = src->c1.ks.auth_token_key; /* options */ dest->options = src->options; @@ -4496,16 +4617,12 @@ inherit_context_top(struct context *dest, /* detach plugins */ dest->plugins_owned = false; -#ifdef ENABLE_CRYPTO dest->c2.tls_multi = NULL; -#endif /* detach c1 ownership */ dest->c1.tuntap_owned = false; dest->c1.status_output_owned = false; -#if P2MP_SERVER dest->c1.ifconfig_pool_persist_owned = false; -#endif /* detach c2 ownership */ dest->c2.event_set_owned = false; @@ -4556,7 +4673,46 @@ close_context(struct context *c, int sig, unsigned int flags) } } -#ifdef ENABLE_CRYPTO +/* Write our PID to a file */ +void +write_pid_file(const char *filename, const char *chroot_dir) +{ + if (filename) + { + unsigned int pid = 0; + FILE *fp = platform_fopen(filename, "w"); + if (!fp) + { + msg(M_ERR, "Open error on pid file %s", filename); + return; + } + + pid = platform_getpid(); + fprintf(fp, "%u\n", pid); + if (fclose(fp)) + { + msg(M_ERR, "Close error on pid file %s", filename); + } + + /* remember file name so it can be deleted "out of context" later */ + /* (the chroot case is more complex and not handled today) */ + if (!chroot_dir) + { + saved_pid_file_name = strdup(filename); + } + } +} + +/* remove PID file on exit, called from openvpn_exit() */ +void +remove_pid_file(void) +{ + if (saved_pid_file_name) + { + platform_unlink(saved_pid_file_name); + } +} + /* * Do a loopback test @@ -4585,12 +4741,9 @@ test_crypto_thread(void *arg) return NULL; } -#endif /* ENABLE_CRYPTO */ - bool do_test_crypto(const struct options *o) { -#ifdef ENABLE_CRYPTO if (o->test_crypto) { struct context c; @@ -4605,6 +4758,5 @@ do_test_crypto(const struct options *o) test_crypto_thread((void *) &c); return true; } -#endif return false; } diff --git a/src/openvpn/init.h b/src/openvpn/init.h index 2c846db..a2fdccd 100644 --- a/src/openvpn/init.h +++ b/src/openvpn/init.h @@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options); bool do_genkey(const struct options *options); -bool do_persist_tuntap(const struct options *options); +bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx); bool possibly_become_daemon(const struct options *options); @@ -76,7 +76,8 @@ void do_route(const struct options *options, struct route_ipv6_list *route_ipv6_list, const struct tuntap *tt, const struct plugin_list *plugins, - struct env_set *es); + struct env_set *es, + openvpn_net_ctx_t *ctx); void close_instance(struct context *c); @@ -140,4 +141,9 @@ void open_plugins(struct context *c, const bool import_options, int init_point); #endif +void tun_abort(void); + +void write_pid_file(const char *filename, const char *chroot_dir); +void remove_pid_file(void); + #endif /* ifndef INIT_H */ diff --git a/src/openvpn/integer.h b/src/openvpn/integer.h index a7e19d3..3755f43 100644 --- a/src/openvpn/integer.h +++ b/src/openvpn/integer.h @@ -26,6 +26,16 @@ #include "error.h" +#ifndef htonll +#define htonll(x) ((1==htonl(1)) ? (x) : \ + ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#endif + +#ifndef ntohll +#define ntohll(x) ((1==ntohl(1)) ? (x) : \ + ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) +#endif + /* * min/max functions */ diff --git a/src/openvpn/list.c b/src/openvpn/list.c index 09e393a..549ebdf 100644 --- a/src/openvpn/list.c +++ b/src/openvpn/list.c @@ -29,7 +29,6 @@ #include "syshead.h" -#if P2MP_SERVER #include "integer.h" #include "list.h" @@ -222,18 +221,6 @@ hash_remove_marked(struct hash *hash, struct hash_bucket *bucket) } } -uint32_t -void_ptr_hash_function(const void *key, uint32_t iv) -{ - return hash_func((const void *)&key, sizeof(key), iv); -} - -bool -void_ptr_compare_function(const void *key1, const void *key2) -{ - return key1 == key2; -} - void hash_iterator_init_range(struct hash *hash, struct hash_iterator *hi, @@ -668,10 +655,3 @@ hash_func(const uint8_t *k, uint32_t length, uint32_t initval) /*-------------------------------------- report the result */ return c; } - -#else /* if P2MP_SERVER */ -static void -dummy(void) -{ -} -#endif /* P2MP_SERVER */ diff --git a/src/openvpn/list.h b/src/openvpn/list.h index b67301c..c381acd 100644 --- a/src/openvpn/list.h +++ b/src/openvpn/list.h @@ -33,8 +33,6 @@ * client instances over various key spaces. */ -#if P2MP_SERVER - /* define this to enable special list test mode */ /*#define LIST_TEST*/ @@ -116,10 +114,6 @@ void hash_iterator_free(struct hash_iterator *hi); uint32_t hash_func(const uint8_t *k, uint32_t length, uint32_t initval); -uint32_t void_ptr_hash_function(const void *key, uint32_t iv); - -bool void_ptr_compare_function(const void *key1, const void *key2); - #ifdef LIST_TEST void list_test(void); @@ -198,5 +192,4 @@ hash_remove(struct hash *hash, const void *key) return ret; } -#endif /* P2MP_SERVER */ #endif /* LIST */ diff --git a/src/openvpn/lladdr.c b/src/openvpn/lladdr.c index ff71e48..22857eb 100644 --- a/src/openvpn/lladdr.c +++ b/src/openvpn/lladdr.c @@ -11,6 +11,8 @@ #include "syshead.h" #include "error.h" #include "misc.h" +#include "run_command.h" +#include "lladdr.h" int set_lladdr(const char *ifname, const char *lladdr, @@ -67,6 +69,6 @@ set_lladdr(const char *ifname, const char *lladdr, msg(M_INFO, "TUN/TAP link layer address set to %s", lladdr); } - argv_reset(&argv); + argv_free(&argv); return r; } diff --git a/src/openvpn/lzo.c b/src/openvpn/lzo.c index 8d9efea..d053fed 100644 --- a/src/openvpn/lzo.c +++ b/src/openvpn/lzo.c @@ -103,9 +103,11 @@ lzo_compress_init(struct compress_context *compctx) 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) + + int lzo_status = lzo_init(); + if (lzo_status != LZO_E_OK) { - msg(M_FATAL, "Cannot initialize LZO compression library"); + msg(M_FATAL, "Cannot initialize LZO compression library (lzo_init() returns %d)", lzo_status); } compctx->wu.lzo.wmem = (lzo_voidp) lzo_malloc(compctx->wu.lzo.wmem_size); check_malloc_return(compctx->wu.lzo.wmem); @@ -121,7 +123,7 @@ lzo_compress_uninit(struct compress_context *compctx) static inline bool lzo_compression_enabled(struct compress_context *compctx) { - if (compctx->flags & COMP_F_ASYM) + if (!(compctx->flags & COMP_F_ALLOW_COMPRESS)) { return false; } diff --git a/src/openvpn/lzo.h b/src/openvpn/lzo.h index 11e1c39..453cd8e 100644 --- a/src/openvpn/lzo.h +++ b/src/openvpn/lzo.h @@ -39,14 +39,14 @@ */ #if defined(HAVE_LZO_LZOUTIL_H) -#include "lzo/lzoutil.h" +#include <lzo/lzoutil.h> #elif defined(HAVE_LZOUTIL_H) -#include "lzoutil.h" +#include <lzoutil.h> #endif #if defined(HAVE_LZO_LZO1X_H) -#include "lzo/lzo1x.h" +#include <lzo/lzo1x.h> #elif defined(HAVE_LZO1X_H) -#include "lzo1x.h" +#include <lzo1x.h> #endif #include "buffer.h" diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 61d61ef..898cb3b 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -75,6 +75,7 @@ man_help(void) msg(M_CLIENT, "auth-retry t : Auth failure retry mode (none,interact,nointeract)."); msg(M_CLIENT, "bytecount n : Show bytes in/out, update every n secs (0=off)."); msg(M_CLIENT, "echo [on|off] [N|all] : Like log, but only show messages in echo buffer."); + msg(M_CLIENT, "cr-response response : Send a challenge response answer via CR_RESPONSE to server"); msg(M_CLIENT, "exit|quit : Close management session."); msg(M_CLIENT, "forget-passwords : Forget passwords entered so far."); msg(M_CLIENT, "help : Print this message."); @@ -104,18 +105,20 @@ man_help(void) msg(M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID"); msg(M_CLIENT, "client-deny CID KID R [CR] : Deny auth client-id/key-id CID/KID with log reason"); msg(M_CLIENT, " text R and optional client reason text CR"); + msg(M_CLIENT, "client-pending-auth CID MSG : Instruct OpenVPN to send AUTH_PENDING and INFO_PRE msg" + " to the client and wait for a final client-auth/client-deny"); msg(M_CLIENT, "client-kill CID [M] : Kill client instance CID with message M (def=RESTART)"); msg(M_CLIENT, "env-filter [level] : Set env-var filter level"); #ifdef MANAGEMENT_PF msg(M_CLIENT, "client-pf CID : Define packet filter for client CID (MULTILINE)"); #endif #endif -#ifdef MANAGMENT_EXTERNAL_KEY - msg(M_CLIENT, "rsa-sig : Enter an RSA signature in response to >RSA_SIGN challenge"); + msg(M_CLIENT, "rsa-sig : Enter a signature in response to >RSA_SIGN challenge"); + msg(M_CLIENT, " Enter signature base64 on subsequent lines followed by END"); + msg(M_CLIENT, "pk-sig : Enter a signature in response to >PK_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."); msg(M_CLIENT, "state [on|off] [N|all] : Like log, but show state history."); @@ -123,7 +126,7 @@ man_help(void) msg(M_CLIENT, "test n : Produce n lines of output for testing/debugging."); msg(M_CLIENT, "username type u : Enter username u for a queried OpenVPN username."); msg(M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent."); - msg(M_CLIENT, "version : Show current version number."); + msg(M_CLIENT, "version [n] : Set client's version to n or show current version of daemon."); msg(M_CLIENT, "END"); } @@ -762,10 +765,8 @@ man_query_need_str(struct management *man, const char *type, const char *action) static void man_forget_passwords(struct management *man) { -#ifdef ENABLE_CRYPTO ssl_purge_auth(false); msg(M_CLIENT, "SUCCESS: Passwords were forgotten"); -#endif } static void @@ -781,6 +782,27 @@ man_net(struct management *man) } } +static void +man_send_cc_message(struct management *man, const char *message, const char *parameters) +{ + if (man->persist.callback.send_cc_message) + { + const bool status = (*man->persist.callback.send_cc_message) + (man->persist.callback.arg, message, parameters); + if (status) + { + msg(M_CLIENT, "SUCCESS: command succeeded"); + } + else + { + msg(M_CLIENT, "ERROR: command failed"); + } + } + else + { + msg(M_CLIENT, "ERROR: This command is not supported by the current daemon mode"); + } +} #ifdef ENABLE_PKCS11 static void @@ -847,8 +869,6 @@ man_hold(struct management *man, const char *cmd) } } -#ifdef MANAGEMENT_IN_EXTRA - #define IER_RESET 0 #define IER_NEW 1 @@ -936,8 +956,7 @@ in_extra_dispatch(struct management *man) break; #endif /* ifdef MANAGEMENT_PF */ -#ifdef MANAGMENT_EXTERNAL_KEY - case IEC_RSA_SIGN: + case IEC_PK_SIGN: man->connection.ext_key_state = EKS_READY; buffer_list_free(man->connection.ext_key_input); man->connection.ext_key_input = man->connection.in_extra; @@ -950,13 +969,10 @@ in_extra_dispatch(struct management *man) man->connection.ext_cert_input = man->connection.in_extra; man->connection.in_extra = NULL; return; -#endif } in_extra_reset(&man->connection, IER_RESET); } -#endif /* MANAGEMENT_IN_EXTRA */ - #ifdef MANAGEMENT_DEF_AUTH static bool @@ -987,6 +1003,43 @@ parse_kid(const char *str, unsigned int *kid) } } +/** + * Will send a notification to the client that succesful authentication + * will require an additional step (web based SSO/2-factor auth/etc) + * + * @param man The management interface struct + * @param cid_str The CID in string form + * @param extra The string to be send to the client containing + * the information of the additional steps + */ +static void +man_client_pending_auth(struct management *man, const char *cid_str, const char *extra) +{ + unsigned long cid = 0; + if (parse_cid(cid_str, &cid)) + { + if (man->persist.callback.client_pending_auth) + { + bool ret = (*man->persist.callback.client_pending_auth) + (man->persist.callback.arg, cid, extra); + + if (ret) + { + msg(M_CLIENT, "SUCCESS: client-pending-auth command succeeded"); + } + else + { + msg(M_CLIENT, "SUCCESS: client-pending-auth command failed." + " Extra paramter might be too long"); + } + } + else + { + msg(M_CLIENT, "ERROR: The client-pending-auth command is not supported by the current daemon mode"); + } + } +} + static void man_client_auth(struct management *man, const char *cid_str, const char *kid_str, const bool extra) { @@ -1102,21 +1155,19 @@ man_client_pf(struct management *man, const char *cid_str) #endif /* MANAGEMENT_PF */ #endif /* MANAGEMENT_DEF_AUTH */ -#ifdef MANAGMENT_EXTERNAL_KEY - static void -man_rsa_sig(struct management *man) +man_pk_sig(struct management *man, const char *cmd_name) { struct man_connection *mc = &man->connection; if (mc->ext_key_state == EKS_SOLICIT) { mc->ext_key_state = EKS_INPUT; - mc->in_extra_cmd = IEC_RSA_SIGN; + mc->in_extra_cmd = IEC_PK_SIGN; in_extra_reset(mc, IER_NEW); } else { - msg(M_CLIENT, "ERROR: The rsa-sig command is not currently available"); + msg(M_CLIENT, "ERROR: The %s command is not currently available", cmd_name); } } @@ -1136,8 +1187,6 @@ man_certificate(struct management *man) } } -#endif /* ifdef MANAGMENT_EXTERNAL_KEY */ - static void man_load_stats(struct management *man) { @@ -1156,7 +1205,15 @@ man_load_stats(struct management *man) } #define MN_AT_LEAST (1<<0) - +/** + * Checks if the correct number of arguments to a management command are present + * and otherwise prints an error and returns false. + * + * @param p pointer to the parameter array + * @param n number of arguments required + * @param flags if MN_AT_LEAST require at least n parameters and not exactly n + * @return Return whether p has n (or at least n) parameters + */ static bool man_need(struct management *man, const char **p, const int n, unsigned int flags) { @@ -1243,6 +1300,15 @@ man_network_change(struct management *man, bool samenetwork) #endif static void +set_client_version(struct management *man, const char *version) +{ + if (version) + { + man->connection.client_version = atoi(version); + } +} + +static void man_dispatch_command(struct management *man, struct status_output *so, const char **p, const int nparms) { struct gc_arena gc = gc_new(); @@ -1257,6 +1323,10 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha { man_help(); } + else if (streq(p[0], "version") && p[1]) + { + set_client_version(man, p[1]); + } else if (streq(p[0], "version")) { msg(M_CLIENT, "OpenVPN Version: %s", title_string); @@ -1459,6 +1529,13 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha man_query_need_str(man, p[1], p[2]); } } + else if (streq(p[0], "cr-response")) + { + if (man_need(man, p, 1, 0)) + { + man_send_cc_message(man, "CR_RESPONSE", p[1]); + } + } else if (streq(p[0], "net")) { man_net(man); @@ -1503,6 +1580,13 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha man_client_auth(man, p[1], p[2], true); } } + else if (streq(p[0], "client-pending-auth")) + { + if (man_need(man, p, 2, 0)) + { + man_client_pending_auth(man, p[1], p[2]); + } + } #ifdef MANAGEMENT_PF else if (streq(p[0], "client-pf")) { @@ -1513,16 +1597,18 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha } #endif #endif /* ifdef MANAGEMENT_DEF_AUTH */ -#ifdef MANAGMENT_EXTERNAL_KEY else if (streq(p[0], "rsa-sig")) { - man_rsa_sig(man); + man_pk_sig(man, "rsa-sig"); + } + else if (streq(p[0], "pk-sig")) + { + man_pk_sig(man, "pk-sig"); } else if (streq(p[0], "certificate")) { man_certificate(man); } -#endif #ifdef ENABLE_PKCS11 else if (streq(p[0], "pkcs11-id-count")) { @@ -1911,19 +1997,16 @@ man_reset_client_socket(struct management *man, const bool exiting) man->connection.state = MS_INITIAL; command_line_reset(man->connection.in); buffer_list_reset(man->connection.out); -#ifdef MANAGEMENT_IN_EXTRA in_extra_reset(&man->connection, IER_RESET); -#endif msg(D_MANAGEMENT, "MANAGEMENT: Client disconnected"); } if (!exiting) { -#ifdef ENABLE_CRYPTO if (man->settings.flags & MF_FORGET_DISCONNECT) { ssl_purge_auth(false); } -#endif + if (man->settings.flags & MF_SIGNAL) { int mysig = man_mod_signal(man, SIGUSR1); @@ -1956,9 +2039,7 @@ man_process_command(struct management *man, const char *line) CLEAR(parms); so = status_open(NULL, 0, -1, &man->persist.vout, 0); -#ifdef MANAGEMENT_IN_EXTRA in_extra_reset(&man->connection, IER_RESET); -#endif if (man_password_needed(man)) { @@ -2196,7 +2277,6 @@ man_read(struct management *man) const char *line; while ((line = command_line_get(man->connection.in))) { -#ifdef MANAGEMENT_IN_EXTRA if (man->connection.in_extra) { if (!strcmp(line, "END")) @@ -2209,8 +2289,9 @@ man_read(struct management *man) } } else -#endif - man_process_command(man, (char *) line); + { + man_process_command(man, (char *) line); + } if (man->connection.halt) { break; @@ -2511,6 +2592,8 @@ man_connection_init(struct management *man) man->connection.es = event_set_init(&maxevents, EVENT_METHOD_FAST); } + man->connection.client_version = 1; /* default version */ + /* * Listen/connect socket */ @@ -2554,12 +2637,8 @@ man_connection_close(struct management *man) { buffer_list_free(mc->out); } -#ifdef MANAGEMENT_IN_EXTRA in_extra_reset(&man->connection, IER_RESET); -#endif -#ifdef MANAGMENT_EXTERNAL_KEY buffer_list_free(mc->ext_key_input); -#endif man_connection_clear(mc); } @@ -2740,7 +2819,9 @@ env_filter_match(const char *env_str, const int env_filter_level) "ifconfig_pool_netmask=", "time_duration=", "bytes_sent=", - "bytes_received=" + "bytes_received=", + "session_id=", + "session_state=" }; if (env_filter_level == 0) @@ -2827,7 +2908,7 @@ management_notify_generic(struct management *man, const char *str) #ifdef MANAGEMENT_DEF_AUTH static void -man_output_peer_info_env(struct management *man, struct man_def_auth_context *mdac) +man_output_peer_info_env(struct management *man, const struct man_def_auth_context *mdac) { char line[256]; if (man->persist.callback.get_peer_info) @@ -2878,6 +2959,32 @@ management_notify_client_needing_auth(struct management *management, } void +management_notify_client_cr_response(unsigned mda_key_id, + const struct man_def_auth_context *mdac, + const struct env_set *es, + const char *response) +{ + struct gc_arena gc; + if (management) + { + gc = gc_new(); + + struct buffer out = alloc_buf_gc(256, &gc); + msg(M_CLIENT, ">CLIENT:CR_RESPONSE,%lu,%u,%s", + mdac->cid, mda_key_id, response); + man_output_extra_env(management, "CLIENT"); + 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"); + management_notify_generic(management, BSTR(&out)); + + gc_free(&gc); + } +} + +void management_connection_established(struct management *management, struct man_def_auth_context *mdac, const struct env_set *es) @@ -3394,9 +3501,7 @@ management_query_user_pass(struct management *man, const char *alert_type = NULL; const char *prefix = NULL; unsigned int up_query_mode = 0; -#ifdef ENABLE_CLIENT_CR const char *sc = NULL; -#endif ret = true; man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */ man->persist.special_state_msg = NULL; @@ -3426,12 +3531,10 @@ management_query_user_pass(struct management *man, up_query_mode = UP_QUERY_USER_PASS; prefix = "PASSWORD"; alert_type = "username/password"; -#ifdef ENABLE_CLIENT_CR if (static_challenge) { sc = static_challenge; } -#endif } buf_printf(&alert_msg, ">%s:Need '%s' %s", prefix, @@ -3443,14 +3546,12 @@ management_query_user_pass(struct management *man, buf_printf(&alert_msg, " MSG:%s", up->username); } -#ifdef ENABLE_CLIENT_CR if (sc) { buf_printf(&alert_msg, " SC:%d,%s", BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), sc); } -#endif man_wait_for_client_connection(man, &signal_received, 0, MWCC_PASSWORD_WAIT); if (signal_received) @@ -3513,8 +3614,6 @@ management_query_user_pass(struct management *man, return ret; } -#ifdef MANAGMENT_EXTERNAL_KEY - static int management_query_multiline(struct management *man, const char *b64_data, const char *prompt, const char *cmd, int *state, struct buffer_list **input) @@ -3651,13 +3750,31 @@ management_query_multiline_flatten(struct management *man, char * /* returns allocated base64 signature */ -management_query_rsa_sig(struct management *man, - const char *b64_data) +management_query_pk_sig(struct management *man, const char *b64_data, + const char *algorithm) { - return management_query_multiline_flatten(man, b64_data, "RSA_SIGN", "rsa-sign", - &man->connection.ext_key_state, &man->connection.ext_key_input); -} + const char *prompt = "PK_SIGN"; + const char *desc = "pk-sign"; + struct buffer buf_data = alloc_buf(strlen(b64_data) + strlen(algorithm) + 20); + if (man->connection.client_version <= 1) + { + prompt = "RSA_SIGN"; + desc = "rsa-sign"; + } + + buf_write(&buf_data, b64_data, (int) strlen(b64_data)); + if (man->connection.client_version > 2) + { + buf_write(&buf_data, ",", (int) strlen(",")); + buf_write(&buf_data, algorithm, (int) strlen(algorithm)); + } + char *ret = management_query_multiline_flatten(man, + (char *)buf_bptr(&buf_data), prompt, desc, + &man->connection.ext_key_state, &man->connection.ext_key_input); + free_buf(&buf_data); + return ret; +} char * management_query_cert(struct management *man, const char *cert_name) @@ -3675,8 +3792,6 @@ management_query_cert(struct management *man, const char *cert_name) return result; } -#endif /* ifdef MANAGMENT_EXTERNAL_KEY */ - /* * Return true if management_hold() would block */ diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index f286754..881bfb1 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -31,7 +31,7 @@ #include "socket.h" #include "mroute.h" -#define MANAGEMENT_VERSION 1 +#define MANAGEMENT_VERSION 3 #define MANAGEMENT_N_PASSWORD_RETRIES 3 #define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE 100 #define MANAGEMENT_ECHO_BUFFER_SIZE 100 @@ -164,6 +164,7 @@ struct management_callback int (*kill_by_addr) (void *arg, const in_addr_t addr, const int port); void (*delete_event) (void *arg, event_t event); int (*n_clients) (void *arg); + bool (*send_cc_message) (void *arg, const char *message, const char *parameter); #ifdef MANAGEMENT_DEF_AUTH bool (*kill_by_cid)(void *arg, const unsigned long cid, const char *kill_msg); bool (*client_auth) (void *arg, @@ -173,6 +174,9 @@ struct management_callback const char *reason, const char *client_reason, struct buffer_list *cc_config); /* ownership transferred */ + bool (*client_pending_auth) (void *arg, + const unsigned long cid, + const char *url); char *(*get_peer_info) (void *arg, const unsigned long cid); #endif #ifdef MANAGEMENT_PF @@ -275,19 +279,18 @@ struct man_connection { struct command_line *in; struct buffer_list *out; -#ifdef MANAGEMENT_IN_EXTRA #define IEC_UNDEF 0 #define IEC_CLIENT_AUTH 1 #define IEC_CLIENT_PF 2 #define IEC_RSA_SIGN 3 #define IEC_CERTIFICATE 4 +#define IEC_PK_SIGN 5 int in_extra_cmd; struct buffer_list *in_extra; #ifdef MANAGEMENT_DEF_AUTH unsigned long in_extra_cid; unsigned int in_extra_kid; #endif -#ifdef MANAGMENT_EXTERNAL_KEY #define EKS_UNDEF 0 #define EKS_SOLICIT 1 #define EKS_INPUT 2 @@ -296,8 +299,6 @@ struct man_connection { struct buffer_list *ext_key_input; int ext_cert_state; struct buffer_list *ext_cert_input; -#endif -#endif /* ifdef MANAGEMENT_IN_EXTRA */ struct event_set *es; int env_filter_level; @@ -311,13 +312,11 @@ struct man_connection { int up_query_mode; struct user_pass up_query; -#ifdef MANAGMENT_EXTERNAL_KEY - struct buffer_list *rsa_sig; -#endif #ifdef TARGET_ANDROID int fdtosend; int lastfdreceived; #endif + int client_version; }; struct management @@ -346,14 +345,14 @@ struct management *management_init(void); #ifdef MANAGEMENT_PF #define MF_CLIENT_PF (1<<7) #endif -#define MF_UNIX_SOCK (1<<8) -#ifdef MANAGMENT_EXTERNAL_KEY -#define MF_EXTERNAL_KEY (1<<9) -#endif -#define MF_UP_DOWN (1<<10) -#define MF_QUERY_REMOTE (1<<11) -#define MF_QUERY_PROXY (1<<12) -#define MF_EXTERNAL_CERT (1<<13) +#define MF_UNIX_SOCK (1<<8) +#define MF_EXTERNAL_KEY (1<<9) +#define MF_EXTERNAL_KEY_NOPADDING (1<<10) +#define MF_EXTERNAL_KEY_PKCS1PAD (1<<11) +#define MF_UP_DOWN (1<<12) +#define MF_QUERY_REMOTE (1<<13) +#define MF_QUERY_PROXY (1<<14) +#define MF_EXTERNAL_CERT (1<<15) bool management_open(struct management *man, const char *addr, @@ -435,16 +434,18 @@ void management_learn_addr(struct management *management, const struct mroute_addr *addr, const bool primary); -#endif +void management_notify_client_cr_response(unsigned mda_key_id, + const struct man_def_auth_context *mdac, + const struct env_set *es, + const char *response); -#ifdef MANAGMENT_EXTERNAL_KEY +#endif /* ifdef MANAGEMENT_DEF_AUTH */ -char *management_query_rsa_sig(struct management *man, const char *b64_data); +char *management_query_pk_sig(struct management *man, const char *b64_data, + const char *algorithm); char *management_query_cert(struct management *man, const char *cert_name); -#endif - static inline bool management_connected(const struct management *man) { @@ -583,17 +584,17 @@ management_bytes_in(struct management *man, const int size) #ifdef MANAGEMENT_DEF_AUTH +void man_bytecount_output_server(struct management *man, + const counter_type *bytes_in_total, + const counter_type *bytes_out_total, + struct man_def_auth_context *mdac); + static inline void management_bytes_server(struct management *man, const counter_type *bytes_in_total, const counter_type *bytes_out_total, struct man_def_auth_context *mdac) { - void man_bytecount_output_server(struct management *man, - const counter_type *bytes_in_total, - const counter_type *bytes_out_total, - struct man_def_auth_context *mdac); - if (man->connection.bytecount_update_seconds > 0 && now >= mdac->bytecount_last_update + man->connection.bytecount_update_seconds && (mdac->flags & (DAF_CONNECTION_ESTABLISHED|DAF_CONNECTION_CLOSED)) == DAF_CONNECTION_ESTABLISHED) diff --git a/src/openvpn/mbuf.h b/src/openvpn/mbuf.h index 4912c95..f37563d 100644 --- a/src/openvpn/mbuf.h +++ b/src/openvpn/mbuf.h @@ -96,11 +96,11 @@ mbuf_maximum_queued(const struct mbuf_set *ms) return (int) ms->max_queued; } +struct multi_instance *mbuf_peek_dowork(struct mbuf_set *ms); + static inline struct multi_instance * mbuf_peek(struct mbuf_set *ms) { - struct multi_instance *mbuf_peek_dowork(struct mbuf_set *ms); - if (mbuf_defined(ms)) { return mbuf_peek_dowork(ms); diff --git a/src/openvpn/memdbg.h b/src/openvpn/memdbg.h index 70c6365..6da9712 100644 --- a/src/openvpn/memdbg.h +++ b/src/openvpn/memdbg.h @@ -44,7 +44,7 @@ #ifdef USE_VALGRIND -#include "valgrind/memcheck.h" +#include <valgrind/memcheck.h> #define VALGRIND_MAKE_READABLE(addr, len) @@ -84,7 +84,7 @@ * #define INTERNAL_MEMORY_SPACE (1024 * 1024 * 50) */ -#include "dmalloc.h" +#include <dmalloc.h> #define openvpn_dmalloc(file, line, size) dmalloc_malloc((file), (line), (size), DMALLOC_FUNC_MALLOC, 0, 0) diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index 9c5e96e..1038b38 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -51,9 +51,6 @@ const char *iproute_path = IPROUTE_PATH; /* GLOBAL */ #endif -/* contains an SSEC_x value defined in misc.h */ -int script_security = SSEC_BUILT_IN; /* GLOBAL */ - /* * Set standard file descriptors to /dev/null */ @@ -99,695 +96,6 @@ save_inetd_socket_descriptor(void) } /* - * Generate an error message based on the status code returned by openvpn_execve(). - */ -const char * -system_error_message(int stat, struct gc_arena *gc) -{ - struct buffer out = alloc_buf_gc(256, gc); - - switch (stat) - { - case OPENVPN_EXECVE_NOT_ALLOWED: - buf_printf(&out, "disallowed by script-security setting"); - break; - -#ifdef _WIN32 - case OPENVPN_EXECVE_ERROR: - buf_printf(&out, "external program did not execute -- "); - /* fall through */ - - default: - buf_printf(&out, "returned error code %d", stat); - break; -#else /* ifdef _WIN32 */ - - case OPENVPN_EXECVE_ERROR: - buf_printf(&out, "external program fork failed"); - break; - - default: - if (!WIFEXITED(stat)) - { - buf_printf(&out, "external program did not exit normally"); - } - else - { - const int cmd_ret = WEXITSTATUS(stat); - if (!cmd_ret) - { - buf_printf(&out, "external program exited normally"); - } - else if (cmd_ret == OPENVPN_EXECVE_FAILURE) - { - buf_printf(&out, "could not execute external program"); - } - else - { - buf_printf(&out, "external program exited with error status: %d", cmd_ret); - } - } - break; -#endif /* ifdef _WIN32 */ - } - return (const char *)out.data; -} - -/* - * Wrapper around openvpn_execve - */ -bool -openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message) -{ - struct gc_arena gc = gc_new(); - const int stat = openvpn_execve(a, es, flags); - int ret = false; - - if (platform_system_ok(stat)) - { - ret = true; - } - else - { - if (error_message) - { - msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s", - error_message, - system_error_message(stat, &gc)); - } - } - gc_free(&gc); - return ret; -} - -bool -openvpn_execve_allowed(const unsigned int flags) -{ - if (flags & S_SCRIPT) - { - return script_security >= SSEC_SCRIPTS; - } - else - { - return script_security >= SSEC_BUILT_IN; - } -} - - -#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 - * assocated with formatting and parsing a command line. - * Returns the exit status of child, OPENVPN_EXECVE_NOT_ALLOWED if openvpn_execve_allowed() - * returns false, or OPENVPN_EXECVE_ERROR on other errors. - */ -int -openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags) -{ - struct gc_arena gc = gc_new(); - int ret = OPENVPN_EXECVE_ERROR; - static bool warn_shown = false; - - if (a && a->argv[0]) - { -#if defined(ENABLE_FEATURE_EXECVE) - if (openvpn_execve_allowed(flags)) - { - const char *cmd = a->argv[0]; - char *const *argv = a->argv; - char *const *envp = (char *const *)make_env_array(es, true, &gc); - pid_t pid; - - pid = fork(); - if (pid == (pid_t)0) /* child side */ - { - execve(cmd, argv, envp); - exit(OPENVPN_EXECVE_FAILURE); - } - else if (pid < (pid_t)0) /* fork failed */ - { - msg(M_ERR, "openvpn_execve: unable to fork"); - } - else /* parent side */ - { - if (waitpid(pid, &ret, 0) != pid) - { - ret = OPENVPN_EXECVE_ERROR; - } - } - } - else - { - ret = OPENVPN_EXECVE_NOT_ALLOWED; - if (!warn_shown && (script_security < SSEC_SCRIPTS)) - { - msg(M_WARN, SCRIPT_SECURITY_WARNING); - warn_shown = true; - } - } -#else /* if defined(ENABLE_FEATURE_EXECVE) */ - msg(M_WARN, "openvpn_execve: execve function not available"); -#endif /* if defined(ENABLE_FEATURE_EXECVE) */ - } - else - { - msg(M_FATAL, "openvpn_execve: called with empty argv"); - } - - gc_free(&gc); - return ret; -} -#endif /* ifndef _WIN32 */ - -/* - * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but - * in a safer way that doesn't require the invocation of a shell or the risks - * assocated with formatting and parsing a command line. - */ -int -openvpn_popen(const struct argv *a, const struct env_set *es) -{ - struct gc_arena gc = gc_new(); - int ret = -1; - static bool warn_shown = false; - - if (a && a->argv[0]) - { -#if defined(ENABLE_FEATURE_EXECVE) - if (script_security >= SSEC_BUILT_IN) - { - const char *cmd = a->argv[0]; - char *const *argv = a->argv; - char *const *envp = (char *const *)make_env_array(es, true, &gc); - pid_t pid; - int pipe_stdout[2]; - - if (pipe(pipe_stdout) == 0) - { - pid = fork(); - if (pid == (pid_t)0) /* child side */ - { - close(pipe_stdout[0]); /* Close read end */ - dup2(pipe_stdout[1],1); - execve(cmd, argv, envp); - exit(OPENVPN_EXECVE_FAILURE); - } - 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 for %s", cmd); - ret = -1; - } - } - else if (!warn_shown && (script_security < SSEC_SCRIPTS)) - { - msg(M_WARN, SCRIPT_SECURITY_WARNING); - warn_shown = true; - } -#else /* if defined(ENABLE_FEATURE_EXECVE) */ - msg(M_WARN, "openvpn_popen: execve function not available"); -#endif /* if defined(ENABLE_FEATURE_EXECVE) */ - } - else - { - msg(M_FATAL, "openvpn_popen: called with empty argv"); - } - - gc_free(&gc); - return ret; -} - - - -/* - * Set environmental variable (int or string). - * - * On Posix, we use putenv for portability, - * and put up with its painful semantics - * that require all the support code below. - */ - -/* General-purpose environmental variable set functions */ - -static char * -construct_name_value(const char *name, const char *value, struct gc_arena *gc) -{ - struct buffer out; - - ASSERT(name); - if (!value) - { - value = ""; - } - out = alloc_buf_gc(strlen(name) + strlen(value) + 2, gc); - buf_printf(&out, "%s=%s", name, value); - return BSTR(&out); -} - -static bool -env_string_equal(const char *s1, const char *s2) -{ - int c1, c2; - ASSERT(s1); - ASSERT(s2); - - while (true) - { - c1 = *s1++; - c2 = *s2++; - if (c1 == '=') - { - c1 = 0; - } - if (c2 == '=') - { - c2 = 0; - } - if (!c1 && !c2) - { - return true; - } - if (c1 != c2) - { - break; - } - } - return false; -} - -static bool -remove_env_item(const char *str, const bool do_free, struct env_item **list) -{ - struct env_item *current, *prev; - - ASSERT(str); - ASSERT(list); - - for (current = *list, prev = NULL; current != NULL; current = current->next) - { - if (env_string_equal(current->string, str)) - { - if (prev) - { - prev->next = current->next; - } - else - { - *list = current->next; - } - if (do_free) - { - secure_memzero(current->string, strlen(current->string)); - free(current->string); - free(current); - } - return true; - } - prev = current; - } - return false; -} - -static void -add_env_item(char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc) -{ - struct env_item *item; - - ASSERT(str); - ASSERT(list); - - ALLOC_OBJ_GC(item, struct env_item, gc); - item->string = do_alloc ? string_alloc(str, gc) : str; - item->next = *list; - *list = item; -} - -/* struct env_set functions */ - -static bool -env_set_del_nolock(struct env_set *es, const char *str) -{ - return remove_env_item(str, es->gc == NULL, &es->list); -} - -static void -env_set_add_nolock(struct env_set *es, const char *str) -{ - remove_env_item(str, es->gc == NULL, &es->list); - add_env_item((char *)str, true, &es->list, es->gc); -} - -struct env_set * -env_set_create(struct gc_arena *gc) -{ - struct env_set *es; - ALLOC_OBJ_CLEAR_GC(es, struct env_set, gc); - es->list = NULL; - es->gc = gc; - return es; -} - -void -env_set_destroy(struct env_set *es) -{ - if (es && es->gc == NULL) - { - struct env_item *e = es->list; - while (e) - { - struct env_item *next = e->next; - free(e->string); - free(e); - e = next; - } - free(es); - } -} - -bool -env_set_del(struct env_set *es, const char *str) -{ - bool ret; - ASSERT(es); - ASSERT(str); - ret = env_set_del_nolock(es, str); - return ret; -} - -void -env_set_add(struct env_set *es, const char *str) -{ - ASSERT(es); - ASSERT(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) -{ - if (check_debug_level(msglevel)) - { - const struct env_item *e; - int i; - - if (es) - { - e = es->list; - i = 0; - - while (e) - { - if (env_safe_to_print(e->string)) - { - msg(msglevel, "ENV [%d] '%s'", i, e->string); - } - ++i; - e = e->next; - } - } - } -} - -void -env_set_inherit(struct env_set *es, const struct env_set *src) -{ - const struct env_item *e; - - ASSERT(es); - - if (src) - { - e = src->list; - while (e) - { - env_set_add_nolock(es, e->string); - e = e->next; - } - } -} - - -/* add/modify/delete environmental strings */ - -void -setenv_counter(struct env_set *es, const char *name, counter_type value) -{ - char buf[64]; - openvpn_snprintf(buf, sizeof(buf), counter_format, value); - setenv_str(es, name, buf); -} - -void -setenv_int(struct env_set *es, const char *name, int value) -{ - char buf[64]; - openvpn_snprintf(buf, sizeof(buf), "%d", value); - setenv_str(es, name, buf); -} - -void -setenv_unsigned(struct env_set *es, const char *name, unsigned int value) -{ - char buf[64]; - openvpn_snprintf(buf, sizeof(buf), "%u", value); - setenv_str(es, name, buf); -} - -void -setenv_str(struct env_set *es, const char *name, const char *value) -{ - setenv_str_ex(es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0); -} - -void -setenv_str_safe(struct env_set *es, const char *name, const char *value) -{ - uint8_t b[64]; - struct buffer buf; - buf_set_write(&buf, b, sizeof(b)); - if (buf_printf(&buf, "OPENVPN_%s", name)) - { - setenv_str(es, BSTR(&buf), value); - } - else - { - 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) -{ - ASSERT(name); - setenv_str(es, name, NULL); -} - -void -setenv_str_ex(struct env_set *es, - const char *name, - const char *value, - const unsigned int name_include, - const unsigned int name_exclude, - const char name_replace, - const unsigned int value_include, - const unsigned int value_exclude, - const char value_replace) -{ - struct gc_arena gc = gc_new(); - const char *name_tmp; - const char *val_tmp = NULL; - - ASSERT(name && strlen(name) > 1); - - name_tmp = string_mod_const(name, name_include, name_exclude, name_replace, &gc); - - if (value) - { - val_tmp = string_mod_const(value, value_include, value_exclude, value_replace, &gc); - } - - ASSERT(es); - - if (val_tmp) - { - const char *str = construct_name_value(name_tmp, val_tmp, &gc); - env_set_add(es, str); -#if DEBUG_VERBOSE_SETENV - msg(M_INFO, "SETENV_ES '%s'", str); -#endif - } - else - { - env_set_del(es, name_tmp); - } - - gc_free(&gc); -} - -/* - * Setenv functions that append an integer index to the name - */ -static const char * -setenv_format_indexed_name(const char *name, const int i, struct gc_arena *gc) -{ - struct buffer out = alloc_buf_gc(strlen(name) + 16, gc); - if (i >= 0) - { - buf_printf(&out, "%s_%d", name, i); - } - else - { - buf_printf(&out, "%s", name); - } - return BSTR(&out); -} - -void -setenv_int_i(struct env_set *es, const char *name, const int value, const int i) -{ - struct gc_arena gc = gc_new(); - const char *name_str = setenv_format_indexed_name(name, i, &gc); - setenv_int(es, name_str, value); - gc_free(&gc); -} - -void -setenv_str_i(struct env_set *es, const char *name, const char *value, const int i) -{ - struct gc_arena gc = gc_new(); - const char *name_str = setenv_format_indexed_name(name, i, &gc); - setenv_str(es, name_str, value); - gc_free(&gc); -} - -/* return true if filename can be opened for read */ -bool -test_file(const char *filename) -{ - bool ret = false; - if (filename) - { - FILE *fp = platform_fopen(filename, "r"); - if (fp) - { - fclose(fp); - ret = true; - } - else - { - if (openvpn_errno() == EACCES) - { - msg( M_WARN | M_ERRNO, "Could not access file '%s'", filename); - } - } - } - - dmsg(D_TEST_FILE, "TEST FILE '%s' [%d]", - filename ? filename : "UNDEF", - ret); - - return ret; -} - -/* create a temporary filename in directory */ -const char * -create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc) -{ - static unsigned int counter; - struct buffer fname = alloc_buf_gc(256, gc); - int fd; - const char *retfname = NULL; - unsigned int attempts = 0; - - do - { - ++attempts; - ++counter; - - buf_printf(&fname, PACKAGE "_%s_%08lx%08lx.tmp", prefix, - (unsigned long) get_random(), (unsigned long) get_random()); - - retfname = gen_path(directory, BSTR(&fname), gc); - if (!retfname) - { - msg(M_WARN, "Failed to create temporary filename and path"); - return NULL; - } - - /* Atomically create the file. Errors out if the file already - * exists. */ - fd = platform_open(retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); - if (fd != -1) - { - close(fd); - return retfname; - } - else if (fd == -1 && errno != EEXIST) - { - /* Something else went wrong, no need to retry. */ - msg(M_WARN | M_ERRNO, "Could not create temporary file '%s'", - retfname); - return NULL; - } - } - while (attempts < 6); - - msg(M_WARN, "Failed to create temporary file after %i attempts", attempts); - return NULL; -} - -#ifdef ENABLE_CRYPTO - -/* * Prepend a random string to hostname to prevent DNS caching. * For example, foo.bar.gov would be modified to <random-chars>.foo.bar.gov. * Of course, this requires explicit support in the DNS server (wildcard). @@ -808,80 +116,7 @@ hostname_randomize(const char *hostname, struct gc_arena *gc) #undef n_rnd_bytes } -#else /* ifdef ENABLE_CRYPTO */ - -const char * -hostname_randomize(const char *hostname, struct gc_arena *gc) -{ - msg(M_WARN, "WARNING: hostname randomization disabled when crypto support is not compiled"); - return hostname; -} - -#endif /* ifdef ENABLE_CRYPTO */ - -/* - * Put a directory and filename together. - */ -const char * -gen_path(const char *directory, const char *filename, struct gc_arena *gc) -{ -#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 - const int CC_PATH_RESERVED = CC_SLASH; -#endif - const char *safe_filename = string_mod_const(filename, CC_PRINT, CC_PATH_RESERVED, '_', gc); - - if (safe_filename - && strcmp(safe_filename, ".") - && strcmp(safe_filename, "..") -#ifdef _WIN32 - && win_safe_filename(safe_filename) -#endif - ) - { - const size_t outsize = strlen(safe_filename) + (directory ? strlen(directory) : 0) + 16; - struct buffer out = alloc_buf_gc(outsize, gc); - char dirsep[2]; - - dirsep[0] = OS_SPECIFIC_DIRSEP; - dirsep[1] = '\0'; - - if (directory) - { - buf_printf(&out, "%s%s", directory, dirsep); - } - buf_printf(&out, "%s", safe_filename); - - return BSTR(&out); - } - else - { - return NULL; - } -} - -bool -absolute_pathname(const char *pathname) -{ - if (pathname) - { - const int c = pathname[0]; -#ifdef _WIN32 - return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\'); -#else - return c == '/'; -#endif - } - else - { - return false; - } -} - #ifdef ENABLE_MANAGEMENT - /* Get username/password from the management interface */ static bool auth_user_pass_mgmt(struct user_pass *up, const char *prefix, const unsigned int flags, @@ -894,13 +129,10 @@ auth_user_pass_mgmt(struct user_pass *up, const char *prefix, const unsigned int management_auth_failure(management, prefix, "previous auth credentials failed"); } -#ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE)) { sc = auth_challenge; } -#endif - if (!management_query_user_pass(management, up, prefix, flags, sc)) { if ((flags & GET_USER_PASS_NOFATAL) != 0) @@ -914,7 +146,6 @@ auth_user_pass_mgmt(struct user_pass *up, const char *prefix, const unsigned int } return true; } - #endif /* ifdef ENABLE_MANAGEMENT */ /* @@ -1069,7 +300,7 @@ get_user_pass_cr(struct user_pass *up, */ if (username_from_stdin || password_from_stdin || response_from_stdin) { -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE) && response_from_stdin) { struct auth_challenge_info *ac = get_auth_challenge(auth_challenge, &gc); @@ -1096,7 +327,7 @@ get_user_pass_cr(struct user_pass *up, } } else -#endif /* ifdef ENABLE_CLIENT_CR */ +#endif /* ifdef ENABLE_MANAGEMENT */ { struct buffer user_prompt = alloc_buf_gc(128, &gc); struct buffer pass_prompt = alloc_buf_gc(128, &gc); @@ -1130,7 +361,7 @@ get_user_pass_cr(struct user_pass *up, } } -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE) && response_from_stdin) { char *response = (char *) gc_malloc(USER_PASS_LEN, false, &gc); @@ -1158,7 +389,7 @@ get_user_pass_cr(struct user_pass *up, string_clear(resp64); free(resp64); } -#endif /* ifdef ENABLE_CLIENT_CR */ +#endif /* ifdef ENABLE_MANAGEMENT */ } } @@ -1177,7 +408,7 @@ get_user_pass_cr(struct user_pass *up, return true; } -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT /* * See management/management-notes.txt for more info on the @@ -1252,52 +483,7 @@ get_auth_challenge(const char *auth_challenge, struct gc_arena *gc) } } -#endif /* ifdef ENABLE_CLIENT_CR */ - -#if AUTO_USERID - -void -get_user_pass_auto_userid(struct user_pass *up, const char *tag) -{ - struct gc_arena gc = gc_new(); - struct buffer buf; - uint8_t macaddr[6]; - static uint8_t digest [MD5_DIGEST_LENGTH]; - static const uint8_t hashprefix[] = "AUTO_USERID_DIGEST"; - - const md_kt_t *md5_kt = md_kt_get("MD5"); - md_ctx_t *ctx; - - CLEAR(*up); - buf_set_write(&buf, (uint8_t *)up->username, USER_PASS_LEN); - buf_printf(&buf, "%s", TARGET_PREFIX); - if (get_default_gateway_mac_addr(macaddr)) - { - dmsg(D_AUTO_USERID, "GUPAU: macaddr=%s", format_hex_ex(macaddr, sizeof(macaddr), 0, 1, ":", &gc)); - ctx = md_ctx_new(); - md_ctx_init(ctx, md5_kt); - md_ctx_update(ctx, hashprefix, sizeof(hashprefix) - 1); - md_ctx_update(ctx, macaddr, sizeof(macaddr)); - md_ctx_final(ctx, digest); - md_ctx_cleanup(ctx); - md_ctx_free(ctx); - buf_printf(&buf, "%s", format_hex_ex(digest, sizeof(digest), 0, 256, " ", &gc)); - } - else - { - buf_printf(&buf, "UNKNOWN"); - } - if (tag && strcmp(tag, "stdin")) - { - buf_printf(&buf, "-%s", tag); - } - up->defined = true; - gc_free(&gc); - - dmsg(D_AUTO_USERID, "GUPAU: AUTO_USERID: '%s'", up->username); -} - -#endif /* if AUTO_USERID */ +#endif /* ifdef ENABLE_MANAGEMENT */ void purge_user_pass(struct user_pass *up, const bool force) @@ -1347,71 +533,6 @@ safe_print(const char *str, struct gc_arena *gc) return string_mod_const(str, CC_PRINT, CC_CRLF, '.', gc); } -static bool -is_password_env_var(const char *str) -{ - return (strncmp(str, "password", 8) == 0); -} - -bool -env_allowed(const char *str) -{ - return (script_security >= SSEC_PW_ENV || !is_password_env_var(str)); -} - -bool -env_safe_to_print(const char *str) -{ -#ifndef UNSAFE_DEBUG - if (is_password_env_var(str)) - { - return false; - } -#endif - return true; -} - -/* Make arrays of strings */ - -const char ** -make_env_array(const struct env_set *es, - const bool check_allowed, - struct gc_arena *gc) -{ - char **ret = NULL; - struct env_item *e = NULL; - int i = 0, n = 0; - - /* figure length of es */ - if (es) - { - for (e = es->list; e != NULL; e = e->next) - { - ++n; - } - } - - /* alloc return array */ - ALLOC_ARRAY_CLEAR_GC(ret, char *, n+1, gc); - - /* fill return array */ - if (es) - { - i = 0; - for (e = es->list; e != NULL; e = e->next) - { - if (!check_allowed || env_allowed(e->string)) - { - ASSERT(i < n); - ret[i++] = e->string; - } - } - } - - ret[i] = NULL; - return (const char **)ret; -} - const char ** make_arg_array(const char *first, const char *parms, struct gc_arena *gc) { @@ -1490,12 +611,12 @@ make_arg_copy(char **p, struct gc_arena *gc) } const char ** -make_extended_arg_array(char **p, struct gc_arena *gc) +make_extended_arg_array(char **p, bool is_inline, struct gc_arena *gc) { const int argc = string_array_len((const char **)p); - if (!strcmp(p[0], INLINE_FILE_TAG) && argc == 2) + if (is_inline) { - return make_inline_array(p[1], gc); + return make_inline_array(p[0], gc); } else if (argc == 0) { @@ -1579,31 +700,6 @@ sanitize_control_message(const char *src, struct gc_arena *gc) return ret; } -/** - * Will set or query for a global compat flag. To modify the compat flags - * the COMPAT_FLAG_SET must be bitwise ORed together with the flag to set. - * If no "operator" flag is given it defaults to COMPAT_FLAG_QUERY, - * which returns the flag state. - * - * @param flag Flag to be set/queried for bitwise ORed with the operator flag - * @return Returns 0 if the flag is not set, otherwise the 'flag' value is returned - */ -bool -compat_flag(unsigned int flag) -{ - static unsigned int compat_flags = 0; - - if (flag & COMPAT_FLAG_SET) - { - compat_flags |= (flag >> 1); - } - - 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 */ @@ -1667,4 +763,22 @@ output_peer_info_env(struct env_set *es, const char *peer_info) } } -#endif /* P2MP_SERVER */ +int +get_num_elements(const char *string, char delimiter) +{ + int string_len = strlen(string); + + ASSERT(0 != string_len); + + int element_count = 1; + /* Get number of ciphers */ + for (int i = 0; i < string_len; i++) + { + if (string[i] == delimiter) + { + element_count++; + } + } + + return element_count; +} diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h index 8a34f43..a03d94e 100644 --- a/src/openvpn/misc.h +++ b/src/openvpn/misc.h @@ -27,6 +27,7 @@ #include "argv.h" #include "basic.h" #include "common.h" +#include "env_set.h" #include "integer.h" #include "buffer.h" #include "platform.h" @@ -37,49 +38,6 @@ /* forward declarations */ struct plugin_list; -/* - * Handle environmental variable lists - */ - -struct env_item { - char *string; - struct env_item *next; -}; - -struct env_set { - struct gc_arena *gc; - struct env_item *list; -}; - -/* system flags */ -#define S_SCRIPT (1<<0) -#define S_FATAL (1<<1) - -const char *system_error_message(int, struct gc_arena *gc); - -/* openvpn_execve return codes */ -#define OPENVPN_EXECVE_ERROR -1 /* generic error while forking to run an external program */ -#define OPENVPN_EXECVE_NOT_ALLOWED -2 /* external program not run due to script security */ -#define OPENVPN_EXECVE_FAILURE 127 /* exit code passed back from child when execve fails */ - -/* wrapper around the execve() call */ -int openvpn_popen(const struct argv *a, const struct env_set *es); - -int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags); - -bool openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message); - -bool openvpn_execve_allowed(const unsigned int flags); - -static inline bool -openvpn_run_script(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *hook) -{ - char msg[256]; - - openvpn_snprintf(msg, sizeof(msg), "WARNING: Failed running command (%s)", hook); - return openvpn_execve_check(a, es, flags | S_SCRIPT, msg); -} - /* Set standard file descriptors to /dev/null */ void set_std_files_to_null(bool stdin_only); @@ -88,86 +46,14 @@ void set_std_files_to_null(bool stdin_only); extern int inetd_socket_descriptor; void save_inetd_socket_descriptor(void); -/* set/delete environmental variable */ -void setenv_str_ex(struct env_set *es, - const char *name, - const char *value, - const unsigned int name_include, - const unsigned int name_exclude, - const char name_replace, - const unsigned int value_include, - const unsigned int value_exclude, - const char value_replace); - -void setenv_counter(struct env_set *es, const char *name, counter_type value); - -void setenv_int(struct env_set *es, const char *name, int value); - -void setenv_unsigned(struct env_set *es, const char *name, unsigned int value); - -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); - -/* struct env_set functions */ - -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); - -void env_set_inherit(struct env_set *es, const struct env_set *src); - /* Make arrays of strings */ -const char **make_env_array(const struct env_set *es, - const bool check_allowed, - struct gc_arena *gc); - 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); - -/* an analogue to the random() function, but use OpenSSL functions if available */ -#ifdef ENABLE_CRYPTO -long int get_random(void); - -#else -#define get_random random -#endif - -/* return true if filename can be opened for read */ -bool test_file(const char *filename); +const char **make_extended_arg_array(char **p, bool is_inline, + struct gc_arena *gc); -/* create a temporary file in directory, returns the filename of the created file */ -const char *create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc); - -/* put a directory and filename together */ -const char *gen_path(const char *directory, const char *filename, struct gc_arena *gc); - -/* return true if pathname is absolute */ -bool absolute_pathname(const char *pathname); - -/* prepend a random prefix to hostname (need ENABLE_CRYPTO) */ +/* prepend a random prefix to hostname */ const char *hostname_randomize(const char *hostname, struct gc_arena *gc); /* @@ -190,7 +76,7 @@ struct user_pass char password[USER_PASS_LEN]; }; -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT /* * Challenge response info on client as pushed by server. */ @@ -216,10 +102,10 @@ struct static_challenge_info { const char *challenge_text; }; -#else /* ifdef ENABLE_CLIENT_CR */ +#else /* ifdef ENABLE_MANAGEMENT */ struct auth_challenge_info {}; struct static_challenge_info {}; -#endif /* ifdef ENABLE_CLIENT_CR */ +#endif /* ifdef ENABLE_MANAGEMENT */ /* * Flags for get_user_pass and management_query_user_pass @@ -269,21 +155,11 @@ void set_auth_token(struct user_pass *up, struct user_pass *tk, */ const char *safe_print(const char *str, struct gc_arena *gc); -/* returns true if environmental variable safe to print to log */ -bool env_safe_to_print(const char *str); - -/* returns true if environmental variable may be passed to an external program */ -bool env_allowed(const char *str); void configure_path(void); const char *sanitize_control_message(const char *str, struct gc_arena *gc); -#if AUTO_USERID -void get_user_pass_auto_userid(struct user_pass *up, const char *tag); - -#endif - /* * /sbin/ip path, may be overridden */ @@ -291,27 +167,24 @@ void get_user_pass_auto_userid(struct user_pass *up, const char *tag); extern const char *iproute_path; #endif -/* Script security */ -#define SSEC_NONE 0 /* strictly no calling of external programs */ -#define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/ -#define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */ -#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */ -extern int script_security; /* GLOBAL */ - - -#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 */ +/** + * Returns the occurrences of 'delimiter' in a string +1 + * This is typically used to find out the number elements in a + * cipher string or similar that is separated by : like + * + * X25519:secp256r1:X448:secp512r1:secp384r1:brainpoolP384r1 + * + * @param string the string to work on + * @param delimiter the delimiter to count, typically ':' + * @return occrrences of delimiter + 1 + */ +int +get_num_elements(const char *string, char delimiter); #endif /* ifndef MISC_H */ diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c index db8c987..793c7e3 100644 --- a/src/openvpn/mroute.c +++ b/src/openvpn/mroute.c @@ -29,7 +29,6 @@ #include "syshead.h" -#if P2MP_SERVER #include "mroute.h" #include "proto.h" @@ -58,7 +57,7 @@ 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->eth_addr); + && is_mac_mcast_addr(addr->ether.addr); } /* @@ -247,11 +246,25 @@ mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest, return ret; } +static void +mroute_copy_ether_to_addr(struct mroute_addr *maddr, + const uint8_t *ether_addr, + uint16_t vid) +{ + maddr->type = MR_ADDR_ETHER; + maddr->netbits = 0; + maddr->len = OPENVPN_ETH_ALEN; + memcpy(maddr->ether.addr, ether_addr, OPENVPN_ETH_ALEN); + maddr->len += sizeof(vid); + maddr->ether.vid = vid; +} + unsigned int mroute_extract_addr_ether(struct mroute_addr *src, struct mroute_addr *dest, struct mroute_addr *esrc, struct mroute_addr *edest, + uint16_t vid, const struct buffer *buf) { unsigned int ret = 0; @@ -260,17 +273,11 @@ mroute_extract_addr_ether(struct mroute_addr *src, const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR(buf); if (src) { - src->type = MR_ADDR_ETHER; - src->netbits = 0; - src->len = 6; - memcpy(src->eth_addr, eth->source, sizeof(dest->eth_addr)); + mroute_copy_ether_to_addr(src, eth->source, vid); } if (dest) { - dest->type = MR_ADDR_ETHER; - dest->netbits = 0; - dest->len = 6; - memcpy(dest->eth_addr, eth->dest, sizeof(dest->eth_addr)); + mroute_copy_ether_to_addr(dest, eth->dest, vid); /* ethernet broadcast/multicast packet? */ if (is_mac_mcast_addr(eth->dest)) @@ -285,21 +292,38 @@ mroute_extract_addr_ether(struct mroute_addr *src, if (esrc || edest) { struct buffer b = *buf; - if (buf_advance(&b, sizeof(struct openvpn_ethhdr))) + if (!buf_advance(&b, sizeof(struct openvpn_ethhdr))) { - switch (ntohs(eth->proto)) - { - case OPENVPN_ETH_P_IPV4: - ret |= (mroute_extract_addr_ip(esrc, edest, &b) << MROUTE_SEC_SHIFT); - break; + return 0; + } - case OPENVPN_ETH_P_ARP: - ret |= (mroute_extract_addr_arp(esrc, edest, &b) << MROUTE_SEC_SHIFT); - break; + uint16_t proto = eth->proto; + if (proto == htons(OPENVPN_ETH_P_8021Q)) + { + if (!buf_advance(&b, SIZE_ETH_TO_8021Q_HDR)) + { + /* It's an 802.1Q packet, but doesn't have a full header, + * so something went wrong */ + return 0; } + + const struct openvpn_8021qhdr *tag; + tag = (const struct openvpn_8021qhdr *)BPTR(buf); + proto = tag->proto; + } + + switch (ntohs(proto)) + { + case OPENVPN_ETH_P_IPV4: + ret |= (mroute_extract_addr_ip(esrc, edest, &b) << MROUTE_SEC_SHIFT); + break; + + case OPENVPN_ETH_P_ARP: + ret |= (mroute_extract_addr_arp(esrc, edest, &b) << MROUTE_SEC_SHIFT); + break; } } -#endif +#endif /* ifdef ENABLE_PF */ } return ret; } @@ -440,8 +464,9 @@ 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->eth_addr, - sizeof(ma->eth_addr), 0, 1, ":", gc)); + buf_printf(&out, "%s", format_hex_ex(ma->ether.addr, + sizeof(ma->ether.addr), 0, 1, ":", gc)); + buf_printf(&out, "@%hu", ma->ether.vid); break; case MR_ADDR_IPV4: @@ -588,10 +613,3 @@ mroute_helper_free(struct mroute_helper *mh) { free(mh); } - -#else /* if P2MP_SERVER */ -static void -dummy(void) -{ -} -#endif /* P2MP_SERVER */ diff --git a/src/openvpn/mroute.h b/src/openvpn/mroute.h index 1063a18..c94b132 100644 --- a/src/openvpn/mroute.h +++ b/src/openvpn/mroute.h @@ -24,8 +24,6 @@ #ifndef MROUTE_H #define MROUTE_H -#if P2MP_SERVER - #include "buffer.h" #include "list.h" #include "route.h" @@ -82,7 +80,10 @@ struct mroute_addr { * valid if MR_WITH_NETBITS is set */ union { uint8_t raw_addr[MR_MAX_ADDR_LEN]; /* actual address */ - uint8_t eth_addr[OPENVPN_ETH_ALEN]; + struct { + uint8_t addr[OPENVPN_ETH_ALEN]; + uint16_t vid; + } ether; struct { in_addr_t addr; /* _network order_ IPv4 address */ in_port_t port; /* _network order_ TCP/UDP port */ @@ -100,7 +101,7 @@ struct mroute_addr { /* 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 ether mroute_union.ether #define v4 mroute_union.v4 #define v6 mroute_union.v6 #define v4mappedv6 mroute_union.v4mappedv6 @@ -170,6 +171,17 @@ void mroute_helper_add_iroute46(struct mroute_helper *mh, int netbits); void mroute_helper_del_iroute46(struct mroute_helper *mh, int netbits); +unsigned int mroute_extract_addr_ip(struct mroute_addr *src, + struct mroute_addr *dest, + const struct buffer *buf); + +unsigned int mroute_extract_addr_ether(struct mroute_addr *src, + struct mroute_addr *dest, + struct mroute_addr *esrc, + struct mroute_addr *edest, + uint16_t vid, + const struct buffer *buf); + /* * Given a raw packet in buf, return the src and dest * addresses of the packet. @@ -179,19 +191,10 @@ mroute_extract_addr_from_packet(struct mroute_addr *src, struct mroute_addr *dest, struct mroute_addr *esrc, struct mroute_addr *edest, + uint16_t vid, const struct buffer *buf, int tunnel_type) { - unsigned int mroute_extract_addr_ip(struct mroute_addr *src, - struct mroute_addr *dest, - const struct buffer *buf); - - unsigned int mroute_extract_addr_ether(struct mroute_addr *src, - struct mroute_addr *dest, - struct mroute_addr *esrc, - struct mroute_addr *edest, - const struct buffer *buf); - unsigned int ret = 0; verify_align_4(buf); if (tunnel_type == DEV_TYPE_TUN) @@ -200,7 +203,7 @@ mroute_extract_addr_from_packet(struct mroute_addr *src, } else if (tunnel_type == DEV_TYPE_TAP) { - ret = mroute_extract_addr_ether(src, dest, esrc, edest, buf); + ret = mroute_extract_addr_ether(src, dest, esrc, edest, vid, buf); } return ret; } @@ -265,5 +268,4 @@ mroute_addr_reset(struct mroute_addr *ma) ma->type = MR_ADDR_NONE; } -#endif /* P2MP_SERVER */ #endif /* MROUTE_H */ diff --git a/src/openvpn/mss.c b/src/openvpn/mss.c index facdf7b..f15c656 100644 --- a/src/openvpn/mss.c +++ b/src/openvpn/mss.c @@ -110,7 +110,7 @@ mss_fixup_ipv6(struct buffer *buf, int maxmss) * before the final header (TCP, UDP, ...), so we'd need to walk that * chain (see RFC 2460 and RFC 6564 for details). * - * In practice, "most typically used" extention headers (AH, routing, + * In practice, "most typically used" extension headers (AH, routing, * fragment, mobility) are very unlikely to be seen inside an OpenVPN * tun, so for now, we only handle the case of "single next header = TCP" */ @@ -150,7 +150,7 @@ mss_fixup_dowork(struct buffer *buf, uint16_t maxmss) if (BLEN(buf) < (int) sizeof(struct openvpn_tcphdr)) { - return; + return; } verify_align_4(buf); diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c index e8d2add..458e6e4 100644 --- a/src/openvpn/mtcp.c +++ b/src/openvpn/mtcp.c @@ -29,10 +29,8 @@ #include "syshead.h" -#if P2MP_SERVER - #include "multi.h" -#include "forward-inline.h" +#include "forward.h" #include "memdbg.h" @@ -269,8 +267,25 @@ multi_tcp_wait(const struct context *c, struct multi_tcp *mtcp) { int status; + unsigned int *persistent = &mtcp->tun_rwflags; socket_set_listen_persistent(c->c2.link_socket, mtcp->es, MTCP_SOCKET); - tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, &mtcp->tun_rwflags); + +#ifdef _WIN32 + if (tuntap_is_wintun(c->c1.tuntap)) + { + if (!tuntap_ring_empty(c->c1.tuntap)) + { + /* there is data in wintun ring buffer, read it immediately */ + mtcp->esr[0].arg = MTCP_TUN; + mtcp->esr[0].rwflags = EVENT_READ; + mtcp->n_esr = 1; + return 1; + } + persistent = NULL; + } +#endif + tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent); + #ifdef ENABLE_MANAGEMENT if (management) { @@ -844,5 +859,3 @@ tunnel_server_tcp(struct context *top) multi_top_free(&multi); close_instance(top); } - -#endif /* if P2MP_SERVER */ diff --git a/src/openvpn/mtcp.h b/src/openvpn/mtcp.h index bba455b..680ab10 100644 --- a/src/openvpn/mtcp.h +++ b/src/openvpn/mtcp.h @@ -28,8 +28,6 @@ #ifndef MTCP_H #define MTCP_H -#if P2MP_SERVER - #include "event.h" /* @@ -75,5 +73,4 @@ void tunnel_server_tcp(struct context *top); void multi_tcp_delete_event(struct multi_tcp *mtcp, event_t event); -#endif /* if P2MP_SERVER */ #endif /* ifndef MTCP_H */ diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c index 04868cd..3ddeac7 100644 --- a/src/openvpn/mtu.c +++ b/src/openvpn/mtu.c @@ -174,8 +174,8 @@ set_mtu_discover_type(int sd, int mtu_type, sa_family_t 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))) + if (setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, + (void *) &mtu_type, sizeof(mtu_type))) { msg(M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket", mtu_type); @@ -185,8 +185,8 @@ set_mtu_discover_type(int sd, int mtu_type, sa_family_t proto_af) #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))) + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, + (void *) &mtu_type, sizeof(mtu_type))) { msg(M_ERR, "Error setting IPV6_MTU_DISCOVER type=%d on TCP6/UDP6 socket", mtu_type); diff --git a/src/openvpn/mtu.h b/src/openvpn/mtu.h index cfa8d2f..549c319 100644 --- a/src/openvpn/mtu.h +++ b/src/openvpn/mtu.h @@ -45,7 +45,7 @@ * ifconfig $1 10.1.0.2 pointopoint 10.1.0.1 mtu 1450 * * Compression overflow bytes is the worst-case size expansion that would be - * expected if we tried to compress mtu + extra_frame bytes of uncompressible data. + * expected if we tried to compress mtu + extra_frame bytes of incompressible data. */ /* diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c index 4f63654..e95a7ac 100644 --- a/src/openvpn/mudp.c +++ b/src/openvpn/mudp.c @@ -29,11 +29,9 @@ #include "syshead.h" -#if P2MP_SERVER - #include "multi.h" #include <inttypes.h> -#include "forward-inline.h" +#include "forward.h" #include "memdbg.h" @@ -278,7 +276,12 @@ p2mp_iow_flags(const struct multi_context *m) { flags |= IOW_READ; } - +#ifdef _WIN32 + if (tuntap_ring_empty(m->top.c1.tuntap)) + { + flags &= ~IOW_READ_TUN; + } +#endif return flags; } @@ -379,4 +382,3 @@ tunnel_server_udp(struct context *top) tunnel_server_udp_single_threaded(top); } -#endif /* if P2MP_SERVER */ diff --git a/src/openvpn/mudp.h b/src/openvpn/mudp.h index 7e31151..460a768 100644 --- a/src/openvpn/mudp.h +++ b/src/openvpn/mudp.h @@ -28,8 +28,6 @@ #ifndef MUDP_H #define MUDP_H -#if P2MP_SERVER - struct context; struct multi_context; @@ -66,5 +64,4 @@ void tunnel_server_udp(struct context *top); */ struct multi_instance *multi_get_create_instance_udp(struct multi_context *m, bool *floated); -#endif -#endif +#endif /* ifndef MUDP_H */ diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index c8c9a40..1373818 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -34,21 +34,23 @@ #include "syshead.h" -#if P2MP_SERVER - +#include "forward.h" #include "multi.h" #include "push.h" -#include "misc.h" +#include "run_command.h" #include "otime.h" +#include "pf.h" #include "gremlin.h" #include "mstats.h" #include "ssl_verify.h" +#include "ssl_ncp.h" +#include "vlan.h" #include <inttypes.h> #include "memdbg.h" -#include "forward-inline.h" -#include "pf-inline.h" + +#include "crypto_backend.h" /*#define MULTI_DEBUG_EVENT_LOOP*/ @@ -136,7 +138,7 @@ learn_address_script(const struct multi_context *m, msg(M_WARN, "WARNING: learn-address plugin call failed"); ret = false; } - argv_reset(&argv); + argv_free(&argv); } if (m->top.options.learn_address_script) @@ -153,7 +155,7 @@ learn_address_script(const struct multi_context *m, { ret = false; } - argv_reset(&argv); + argv_free(&argv); } gc_free(&gc); @@ -386,7 +388,8 @@ multi_init(struct multi_context *m, struct context *t, bool tcp_mode, int thread * differently based on whether a tun or tap style * tunnel. */ - if (t->options.ifconfig_pool_defined) + if (t->options.ifconfig_pool_defined + || t->options.ifconfig_ipv6_pool_defined) { int pool_type = IFCONFIG_POOL_INDIV; @@ -395,7 +398,8 @@ multi_init(struct multi_context *m, struct context *t, bool tcp_mode, int thread pool_type = IFCONFIG_POOL_30NET; } - m->ifconfig_pool = ifconfig_pool_init(pool_type, + m->ifconfig_pool = ifconfig_pool_init(t->options.ifconfig_pool_defined, + pool_type, t->options.ifconfig_pool_start, t->options.ifconfig_pool_end, t->options.duplicate_cn, @@ -564,44 +568,36 @@ multi_client_disconnect_setenv(struct multi_instance *mi) setenv_stats(&mi->context); /* setenv connection duration */ - { - const unsigned int duration = (unsigned int) now - mi->created; - setenv_unsigned(mi->context.c2.es, "time_duration", duration); - } + setenv_long_long(mi->context.c2.es, "time_duration", now - mi->created); } static void multi_client_disconnect_script(struct multi_instance *mi) { - if ((mi->context.c2.context_auth == CAS_SUCCEEDED && mi->connection_established_flag) - || mi->context.c2.context_auth == CAS_PARTIAL) - { - multi_client_disconnect_setenv(mi); + multi_client_disconnect_setenv(mi); - if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) + if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) + { + if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) { - if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) - { - msg(M_WARN, "WARNING: client-disconnect plugin call failed"); - } + msg(M_WARN, "WARNING: client-disconnect plugin call failed"); } + } - if (mi->context.options.client_disconnect_script) - { - struct argv argv = argv_new(); - setenv_str(mi->context.c2.es, "script_type", "client-disconnect"); - argv_parse_cmd(&argv, mi->context.options.client_disconnect_script); - openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-disconnect"); - argv_reset(&argv); - } + if (mi->context.options.client_disconnect_script) + { + struct argv argv = argv_new(); + setenv_str(mi->context.c2.es, "script_type", "client-disconnect"); + argv_parse_cmd(&argv, mi->context.options.client_disconnect_script); + openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-disconnect"); + argv_free(&argv); + } #ifdef MANAGEMENT_DEF_AUTH - if (management) - { - management_notify_client_close(management, &mi->context.c2.mda_context, mi->context.c2.es); - } -#endif - + if (management) + { + management_notify_client_close(management, &mi->context.c2.mda_context, mi->context.c2.es); } +#endif } void @@ -682,14 +678,13 @@ multi_close_instance(struct multi_context *m, #ifdef MANAGEMENT_DEF_AUTH set_cc_config(mi, NULL); #endif - - multi_client_disconnect_script(mi); - - if (mi->did_open_context) + if (mi->context.c2.context_auth == CAS_SUCCEEDED) { - close_context(&mi->context, SIGTERM, CC_GC_FREE); + multi_client_disconnect_script(mi); } + close_context(&mi->context, SIGTERM, CC_GC_FREE); + multi_tcp_instance_specific_free(mi); ungenerate_prefix(mi); @@ -787,7 +782,6 @@ multi_create_instance(struct multi_context *m, const struct mroute_addr *real) generate_prefix(mi); } - mi->did_open_context = true; inherit_context_child(&mi->context, &m->top); if (IS_SIG(&mi->context)) { @@ -827,10 +821,8 @@ multi_create_instance(struct multi_context *m, const struct mroute_addr *real) mi->did_cid_hash = true; #endif - mi->context.c2.push_reply_deferred = true; - -#ifdef ENABLE_ASYNC_PUSH mi->context.c2.push_request_received = false; +#ifdef ENABLE_ASYNC_PUSH mi->inotify_watch = -1; #endif @@ -941,8 +933,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%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); + 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%cData Channel Cipher", + sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep); hash_iterator_init(m->hash, &hi); while ((he = hash_iterator_next(&hi))) { @@ -957,7 +949,7 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int #else "" #endif - "%c%" PRIu32, + "%c%" PRIu32 "%c%s", 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), @@ -972,7 +964,8 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int #else sep, #endif - sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX); + sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX, + sep, translate_cipher_name_to_openvpn(mi->context.options.ciphername)); } gc_free(&gc); } @@ -1495,41 +1488,47 @@ multi_select_virtual_addr(struct multi_context *m, struct multi_instance *mi) const int tunnel_topology = TUNNEL_TOPOLOGY(mi->context.c1.tuntap); msg( M_INFO, "MULTI_sva: pool returned IPv4=%s, IPv6=%s", - print_in_addr_t( remote, 0, &gc ), + (mi->context.options.ifconfig_pool_defined + ? print_in_addr_t(remote, 0, &gc) + : "(Not enabled)"), (mi->context.options.ifconfig_ipv6_pool_defined ? print_in6_addr( remote_ipv6, 0, &gc ) : "(Not enabled)") ); - /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */ - mi->context.c2.push_ifconfig_local = remote; - if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET)) + if (mi->context.options.ifconfig_pool_defined) { - mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.ifconfig_pool_netmask; - if (!mi->context.c2.push_ifconfig_remote_netmask) + /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */ + mi->context.c2.push_ifconfig_local = remote; + if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET)) { - mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->remote_netmask; + mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.ifconfig_pool_netmask; + if (!mi->context.c2.push_ifconfig_remote_netmask) + { + mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->remote_netmask; + } } - } - else if (tunnel_type == DEV_TYPE_TUN) - { - if (tunnel_topology == TOP_P2P) + else if (tunnel_type == DEV_TYPE_TUN) { - mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local; + if (tunnel_topology == TOP_P2P) + { + mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local; + } + else if (tunnel_topology == TOP_NET30) + { + mi->context.c2.push_ifconfig_remote_netmask = local; + } } - else if (tunnel_topology == TOP_NET30) + + if (mi->context.c2.push_ifconfig_remote_netmask) { - mi->context.c2.push_ifconfig_remote_netmask = local; + mi->context.c2.push_ifconfig_defined = true; + } + else + { + msg(D_MULTI_ERRORS, + "MULTI: no --ifconfig-pool netmask parameter is available to push to %s", + multi_instance_string(mi, false, &gc)); } - } - - if (mi->context.c2.push_ifconfig_remote_netmask) - { - mi->context.c2.push_ifconfig_defined = true; - } - else - { - msg(D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s", - multi_instance_string(mi, false, &gc)); } if (mi->context.options.ifconfig_ipv6_pool_defined) @@ -1636,16 +1635,15 @@ static void multi_client_connect_post(struct multi_context *m, struct multi_instance *mi, const char *dc_file, - unsigned int option_permissions_mask, unsigned int *option_types_found) { /* Did script generate a dynamic config file? */ - if (test_file(dc_file)) + if (platform_test_file(dc_file)) { options_server_import(&mi->context.options, dc_file, D_IMPORT_ERRORS|M_OPTERR, - option_permissions_mask, + CLIENT_CONNECT_OPT_MASK, option_types_found, mi->context.c2.es); @@ -1669,7 +1667,6 @@ static void multi_client_connect_post_plugin(struct multi_context *m, struct multi_instance *mi, const struct plugin_return *pr, - unsigned int option_permissions_mask, unsigned int *option_types_found) { struct plugin_return config; @@ -1687,7 +1684,7 @@ multi_client_connect_post_plugin(struct multi_context *m, options_string_import(&mi->context.options, config.list[i]->value, D_IMPORT_ERRORS|M_OPTERR, - option_permissions_mask, + CLIENT_CONNECT_OPT_MASK, option_types_found, mi->context.c2.es); } @@ -1706,29 +1703,30 @@ multi_client_connect_post_plugin(struct multi_context *m, #endif /* ifdef ENABLE_PLUGIN */ -#ifdef MANAGEMENT_DEF_AUTH /* * Called to load management-derived client-connect config */ -static void +enum client_connect_return multi_client_connect_mda(struct multi_context *m, struct multi_instance *mi, - const struct buffer_list *config, - unsigned int option_permissions_mask, + bool deferred, unsigned int *option_types_found) { - if (config) + /* We never return CC_RET_DEFERRED */ + ASSERT(!deferred); + enum client_connect_return ret = CC_RET_SKIPPED; +#ifdef MANAGEMENT_DEF_AUTH + if (mi->cc_config) { struct buffer_entry *be; - - for (be = config->head; be != NULL; be = be->next) + for (be = mi->cc_config->head; be != NULL; be = be->next) { const char *opt = BSTR(&be->buf); options_string_import(&mi->context.options, opt, D_IMPORT_ERRORS|M_OPTERR, - option_permissions_mask, + CLIENT_CONNECT_OPT_MASK, option_types_found, mi->context.c2.es); } @@ -1741,10 +1739,12 @@ multi_client_connect_mda(struct multi_context *m, */ multi_select_virtual_addr(m, mi); multi_set_virtual_addr_env(mi); - } -} + ret = CC_RET_SUCCEEDED; + } #endif /* ifdef MANAGEMENT_DEF_AUTH */ + return ret; +} static void multi_client_connect_setenv(struct multi_context *m, @@ -1765,350 +1765,951 @@ multi_client_connect_setenv(struct multi_context *m, { const char *created_ascii = time_string(mi->created, 0, false, &gc); setenv_str(mi->context.c2.es, "time_ascii", created_ascii); - setenv_unsigned(mi->context.c2.es, "time_unix", (unsigned int)mi->created); + setenv_long_long(mi->context.c2.es, "time_unix", mi->created); } gc_free(&gc); } -/* - * Called as soon as the SSL/TLS connection authenticates. +/** + * Extracts the IV_PROTO variable and returns its value or 0 + * if it cannot be extracted. * - * Instance-specific directives to be processed: - * - * iroute start-ip end-ip - * ifconfig-push local remote-netmask - * push */ -static void -multi_connection_established(struct multi_context *m, struct multi_instance *mi) +static unsigned int +extract_iv_proto(const char *peer_info) { - if (tls_authentication_status(mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED) + + const char *optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; + if (optstr) { - struct gc_arena gc = gc_new(); - unsigned int option_types_found = 0; + int proto = 0; + int r = sscanf(optstr, "IV_PROTO=%d", &proto); + if (r == 1 && proto > 0) + { + return proto; + } + } + return 0; +} - const unsigned int option_permissions_mask = - OPT_P_INSTANCE - | OPT_P_INHERIT - | OPT_P_PUSH - | OPT_P_TIMER - | OPT_P_CONFIG - | OPT_P_ECHO - | OPT_P_COMP - | OPT_P_SOCKFLAGS; +/** + * Calculates the options that depend on the client capabilities + * based on local options and available peer info + * - choosen cipher + * - peer id + */ +static bool +multi_client_set_protocol_options(struct context *c) +{ + struct tls_multi *tls_multi = c->c2.tls_multi; + const char *const peer_info = tls_multi->peer_info; + struct options *o = &c->options; - int cc_succeeded = true; /* client connect script status */ - int cc_succeeded_count = 0; - ASSERT(mi->context.c1.tuntap); + unsigned int proto = extract_iv_proto(peer_info); + if (proto & IV_PROTO_DATA_V2) + { + tls_multi->use_peer_id = true; + } + if (proto & IV_PROTO_REQUEST_PUSH) + { + c->c2.push_request_received = true; + } - /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */ - tls_lock_common_name(mi->context.c2.tls_multi); - tls_lock_cert_hash_set(mi->context.c2.tls_multi); + /* Select cipher if client supports Negotiable Crypto Parameters */ + if (!o->ncp_enabled) + { + return true; + } - /* generate a msg() prefix for this client instance */ - generate_prefix(mi); + /* if we have already created our key, we cannot *change* our own + * cipher -> so log the fact and push the "what we have now" cipher + * (so the client is always told what we expect it to use) + */ + 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, " + "re-sending previously negotiated cipher '%s'", + o->ciphername ); + return true; + } - /* delete instances of previous clients with same common-name */ - if (!mi->context.options.duplicate_cn) + /* + * Push the first cipher from --data-ciphers to the client that + * the client announces to be supporting. + */ + char *push_cipher = ncp_get_best_cipher(o->ncp_ciphers, peer_info, + tls_multi->remote_ciphername, + &o->gc); + + if (push_cipher) + { + o->ciphername = push_cipher; + return true; + } + + /* NCP cipher negotiation failed. Try to figure out why exactly it + * failed and give good error messages and potentially do a fallback + * for non NCP clients */ + struct gc_arena gc = gc_new(); + bool ret = false; + + const char *peer_ciphers = tls_peer_ncp_list(peer_info, &gc); + /* If we are in a situation where we know the client ciphers, there is no + * reason to fall back to a cipher that will not be accepted by the other + * side, in this situation we fail the auth*/ + if (strlen(peer_ciphers) > 0) + { + msg(M_INFO, "PUSH: No common cipher between server and client. " + "Server data-ciphers: '%s', client supported ciphers '%s'", + o->ncp_ciphers, peer_ciphers); + } + else if (tls_multi->remote_ciphername) + { + msg(M_INFO, "PUSH: No common cipher between server and client. " + "Server data-ciphers: '%s', client supports cipher '%s'", + o->ncp_ciphers, tls_multi->remote_ciphername); + } + else + { + msg(M_INFO, "PUSH: No NCP or OCC cipher data received from peer."); + + if (o->enable_ncp_fallback && !tls_multi->remote_ciphername) { - multi_delete_dup(m, mi); + msg(M_INFO, "Using data channel cipher '%s' since " + "--data-ciphers-fallback is set.", o->ciphername); + ret = true; } + else + { + msg(M_INFO, "Use --data-ciphers-fallback with the cipher the " + "client is using if you want to allow the client to connect"); + } + } + if (!ret) + { + auth_set_client_reason(tls_multi, "Data channel cipher negotiation " + "failed (no shared cipher)"); + } - /* reset pool handle to null */ - mi->vaddr_handle = -1; + gc_free(&gc); + return ret; +} - /* - * Try to source a dynamic config file from the - * --client-config-dir directory. - */ - if (mi->context.options.client_config_dir) - { - const char *ccd_file; +/** + * Delete the temporary file for the return value of client connect + * It also removes it from client_connect_defer_state and environment + */ +static void +ccs_delete_deferred_ret_file(struct multi_instance *mi) +{ + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + if (!ccs->deferred_ret_file) + { + return; + } + + setenv_del(mi->context.c2.es, "client_connect_deferred_file"); + if (!platform_unlink(ccs->deferred_ret_file)) + { + msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", + ccs->deferred_ret_file); + } + free(ccs->deferred_ret_file); + ccs->deferred_ret_file = NULL; +} - ccd_file = gen_path(mi->context.options.client_config_dir, - tls_common_name(mi->context.c2.tls_multi, false), - &gc); +/** + * Create a temporary file for the return value of client connect + * and puts it into the client_connect_defer_state and environment + * as "client_connect_deferred_file" + * + * @return boolean value if creation was successful + */ +static bool +ccs_gen_deferred_ret_file(struct multi_instance *mi) +{ + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + struct gc_arena gc = gc_new(); + const char *fn; - /* try common-name file */ - if (test_file(ccd_file)) + /* Delete file if it already exists */ + ccs_delete_deferred_ret_file(mi); + + fn = platform_create_temp_file(mi->context.options.tmp_dir, "ccr", &gc); + if (!fn) + { + gc_free(&gc); + return false; + } + ccs->deferred_ret_file = string_alloc(fn, NULL); + + setenv_str(mi->context.c2.es, "client_connect_deferred_file", + ccs->deferred_ret_file); + + gc_free(&gc); + return true; +} + +/** + * Tests whether the deferred return value file exists and returns the + * contained return value. + * + * @return CC_RET_SKIPPED if the file does not exist or is empty. + * CC_RET_DEFERRED, CC_RET_SUCCEEDED or CC_RET_FAILED depending on + * the value stored in the file. + */ +static enum client_connect_return +ccs_test_deferred_ret_file(struct multi_instance *mi) +{ + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + FILE *fp = fopen(ccs->deferred_ret_file, "r"); + if (!fp) + { + return CC_RET_SKIPPED; + } + + enum client_connect_return ret = CC_RET_SKIPPED; + const int c = fgetc(fp); + switch (c) + { + case '0': + ret = CC_RET_FAILED; + break; + + case '1': + ret = CC_RET_SUCCEEDED; + break; + + case '2': + ret = CC_RET_DEFERRED; + break; + + case EOF: + if (feof(fp)) { - options_server_import(&mi->context.options, - ccd_file, - D_IMPORT_ERRORS|M_OPTERR, - option_permissions_mask, - &option_types_found, - mi->context.c2.es); + ret = CC_RET_SKIPPED; + break; } - else /* try default file */ - { - ccd_file = gen_path(mi->context.options.client_config_dir, - CCD_DEFAULT, - &gc); - if (test_file(ccd_file)) - { - options_server_import(&mi->context.options, - ccd_file, - D_IMPORT_ERRORS|M_OPTERR, - option_permissions_mask, - &option_types_found, - mi->context.c2.es); - } - } + /* Not EOF but other error -> fall through to error state */ + default: + /* We received an unknown/unexpected value. Assume failure. */ + msg(M_WARN, "WARNING: Unknown/unexpected value in deferred" + "client-connect resultfile"); + ret = CC_RET_FAILED; + } + fclose(fp); + + return ret; +} + +/** + * Deletes the temporary file for the config directives of the client connect + * script and removes it into the client_connect_defer_state and environment + * + */ +static void +ccs_delete_config_file(struct multi_instance *mi) +{ + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + if (ccs->config_file) + { + setenv_del(mi->context.c2.es, "client_connect_config_file"); + if (!platform_unlink(ccs->config_file)) + { + msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", + ccs->config_file); } + free(ccs->config_file); + ccs->config_file = NULL; + } +} - /* - * Select a virtual address from either --ifconfig-push in --client-config-dir file - * or --ifconfig-pool. - */ - multi_select_virtual_addr(m, mi); +/** + * Create a temporary file for the config directives of the client connect + * script and puts it into the client_connect_defer_state and environment + * as "client_connect_config_file" + * + * @return boolean value if creation was successful + */ +static bool +ccs_gen_config_file(struct multi_instance *mi) +{ + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + struct gc_arena gc = gc_new(); + const char *fn; - /* do --client-connect setenvs */ - multi_client_connect_setenv(m, mi); + if (ccs->config_file) + { + ccs_delete_config_file(mi); + } + fn = platform_create_temp_file(mi->context.options.tmp_dir, "cc", &gc); + if (!fn) + { + gc_free(&gc); + return false; + } + ccs->config_file = string_alloc(fn, NULL); + + setenv_str(mi->context.c2.es, "client_connect_config_file", + ccs->config_file); + + gc_free(&gc); + return true; +} + +static enum client_connect_return +multi_client_connect_call_plugin_v1(struct multi_context *m, + struct multi_instance *mi, + bool deferred, + unsigned int *option_types_found) +{ + enum client_connect_return ret = CC_RET_SKIPPED; #ifdef ENABLE_PLUGIN - /* - * Call client-connect plug-in. - */ + ASSERT(m); + ASSERT(mi); + ASSERT(option_types_found); + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); - /* deprecated callback, use a file for passing back return info */ - if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) - { - struct argv argv = argv_new(); - const char *dc_file = create_temp_file(mi->context.options.tmp_dir, "cc", &gc); + /* deprecated callback, use a file for passing back return info */ + if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) + { + struct argv argv = argv_new(); + int call; - if (!dc_file) + if (!deferred) + { + call = OPENVPN_PLUGIN_CLIENT_CONNECT; + if (!ccs_gen_config_file(mi) + || !ccs_gen_deferred_ret_file(mi)) { - cc_succeeded = false; - goto script_depr_failed; + ret = CC_RET_FAILED; + goto cleanup; } + } + else + { + call = OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER; + /* the initial call should have created these files */ + ASSERT(ccs->config_file); + ASSERT(ccs->deferred_ret_file); + } - argv_printf(&argv, "%s", dc_file); - if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) - { - msg(M_WARN, "WARNING: client-connect plugin call failed"); - cc_succeeded = false; - } - else - { - multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; - } + argv_printf(&argv, "%s", ccs->config_file); + int plug_ret = plugin_call(mi->context.plugins, call, + &argv, NULL, mi->context.c2.es); + if (plug_ret == OPENVPN_PLUGIN_FUNC_SUCCESS) + { + ret = CC_RET_SUCCEEDED; + } + else if (plug_ret == OPENVPN_PLUGIN_FUNC_DEFERRED) + { + ret = CC_RET_DEFERRED; + /** + * Contrary to the plugin v2 API, we do not demand a working + * deferred plugin as all return can be handled by the files + * and plugin_call return success if a plugin is not defined + */ + } + else + { + msg(M_WARN, "WARNING: client-connect plugin call failed"); + ret = CC_RET_FAILED; + } - if (!platform_unlink(dc_file)) - { - msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", - dc_file); - } -script_depr_failed: - argv_reset(&argv); + /** + * plugin api v1 client connect async feature has both plugin and + * file return status, so in cases where the file has a code that + * demands override, we override our return code + */ + int file_ret = ccs_test_deferred_ret_file(mi); + + if (file_ret == CC_RET_FAILED) + { + ret = CC_RET_FAILED; + } + else if (ret == CC_RET_SUCCEEDED && file_ret == CC_RET_DEFERRED) + { + ret = CC_RET_DEFERRED; + } + + /* if we still think we have succeeded, do postprocessing */ + if (ret == CC_RET_SUCCEEDED) + { + multi_client_connect_post(m, mi, ccs->config_file, + option_types_found); } +cleanup: + argv_free(&argv); - /* V2 callback, use a plugin_return struct for passing back return info */ - if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2)) + if (ret != CC_RET_DEFERRED) { - struct plugin_return pr; + ccs_delete_config_file(mi); + ccs_delete_deferred_ret_file(mi); + } + } +#endif /* ifdef ENABLE_PLUGIN */ + return ret; +} - plugin_return_init(&pr); +static enum client_connect_return +multi_client_connect_call_plugin_v2(struct multi_context *m, + struct multi_instance *mi, + bool deferred, + unsigned int *option_types_found) +{ + enum client_connect_return ret = CC_RET_SKIPPED; +#ifdef ENABLE_PLUGIN + ASSERT(m); + ASSERT(mi); + ASSERT(option_types_found); - if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) - { - msg(M_WARN, "WARNING: client-connect-v2 plugin call failed"); - cc_succeeded = false; - } - else + int call = deferred ? OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 : + OPENVPN_PLUGIN_CLIENT_CONNECT_V2; + /* V2 callback, use a plugin_return struct for passing back return info */ + if (plugin_defined(mi->context.plugins, call)) + { + struct plugin_return pr; + + plugin_return_init(&pr); + + int plug_ret = plugin_call(mi->context.plugins, call, + NULL, &pr, mi->context.c2.es); + if (plug_ret == OPENVPN_PLUGIN_FUNC_SUCCESS) + { + multi_client_connect_post_plugin(m, mi, &pr, option_types_found); + ret = CC_RET_SUCCEEDED; + } + else if (plug_ret == OPENVPN_PLUGIN_FUNC_DEFERRED) + { + ret = CC_RET_DEFERRED; + if (!(plugin_defined(mi->context.plugins, + OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2))) { - multi_client_connect_post_plugin(m, mi, &pr, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; + msg(M_WARN, "A plugin that defers from the " + "OPENVPN_PLUGIN_CLIENT_CONNECT_V2 call must also " + "declare support for " + "OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2"); + ret = CC_RET_FAILED; } - - plugin_return_free(&pr); } + else + { + msg(M_WARN, "WARNING: client-connect-v2 plugin call failed"); + ret = CC_RET_FAILED; + } + + + plugin_return_free(&pr); + } #endif /* ifdef ENABLE_PLUGIN */ + return ret; +} + +static enum client_connect_return +multi_client_connect_script_deferred(struct multi_context *m, + struct multi_instance *mi, + unsigned int *option_types_found) +{ + ASSERT(mi); + ASSERT(option_types_found); + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + enum client_connect_return ret = CC_RET_SKIPPED; + ret = ccs_test_deferred_ret_file(mi); + + if (ret == CC_RET_SKIPPED) + { /* - * Run --client-connect script. + * Skipped and deferred are equivalent in this context. + * skipped means that the called program has not yet + * written a return status implicitly needing more time + * while deferred is the explicit notification that it + * needs more time */ - if (mi->context.options.client_connect_script && cc_succeeded) - { - struct argv argv = argv_new(); - const char *dc_file = NULL; + ret = CC_RET_DEFERRED; + } - setenv_str(mi->context.c2.es, "script_type", "client-connect"); + if (ret == CC_RET_SUCCEEDED) + { + ccs_delete_deferred_ret_file(mi); + multi_client_connect_post(m, mi, ccs->config_file, + option_types_found); + ccs_delete_config_file(mi); + } + if (ret == CC_RET_FAILED) + { + msg(M_INFO, "MULTI: deferred --client-connect script returned CC_RET_FAILED"); + ccs_delete_deferred_ret_file(mi); + ccs_delete_config_file(mi); + } + return ret; +} - dc_file = create_temp_file(mi->context.options.tmp_dir, "cc", &gc); - if (!dc_file) - { - cc_succeeded = false; - goto script_failed; - } +/** + * Runs the --client-connect script if one is defined. + */ +static enum client_connect_return +multi_client_connect_call_script(struct multi_context *m, + struct multi_instance *mi, + bool deferred, + unsigned int *option_types_found) +{ + if (deferred) + { + return multi_client_connect_script_deferred(m, mi, option_types_found); + } + ASSERT(m); + ASSERT(mi); + + enum client_connect_return ret = CC_RET_SKIPPED; + struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); + + if (mi->context.options.client_connect_script) + { + struct argv argv = argv_new(); + struct gc_arena gc = gc_new(); + + setenv_str(mi->context.c2.es, "script_type", "client-connect"); + + if (!ccs_gen_config_file(mi) + || !ccs_gen_deferred_ret_file(mi)) + { + ret = CC_RET_FAILED; + goto cleanup; + } - argv_parse_cmd(&argv, mi->context.options.client_connect_script); - argv_printf_cat(&argv, "%s", dc_file); + argv_parse_cmd(&argv, mi->context.options.client_connect_script); + argv_printf_cat(&argv, "%s", ccs->config_file); - if (openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-connect")) + if (openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-connect")) + { + if (ccs_test_deferred_ret_file(mi) == CC_RET_DEFERRED) { - multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; + ret = CC_RET_DEFERRED; } else { - cc_succeeded = false; + multi_client_connect_post(m, mi, ccs->config_file, + option_types_found); + ret = CC_RET_SUCCEEDED; } + } + else + { + ret = CC_RET_FAILED; + } +cleanup: + if (ret != CC_RET_DEFERRED) + { + ccs_delete_config_file(mi); + ccs_delete_deferred_ret_file(mi); + } + argv_free(&argv); + gc_free(&gc); + } + return ret; +} - if (!platform_unlink(dc_file)) - { - msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s", - dc_file); - } +/** + * Generates the data channel keys + */ +static bool +multi_client_generate_tls_keys(struct context *c) +{ + struct frame *frame_fragment = NULL; +#ifdef ENABLE_FRAGMENT + if (c->options.ce.fragment) + { + frame_fragment = &c->c2.frame_fragment; + } +#endif + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + if (!tls_session_update_crypto_params(session, &c->options, + &c->c2.frame, frame_fragment)) + { + msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); + register_signal(c, SIGUSR1, "process-push-msg-failed"); + return false; + } -script_failed: - argv_reset(&argv); + return true; +} + +static void +multi_client_connect_late_setup(struct multi_context *m, + struct multi_instance *mi, + const unsigned int option_types_found) +{ + ASSERT(m); + ASSERT(mi); + + struct gc_arena gc = gc_new(); + /* + * Process sourced options. + */ + do_deferred_options(&mi->context, option_types_found); + + /* + * make sure we got ifconfig settings from somewhere + */ + if (!mi->context.c2.push_ifconfig_defined) + { + msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote" + "--ifconfig address is available for %s", + multi_instance_string(mi, false, &gc)); + } + + /* + * make sure that ifconfig settings comply with constraints + */ + if (!ifconfig_push_constraint_satisfied(&mi->context)) + { + const char *ifconfig_constraint_network = + print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc); + const char *ifconfig_constraint_netmask = + print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc); + + /* JYFIXME -- this should cause the connection to fail */ + msg(D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s)" + "violates tunnel network/netmask constraint (%s/%s)", + multi_instance_string(mi, false, &gc), + print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc), + ifconfig_constraint_network, ifconfig_constraint_netmask); + } + + /* + * For routed tunnels, set up internal route to endpoint + * plus add all iroute routes. + */ + if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN) + { + if (mi->context.c2.push_ifconfig_defined) + { + multi_learn_in_addr_t(m, mi, + mi->context.c2.push_ifconfig_local, + -1, true); + msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s", + multi_instance_string(mi, false, &gc), + print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc)); } - /* - * Check for client-connect script left by management interface client - */ -#ifdef MANAGEMENT_DEF_AUTH - if (cc_succeeded && mi->cc_config) + if (mi->context.c2.push_ifconfig_ipv6_defined) { - multi_client_connect_mda(m, mi, mi->cc_config, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; + multi_learn_in6_addr(m, mi, + mi->context.c2.push_ifconfig_ipv6_local, + -1, true); + /* TODO: find out where addresses are "unlearned"!! */ + const char *ifconfig_local_ipv6 = + print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc); + msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s", + multi_instance_string(mi, false, &gc), + ifconfig_local_ipv6); } -#endif + + /* add routes locally, pointing to new client, if + * --iroute options have been specified */ + multi_add_iroutes(m, mi); /* - * Check for "disable" directive in client-config-dir file - * or config file generated by --client-connect script. + * iroutes represent subnets which are "owned" by a particular + * client. Therefore, do not actually push a route to a client + * if it matches one of the client's iroutes. */ - if (mi->context.options.disable) + remove_iroutes_from_push_route_list(&mi->context.options); + } + else if (mi->context.options.iroutes) + { + msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute" + "only works with tun-style tunnels", + multi_instance_string(mi, false, &gc)); + } + + /* 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; + + /* authentication complete, calculate dynamic client specific options */ + if (!multi_client_set_protocol_options(&mi->context)) + { + mi->context.c2.context_auth = CAS_FAILED; + } + /* Generate data channel keys only if setting protocol options + * has not failed */ + else if (!multi_client_generate_tls_keys(&mi->context)) + { + mi->context.c2.context_auth = CAS_FAILED; + } + + /* send push reply if ready */ + if (mi->context.c2.push_request_received) + { + process_incoming_push_request(&mi->context); + } + + gc_free(&gc); +} + +static void +multi_client_connect_early_setup(struct multi_context *m, + struct multi_instance *mi) +{ + ASSERT(mi->context.c1.tuntap); + /* + * lock down the common name and cert hashes so they can't change + * during future TLS renegotiations + */ + tls_lock_common_name(mi->context.c2.tls_multi); + tls_lock_cert_hash_set(mi->context.c2.tls_multi); + + /* generate a msg() prefix for this client instance */ + generate_prefix(mi); + + /* delete instances of previous clients with same common-name */ + if (!mi->context.options.duplicate_cn) + { + multi_delete_dup(m, mi); + } + + /* reset pool handle to null */ + mi->vaddr_handle = -1; + + /* do --client-connect setenvs */ + multi_select_virtual_addr(m, mi); + + multi_client_connect_setenv(m, mi); +} + +/** + * Try to source a dynamic config file from the + * --client-config-dir directory. + */ +static enum client_connect_return +multi_client_connect_source_ccd(struct multi_context *m, + struct multi_instance *mi, + bool deferred, + unsigned int *option_types_found) +{ + /* Since we never return a CC_RET_DEFERRED, this indicates a serious + * problem */ + ASSERT(!deferred); + enum client_connect_return ret = CC_RET_SKIPPED; + if (mi->context.options.client_config_dir) + { + struct gc_arena gc = gc_new(); + const char *ccd_file = NULL; + + const char *ccd_client = + platform_gen_path(mi->context.options.client_config_dir, + tls_common_name(mi->context.c2.tls_multi, false), + &gc); + + const char *ccd_default = + platform_gen_path(mi->context.options.client_config_dir, + CCD_DEFAULT, &gc); + + + /* try common-name file */ + if (platform_test_file(ccd_client)) { - msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to 'disable' directive"); - cc_succeeded = false; - cc_succeeded_count = 0; + ccd_file = ccd_client; + } + /* try default file */ + else if (platform_test_file(ccd_default)) + { + ccd_file = ccd_default; } - if (cc_succeeded) + if (ccd_file) { + options_server_import(&mi->context.options, + ccd_file, + D_IMPORT_ERRORS|M_OPTERR, + CLIENT_CONNECT_OPT_MASK, + option_types_found, + mi->context.c2.es); /* - * Process sourced options. + * Select a virtual address from either --ifconfig-push in + * --client-config-dir file or --ifconfig-pool. */ - do_deferred_options(&mi->context, option_types_found); + multi_select_virtual_addr(m, mi); - /* - * make sure we got ifconfig settings from somewhere - */ - if (!mi->context.c2.push_ifconfig_defined) - { - msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote --ifconfig address is available for %s", - multi_instance_string(mi, false, &gc)); - } + multi_client_connect_setenv(m, mi); - /* - * make sure that ifconfig settings comply with constraints - */ - if (!ifconfig_push_constraint_satisfied(&mi->context)) - { - /* JYFIXME -- this should cause the connection to fail */ - msg(D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s) violates tunnel network/netmask constraint (%s/%s)", - multi_instance_string(mi, false, &gc), - print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc), - print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc), - print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc)); - } + ret = CC_RET_SUCCEEDED; + } + gc_free(&gc); + } + return ret; +} - /* - * For routed tunnels, set up internal route to endpoint - * plus add all iroute routes. - */ - if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN) - { - if (mi->context.c2.push_ifconfig_defined) - { - multi_learn_in_addr_t(m, mi, mi->context.c2.push_ifconfig_local, -1, true); - msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s", - multi_instance_string(mi, false, &gc), - print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc)); - } +typedef enum client_connect_return (*multi_client_connect_handler) + (struct multi_context *m, struct multi_instance *mi, + bool from_deferred, unsigned int *option_types_found); - if (mi->context.c2.push_ifconfig_ipv6_defined) - { - multi_learn_in6_addr(m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true); - /* TODO: find out where addresses are "unlearned"!! */ - msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s", - multi_instance_string(mi, false, &gc), - print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc)); - } +static const multi_client_connect_handler client_connect_handlers[] = { + multi_client_connect_source_ccd, + multi_client_connect_call_plugin_v1, + multi_client_connect_call_plugin_v2, + multi_client_connect_call_script, + multi_client_connect_mda, + NULL, +}; - /* add routes locally, pointing to new client, if - * --iroute options have been specified */ - multi_add_iroutes(m, mi); +/* + * Called as soon as the SSL/TLS connection is authenticated. + * + * Will collect the client specific configuration from the different + * sources like ccd files, connect plugins and management interface. + * + * This method starts with cas_context CAS_PENDING and will move the + * state machine to either CAS_SUCCEEDED on success or + * CAS_FAILED/CAS_PARTIAL on failure. + * + * Instance-specific directives to be processed (CLIENT_CONNECT_OPT_MASK) + * include: + * + * iroute start-ip end-ip + * ifconfig-push local remote-netmask + * push + * + * + */ +static void +multi_connection_established(struct multi_context *m, struct multi_instance *mi) +{ + if (tls_authentication_status(mi->context.c2.tls_multi, 0) + != TLS_AUTHENTICATION_SUCCEEDED) + { + return; + } + + /* We are only called for the CAS_PENDING_x states, so we + * can ignore other states here */ + bool from_deferred = (mi->context.c2.context_auth != CAS_PENDING); + int *cur_handler_index = &mi->client_connect_defer_state.cur_handler_index; + unsigned int *option_types_found = + &mi->client_connect_defer_state.option_types_found; + + /* We are called for the first time */ + if (!from_deferred) + { + *cur_handler_index = 0; + *option_types_found = 0; + /* Initially we have no handler that has returned a result */ + mi->context.c2.context_auth = CAS_PENDING_DEFERRED; + + multi_client_connect_early_setup(m, mi); + } + + bool cc_succeeded = true; + + while (cc_succeeded + && client_connect_handlers[*cur_handler_index] != NULL) + { + enum client_connect_return ret; + ret = client_connect_handlers[*cur_handler_index](m, mi, from_deferred, + option_types_found); + + from_deferred = false; + + switch (ret) + { + case CC_RET_SUCCEEDED: /* - * iroutes represent subnets which are "owned" by a particular - * client. Therefore, do not actually push a route to a client - * if it matches one of the client's iroutes. + * Remember that we already had at least one handler + * returning a result should we go to into deferred state */ - remove_iroutes_from_push_route_list(&mi->context.options); - } - else if (mi->context.options.iroutes) - { - msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute only works with tun-style tunnels", - multi_instance_string(mi, false, &gc)); - } + mi->context.c2.context_auth = CAS_PENDING_DEFERRED_PARTIAL; + break; - /* 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; + case CC_RET_SKIPPED: + /* + * Move on with the next handler without modifying any + * other state + */ + break; - /* set context-level authentication flag */ - mi->context.c2.context_auth = CAS_SUCCEEDED; + case CC_RET_DEFERRED: + /* + * we already set client_connect_status to DEFERRED_RESULT or + * DEFERRED_NO_RESULT. We just return + * from the function as having client_connect_status + */ + return; -#ifdef ENABLE_ASYNC_PUSH - /* authentication complete, send push reply */ - if (mi->context.c2.push_request_received) - { - process_incoming_push_request(&mi->context); - } -#endif + case CC_RET_FAILED: + /* + * One handler failed. We abort the chain and set the final + * result to failed + */ + cc_succeeded = false; + break; + + default: + ASSERT(0); } - else + + /* + * Check for "disable" directive in client-config-dir file + * or config file generated by --client-connect script. + */ + if (mi->context.options.disable) { - /* set context-level authentication flag */ - mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED; + msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to " + "'disable' directive"); + cc_succeeded = false; } - /* set flag so we don't get called again */ - mi->connection_established_flag = true; - - /* increment number of current authenticated clients */ - ++m->n_clients; - update_mstat_n_clients(m->n_clients); - --mi->n_clients_delta; + (*cur_handler_index)++; + } -#ifdef MANAGEMENT_DEF_AUTH - if (management) + if (cc_succeeded) + { + multi_client_connect_late_setup(m, mi, *option_types_found); + } + else + { + /* run the disconnect script if we had a connect script that + * did not fail */ + if (mi->context.c2.context_auth == CAS_PENDING_DEFERRED_PARTIAL) { - management_connection_established(management, &mi->context.c2.mda_context, mi->context.c2.es); + multi_client_disconnect_script(mi); } -#endif - gc_free(&gc); + mi->context.c2.context_auth = CAS_FAILED; } - /* - * Reply now to client's PUSH_REQUEST query - */ - mi->context.c2.push_reply_deferred = false; + /* increment number of current authenticated clients */ + ++m->n_clients; + update_mstat_n_clients(m->n_clients); + --mi->n_clients_delta; + +#ifdef MANAGEMENT_DEF_AUTH + if (management) + { + management_connection_established(management, + &mi->context.c2.mda_context, mi->context.c2.es); + } +#endif } #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. + * Called when inotify event is fired, which happens when acf + * or connect-status file is closed or deleted. + * Continues authentication and sends push_reply + * (or be deferred again by client-connect) */ void multi_process_file_closed(struct multi_context *m, const unsigned int mpp_flags) @@ -2134,28 +2735,6 @@ multi_process_file_closed(struct multi_context *m, const unsigned int mpp_flags) { /* continue authentication, perform NCP negotiation and send push_reply */ multi_process_post(m, mi, mpp_flags); - - /* With NCP and deferred authentication, we perform cipher negotiation and - * data channel keys generation on incoming push request, assuming that auth - * succeeded. When auth succeeds in between push requests and async push is used, - * we send push reply immediately. Above multi_process_post() call performs - * NCP negotiation and here we do keys generation. */ - - struct context *c = &mi->context; - struct frame *frame_fragment = NULL; -#ifdef ENABLE_FRAGMENT - if (c->options.ce.fragment) - { - frame_fragment = &c->c2.frame_fragment; - } -#endif - struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; - if (!tls_session_update_crypto_params(session, &c->options, - &c->c2.frame, frame_fragment)) - { - msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); - register_signal(c, SIGUSR1, "init-data-channel-failed"); - } } else { @@ -2227,7 +2806,8 @@ static void multi_bcast(struct multi_context *m, const struct buffer *buf, const struct multi_instance *sender_instance, - const struct mroute_addr *sender_addr) + const struct mroute_addr *sender_addr, + uint16_t vid) { struct hash_iterator hi; struct hash_element *he; @@ -2251,7 +2831,11 @@ multi_bcast(struct multi_context *m, #ifdef ENABLE_PF if (sender_instance) { - if (!pf_c2c_test(&sender_instance->context, &mi->context, "bcast_c2c")) + if (!pf_c2c_test(&sender_instance->context.c2.pf, + sender_instance->context.c2.tls_multi, + &mi->context.c2.pf, + mi->context.c2.tls_multi, + "bcast_c2c")) { msg(D_PF_DROPPED_BCAST, "PF: client[%s] -> client[%s] packet dropped by BCAST packet filter", mi_prefix(sender_instance), @@ -2261,7 +2845,8 @@ multi_bcast(struct multi_context *m, } if (sender_addr) { - if (!pf_addr_test(&mi->context, sender_addr, "bcast_src_addr")) + if (!pf_addr_test(&mi->context.c2.pf, &mi->context, + sender_addr, "bcast_src_addr")) { struct gc_arena gc = gc_new(); msg(D_PF_DROPPED_BCAST, "PF: addr[%s] -> client[%s] packet dropped by BCAST packet filter", @@ -2272,6 +2857,10 @@ multi_bcast(struct multi_context *m, } } #endif /* ifdef ENABLE_PF */ + if (vid != 0 && vid != mi->context.options.vlan_pvid) + { + continue; + } multi_add_mbuf(m, mi, mb); } } @@ -2329,6 +2918,32 @@ multi_schedule_context_wakeup(struct multi_context *m, struct multi_instance *mi compute_wakeup_sigma(&mi->context.c2.timeval)); } +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) +static void +add_inotify_file_watch(struct multi_context *m, struct multi_instance *mi, + int inotify_fd, const char *file) +{ + /* watch acf file */ + long watch_descriptor = inotify_add_watch(inotify_fd, 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 | M_ERRNO, "MULTI: inotify_add_watch error"); + } +} +#endif /* if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) */ + /* * Figure instance-specific timers, convert * earliest to absolute time in mi->wakeup, @@ -2344,12 +2959,12 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns 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; + bool was_unauthenticated = true; 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; + was_unauthenticated = (ks->authenticated == KS_AUTH_FALSE); } #endif @@ -2358,23 +2973,16 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns pre_select(&mi->context); #if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) - if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated) + /* + * if we see the state transition from unauthenticated to deferred + * and an auth_control_file, we assume it got just added and add + * inotify watch to that file + */ + if (ks && ks->auth_control_file && was_unauthenticated + && (ks->authenticated == KS_AUTH_DEFERRED)) { - /* 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 | M_ERRNO, "MULTI: inotify_add_watch error"); - } + add_inotify_file_watch(m, mi, m->top.c2.inotify_fd, + ks->auth_control_file); } #endif @@ -2382,11 +2990,20 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns { /* 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)) + if (is_cas_pending(mi->context.c2.context_auth) + && CONNECTION_ESTABLISHED(&mi->context)) { multi_connection_established(m, mi); } - +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) + if (is_cas_pending(mi->context.c2.context_auth) + && mi->client_connect_defer_state.deferred_ret_file) + { + add_inotify_file_watch(m, mi, m->top.c2.inotify_fd, + mi->client_connect_defer_state. + deferred_ret_file); + } +#endif /* tell scheduler to wake us up at some point in the future */ multi_schedule_context_wakeup(m, mi); } @@ -2406,14 +3023,14 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns multi_set_pending(m, ANY_OUT(&mi->context) ? mi : NULL); #ifdef MULTI_DEBUG_EVENT_LOOP - printf("POST %s[%d] to=%d lo=%d/%d w=%d/%d\n", + printf("POST %s[%d] to=%d lo=%d/%d w=%" PRIi64 "/%ld\n", id(mi), (int) (mi == m->pending), mi ? mi->context.c2.to_tun.len : -1, mi ? mi->context.c2.to_link.len : -1, (mi && mi->context.c2.fragment) ? mi->context.c2.fragment->outgoing.len : -1, - (int)mi->context.c2.timeval.tv_sec, - (int)mi->context.c2.timeval.tv_usec); + (int64_t)mi->context.c2.timeval.tv_sec, + (long)mi->context.c2.timeval.tv_usec); #endif } @@ -2579,6 +3196,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst &dest, NULL, NULL, + 0, &c->c2.to_tun, DEV_TYPE_TUN); @@ -2610,7 +3228,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mroute_flags & MROUTE_EXTRACT_MCAST) { /* for now, treat multicast as broadcast */ - multi_bcast(m, &c->c2.to_tun, m->pending, NULL); + multi_bcast(m, &c->c2.to_tun, m->pending, NULL, 0); } else /* possible client to client routing */ { @@ -2621,7 +3239,10 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mi) { #ifdef ENABLE_PF - if (!pf_c2c_test(c, &mi->context, "tun_c2c")) + if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi, + &mi->context.c2.pf, + mi->context.c2.tls_multi, + "tun_c2c")) { msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter", mi_prefix(mi)); @@ -2637,7 +3258,8 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } } #ifdef ENABLE_PF - if (c->c2.to_tun.len && !pf_addr_test(c, &dest, "tun_dest_addr")) + if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c, &dest, + "tun_dest_addr")) { msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter", mroute_addr_print_ex(&dest, MAPF_SHOW_ARP, &gc)); @@ -2647,10 +3269,25 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP) { + uint16_t vid = 0; #ifdef ENABLE_PF struct mroute_addr edest; mroute_addr_reset(&edest); #endif + + if (m->top.options.vlan_tagging) + { + if (vlan_is_tagged(&c->c2.to_tun)) + { + /* Drop VLAN-tagged frame. */ + msg(D_VLAN_DEBUG, "dropping incoming VLAN-tagged frame"); + c->c2.to_tun.len = 0; + } + else + { + vid = c->options.vlan_pvid; + } + } /* extract packet source and dest addresses */ mroute_flags = mroute_extract_addr_from_packet(&src, &dest, @@ -2660,6 +3297,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst #else NULL, #endif + vid, &c->c2.to_tun, DEV_TYPE_TAP); @@ -2672,7 +3310,8 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst { if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST)) { - multi_bcast(m, &c->c2.to_tun, m->pending, NULL); + multi_bcast(m, &c->c2.to_tun, m->pending, NULL, + vid); } else /* try client-to-client routing */ { @@ -2682,7 +3321,10 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst if (mi) { #ifdef ENABLE_PF - if (!pf_c2c_test(c, &mi->context, "tap_c2c")) + if (!pf_c2c_test(&c->c2.pf, c->c2.tls_multi, + &mi->context.c2.pf, + mi->context.c2.tls_multi, + "tap_c2c")) { msg(D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter", mi_prefix(mi)); @@ -2698,7 +3340,9 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } } #ifdef ENABLE_PF - if (c->c2.to_tun.len && !pf_addr_test(c, &edest, "tap_dest_addr")) + if (c->c2.to_tun.len && !pf_addr_test(&c->c2.pf, c, + &edest, + "tap_dest_addr")) { msg(D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter", mroute_addr_print_ex(&edest, MAPF_SHOW_ARP, &gc)); @@ -2745,6 +3389,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags unsigned int mroute_flags; struct mroute_addr src, dest; const int dev_type = TUNNEL_TYPE(m->top.c1.tuntap); + int16_t vid = 0; #ifdef ENABLE_PF struct mroute_addr esrc, *e1, *e2; @@ -2769,6 +3414,15 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags return true; } + if (dev_type == DEV_TYPE_TAP && m->top.options.vlan_tagging) + { + vid = vlan_decapsulate(&m->top, &m->top.c2.buf); + if (vid < 0) + { + return false; + } + } + /* * Route an incoming tun/tap packet to * the appropriate multi_instance object. @@ -2782,6 +3436,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags NULL, #endif NULL, + vid, &m->top.c2.buf, dev_type); @@ -2794,9 +3449,9 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags { /* for now, treat multicast as broadcast */ #ifdef ENABLE_PF - multi_bcast(m, &m->top.c2.buf, NULL, e2); + multi_bcast(m, &m->top.c2.buf, NULL, e2, vid); #else - multi_bcast(m, &m->top.c2.buf, NULL, NULL); + multi_bcast(m, &m->top.c2.buf, NULL, NULL, vid); #endif } else @@ -2811,7 +3466,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags set_prefix(m->pending); #ifdef ENABLE_PF - if (!pf_addr_test(c, e2, "tun_tap_src_addr")) + if (!pf_addr_test(&c->c2.pf, c, e2, "tun_tap_src_addr")) { msg(D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter", mroute_addr_print_ex(&src, MAPF_SHOW_ARP, &gc)); @@ -2859,7 +3514,7 @@ multi_get_queue(struct mbuf_set *ms) if (mbuf_extract_item(ms, &item)) /* cleartext IP packet */ { - unsigned int pip_flags = PIPV4_PASSTOS; + unsigned int pip_flags = PIPV4_PASSTOS | PIPV6_IMCP_NOHOST_SERVER; set_prefix(item.instance); item.instance->context.c2.buf = item.buffer->buf; @@ -2978,7 +3633,7 @@ gremlin_flood_clients(struct multi_context *m) for (i = 0; i < parm.n_packets; ++i) { - multi_bcast(m, &buf, NULL, NULL); + multi_bcast(m, &buf, NULL, NULL, 0); } gc_free(&gc); @@ -3260,6 +3915,24 @@ management_kill_by_cid(void *arg, const unsigned long cid, const char *kill_msg) } static bool +management_client_pending_auth(void *arg, + const unsigned long cid, + const char *extra) +{ + struct multi_context *m = (struct multi_context *) arg; + struct multi_instance *mi = lookup_by_cid(m, cid); + if (mi) + { + /* sends INFO_PRE and AUTH_PENDING messages to client */ + bool ret = send_auth_pending_messages(&mi->context, extra); + multi_schedule_context_wakeup(m, mi); + return ret; + } + return false; +} + + +static bool management_client_auth(void *arg, const unsigned long cid, const unsigned int mda_key_id, @@ -3280,7 +3953,7 @@ management_client_auth(void *arg, { if (auth) { - if (!mi->connection_established_flag) + if (is_cas_pending(mi->context.c2.context_auth)) { set_cc_config(mi, cc_config); cc_config_owned = false; @@ -3292,7 +3965,7 @@ management_client_auth(void *arg, { msg(D_MULTI_LOW, "MULTI: connection rejected: %s, CLI:%s", reason, np(client_reason)); } - if (mi->connection_established_flag) + if (!is_cas_pending(mi->context.c2.context_auth)) { send_auth_failed(&mi->context, client_reason); /* mid-session reauth failed */ multi_schedule_context_wakeup(m, mi); @@ -3366,6 +4039,7 @@ init_management_callback_multi(struct multi_context *m) #ifdef MANAGEMENT_DEF_AUTH cb.kill_by_cid = management_kill_by_cid; cb.client_auth = management_client_auth; + cb.client_pending_auth = management_client_pending_auth; cb.get_peer_info = management_get_peer_info; #endif #ifdef MANAGEMENT_PF @@ -3393,10 +4067,3 @@ tunnel_server(struct context *top) tunnel_server_tcp(top); } } - -#else /* if P2MP_SERVER */ -static void -dummy(void) -{ -} -#endif /* P2MP_SERVER */ diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h index ebcc22d..40e808a 100644 --- a/src/openvpn/multi.h +++ b/src/openvpn/multi.h @@ -28,8 +28,6 @@ #ifndef MULTI_H #define MULTI_H -#if P2MP_SERVER - #include "init.h" #include "forward.h" #include "mroute.h" @@ -40,6 +38,7 @@ #include "mudp.h" #include "mtcp.h" #include "perf.h" +#include "vlan.h" #define MULTI_PREFIX_MAX_LENGTH 256 @@ -64,6 +63,31 @@ struct deferred_signal_schedule_entry }; /** + * Detached client connection state. This is the state that is tracked while + * the client connect hooks are executed. + */ +struct client_connect_defer_state +{ + /* Index of currently executed handler. */ + int cur_handler_index; + /* Remember which option classes where processed for delayed option + * handling. */ + unsigned int option_types_found; + + /** + * The temporary file name that contains the return status of the + * client-connect script if it exits with defer as status + */ + char *deferred_ret_file; + + /** + * The temporary file name that contains the config directives + * returned by the client-connect script + */ + char *config_file; +}; + +/** * Server-mode state structure for one single VPN tunnel. * * This structure is used by OpenVPN processes running in server-mode to @@ -76,7 +100,6 @@ struct deferred_signal_schedule_entry struct multi_instance { struct schedule_entry se; /* this must be the first element of the structure */ struct gc_arena gc; - bool defined; bool halt; int refcount; int route_count; /* number of routes (including cached routes) owned by this instance */ @@ -98,20 +121,18 @@ struct multi_instance { 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; bool did_iter; #ifdef MANAGEMENT_DEF_AUTH bool did_cid_hash; struct buffer_list *cc_config; #endif - bool connection_established_flag; bool did_iroutes; int n_clients_delta; /* added to multi_context.n_clients when instance is closed */ struct context context; /**< The context structure storing state * for this VPN tunnel. */ - + struct client_connect_defer_state client_connect_defer_state; #ifdef ENABLE_ASYNC_PUSH int inotify_watch; /* watch descriptor for acf */ #endif @@ -191,6 +212,17 @@ struct multi_context { struct deferred_signal_schedule_entry deferred_shutdown_signal; }; +/** + * Return values used by the client connect call-back functions. + */ +enum client_connect_return +{ + CC_RET_FAILED, + CC_RET_SUCCEEDED, + CC_RET_DEFERRED, + CC_RET_SKIPPED +}; + /* * Host route */ @@ -533,11 +565,13 @@ clear_prefix(void) */ #define MULTI_CACHE_ROUTE_TTL 60 +void multi_reap_process_dowork(const struct multi_context *m); + +void multi_process_per_second_timers_dowork(struct multi_context *m); + static inline void multi_reap_process(const struct multi_context *m) { - void multi_reap_process_dowork(const struct multi_context *m); - if (m->reaper->last_call != now) { multi_reap_process_dowork(m); @@ -549,8 +583,6 @@ multi_process_per_second_timers(struct multi_context *m) { if (m->per_second_trigger != now) { - void multi_process_per_second_timers_dowork(struct multi_context *m); - multi_process_per_second_timers_dowork(m); m->per_second_trigger = now; } @@ -620,13 +652,16 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags mi->context.c2.to_tun.len); #endif set_prefix(mi); + vlan_process_outgoing_tun(m, mi); process_outgoing_tun(&mi->context); ret = multi_process_post(m, mi, mpp_flags); clear_prefix(); return ret; } - +#define CLIENT_CONNECT_OPT_MASK (OPT_P_INSTANCE | OPT_P_INHERIT \ + |OPT_P_PUSH | OPT_P_TIMER | OPT_P_CONFIG \ + |OPT_P_ECHO | OPT_P_COMP | OPT_P_SOCKFLAGS) static inline bool multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags) @@ -650,5 +685,4 @@ multi_set_pending(struct multi_context *m, struct multi_instance *mi) m->pending = mi; } -#endif /* P2MP_SERVER */ #endif /* MULTI_H */ diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h new file mode 100644 index 0000000..9c1d169 --- /dev/null +++ b/src/openvpn/networking.h @@ -0,0 +1,293 @@ +/* + * Generic interface to platform specific networking code + * + * Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc> + * + * 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 NETWORKING_H_ +#define NETWORKING_H_ + +#include "syshead.h" + +struct context; + +#ifdef ENABLE_SITNL +#include "networking_sitnl.h" +#elif ENABLE_IPROUTE +#include "networking_iproute2.h" +#else +/* define mock types to ensure code builds on any platform */ +typedef void *openvpn_net_ctx_t; +typedef void *openvpn_net_iface_t; + +static inline int +net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx) +{ + return 0; +} + +static inline void +net_ctx_reset(openvpn_net_ctx_t *ctx) +{ + (void)ctx; +} + +static inline void +net_ctx_free(openvpn_net_ctx_t *ctx) +{ + (void)ctx; +} +#endif /* ifdef ENABLE_SITNL */ + +#if defined(ENABLE_SITNL) || defined(ENABLE_IPROUTE) + +/** + * Initialize the platform specific context object + * + * @param c openvpn generic context + * @param ctx the implementation specific context to initialize + * + * @return 0 on success, a negative error code otherwise + */ +int net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx); + +/** + * Release resources allocated by the internal garbage collector + * + * @param ctx the implementation specific context + */ +void net_ctx_reset(openvpn_net_ctx_t *ctx); + +/** + * Release all resources allocated within the platform specific context object + * + * @param ctx the implementation specific context to release + */ +void net_ctx_free(openvpn_net_ctx_t *ctx); + +/** + * Bring interface up or down. + * + * @param ctx the implementation specific context + * @param iface the interface to modify + * @param up true if the interface has to be brought up, false otherwise + * + * @return 0 on success, a negative error code otherwise + */ +int net_iface_up(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, + bool up); + +/** + * Set the MTU for an interface + * + * @param ctx the implementation specific context + * @param iface the interface to modify + * @param mtru the new MTU + * + * @return 0 on success, a negative error code otherwise + */ +int net_iface_mtu_set(openvpn_net_ctx_t *ctx, + const openvpn_net_iface_t *iface, uint32_t mtu); + +/** + * Add an IPv4 address to an interface + * + * @param ctx the implementation specific context + * @param iface the interface where the address has to be added + * @param addr the address to add + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ +int net_addr_v4_add(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, + const in_addr_t *addr, int prefixlen); + +/** + * Add an IPv6 address to an interface + * + * @param ctx the implementation specific context + * @param iface the interface where the address has to be added + * @param addr the address to add + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ + +int net_addr_v6_add(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, + const struct in6_addr *addr, int prefixlen); + +/** + * Remove an IPv4 from an interface + * + * @param ctx the implementation specific context + * @param iface the interface to remove the address from + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ +int net_addr_v4_del(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, + const in_addr_t *addr, int prefixlen); + +/** + * Remove an IPv6 from an interface + * + * @param ctx the implementation specific context + * @param iface the interface to remove the address from + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ +int net_addr_v6_del(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface, + const struct in6_addr *addr, int prefixlen); + +/** + * Add a point-to-point IPv4 address to an interface + * + * @param ctx the implementation specific context + * @param iface the interface where the address has to be added + * @param local the address to add + * @param remote the associated p-t-p remote address + * + * @return 0 on success, a negative error code otherwise + */ +int net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, + const openvpn_net_iface_t *iface, + const in_addr_t *local, const in_addr_t *remote); + +/** + * Remove a point-to-point IPv4 address from an interface + * + * @param ctx the implementation specific context + * @param iface the interface to remove the address from + * @param local the address to remove + * @param remote the associated p-t-p remote address + * + * @return 0 on success, a negative error code otherwise + */ +int net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, + const openvpn_net_iface_t *iface, + const in_addr_t *local, const in_addr_t *remote); + + +/** + * Add a route for an IPv4 address/network + * + * @param ctx the implementation specific context + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + int prefixlen, const in_addr_t *gw, + const openvpn_net_iface_t *iface, uint32_t table, + int metric); + +/** + * Add a route for an IPv6 address/network + * + * @param ctx the implementation specific context + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, + const openvpn_net_iface_t *iface, + uint32_t table, int metric); + +/** + * Delete a route for an IPv4 address/network + * + * @param ctx the implementation specific context + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + int prefixlen, const in_addr_t *gw, + const openvpn_net_iface_t *iface, uint32_t table, + int metric); + +/** + * Delete a route for an IPv4 address/network + * + * @param ctx the implementation specific context + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, + const openvpn_net_iface_t *iface, + uint32_t table, int metric); + +/** + * Retrieve the gateway and outgoing interface for the specified IPv4 + * address/network + * + * @param ctx the implementation specific context + * @param dst The destination to lookup + * @param best_gw Location where the retrieved GW has to be stored + * @param best_iface Location where the retrieved interface has to be stored + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + in_addr_t *best_gw, openvpn_net_iface_t *best_iface); + +/** + * Retrieve the gateway and outgoing interface for the specified IPv6 + * address/network + * + * @param ctx the implementation specific context + * @param dst The destination to lookup + * @param best_gw Location where the retrieved GW has to be stored + * @param best_iface Location where the retrieved interface has to be stored + * + * @return 0 on success, a negative error code otherwise + */ +int net_route_v6_best_gw(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + struct in6_addr *best_gw, + openvpn_net_iface_t *best_iface); + +#endif /* ENABLE_SITNL || ENABLE_IPROUTE */ + +#endif /* NETWORKING_H_ */ diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c new file mode 100644 index 0000000..f3b9c61 --- /dev/null +++ b/src/openvpn/networking_iproute2.c @@ -0,0 +1,382 @@ +/* + * Networking API implementation for iproute2 + * + * Copyright (C) 2018 Antonio Quartulli <a@unstable.cc> + * + * 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 + +#if defined(TARGET_LINUX) && defined(ENABLE_IPROUTE) + +#include "syshead.h" + +#include "argv.h" +#include "networking.h" +#include "misc.h" +#include "openvpn.h" +#include "run_command.h" +#include "socket.h" + +#include <stdbool.h> +#include <netinet/in.h> + +int +net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx) +{ + ctx->es = NULL; + if (c) + { + ctx->es = c->es; + } + ctx->gc = gc_new(); + + return 0; +} + +void +net_ctx_reset(openvpn_net_ctx_t *ctx) +{ + gc_reset(&ctx->gc); +} + +void +net_ctx_free(openvpn_net_ctx_t *ctx) +{ + gc_free(&ctx->gc); +} + +int +net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up) +{ + struct argv argv = argv_new(); + + argv_printf(&argv, "%s link set dev %s %s", iproute_path, iface, + up ? "up" : "down"); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed"); + + argv_free(&argv); + + return 0; +} + +int +net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, uint32_t mtu) +{ + struct argv argv = argv_new(); + + argv_printf(&argv, "%s link set dev %s up mtu %d", iproute_path, iface, + mtu); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed"); + + return 0; +} + +int +net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + struct argv argv = argv_new(); + + const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s addr add dev %s %s/%d", iproute_path, iface, + addr_str, prefixlen); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + struct argv argv = argv_new(); + char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 addr add %s/%d dev %s", iproute_path, addr_str, + prefixlen, iface); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, + "Linux ip -6 addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + struct argv argv = argv_new(); + const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s addr del dev %s %s/%d", iproute_path, iface, + addr_str, prefixlen); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + struct argv argv = argv_new(); + char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 addr del %s/%d dev %s", iproute_path, + addr_str, prefixlen, iface); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip -6 addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + struct argv argv = argv_new(); + const char *local_str = print_in_addr_t(*local, 0, &ctx->gc); + const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc); + + argv_printf(&argv, "%s addr add dev %s local %s peer %s", iproute_path, + iface, local_str, remote_str); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + struct argv argv = argv_new(); + const char *local_str = print_in_addr_t(*local, 0, &ctx->gc); + const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc); + + argv_printf(&argv, "%s addr del dev %s local %s peer %s", iproute_path, + iface, local_str, remote_str); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric) +{ + struct argv argv = argv_new(); + const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s route add %s/%d", iproute_path, dst_str, prefixlen); + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + if (iface) + { + argv_printf_cat(&argv, "dev %s", iface); + } + + if (gw) + { + const char *gw_str = print_in_addr_t(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route add command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, const char *iface, + uint32_t table, int metric) +{ + struct argv argv = argv_new(); + char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 route add %s/%d dev %s", iproute_path, dst_str, + prefixlen, iface); + + if (gw) + { + char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 add command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric) +{ + struct argv argv = argv_new(); + const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s route del %s/%d", iproute_path, dst_str, prefixlen); + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route delete command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, const char *iface, + uint32_t table, int metric) +{ + struct argv argv = argv_new(); + char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 route del %s/%d dev %s", iproute_path, dst_str, + prefixlen, iface); + + if (gw) + { + char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 del command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + in_addr_t *best_gw, char *best_iface) +{ + best_iface[0] = '\0'; + + FILE *fp = fopen("/proc/net/route", "r"); + if (!fp) + { + return -1; + } + + char line[256]; + int count = 0; + unsigned int lowest_metric = UINT_MAX; + while (fgets(line, sizeof(line), fp) != NULL) + { + if (count) + { + unsigned int net_x = 0; + unsigned int mask_x = 0; + unsigned int gw_x = 0; + unsigned int metric = 0; + unsigned int flags = 0; + char name[16]; + name[0] = '\0'; + + const int np = sscanf(line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x", + name, &net_x, &gw_x, &flags, &metric, + &mask_x); + + if (np == 6 && (flags & IFF_UP)) + { + const in_addr_t net = ntohl(net_x); + const in_addr_t mask = ntohl(mask_x); + const in_addr_t gw = ntohl(gw_x); + + if (!net && !mask && metric < lowest_metric) + { + *best_gw = gw; + strcpy(best_iface, name); + lowest_metric = metric; + } + } + } + ++count; + } + fclose(fp); + + return 0; +} + +/* + * The following function is not implemented in the iproute backend as it + * uses the sitnl implementation from networking_sitnl.c. + * + * int + * net_route_v6_best_gw(const struct in6_addr *dst, + * struct in6_addr *best_gw, char *best_iface) + */ + +#endif /* ENABLE_IPROUTE && TARGET_LINUX */ diff --git a/src/openvpn/networking_iproute2.h b/src/openvpn/networking_iproute2.h new file mode 100644 index 0000000..24c605d --- /dev/null +++ b/src/openvpn/networking_iproute2.h @@ -0,0 +1,37 @@ +/* + * Generic interface to platform specific networking code + * + * Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc> + * + * 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 NETWORKING_IP_H_ +#define NETWORKING_IP_H_ + +#include "env_set.h" + +typedef char openvpn_net_iface_t; + +struct openvpn_net_ctx +{ + struct env_set *es; + struct gc_arena gc; +}; + +typedef struct openvpn_net_ctx openvpn_net_ctx_t; + +#endif /* NETWORKING_IP_H_ */ diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c new file mode 100644 index 0000000..713a213 --- /dev/null +++ b/src/openvpn/networking_sitnl.c @@ -0,0 +1,1246 @@ +/* + * Simplified Interface To NetLink + * + * Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc> + * + * 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 TARGET_LINUX + +#include "syshead.h" + +#include "errlevel.h" +#include "buffer.h" +#include "networking.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#define SNDBUF_SIZE (1024 * 2) +#define RCVBUF_SIZE (1024 * 4) + +#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size) \ + { \ + if (sitnl_addattr(_msg, _max_size, _attr, _data, _size) < 0) \ + { \ + goto err; \ + } \ + } + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((uint8_t *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +/** + * Generic address data structure used to pass addresses and prefixes as + * argument to AF family agnostic functions + */ +typedef union { + in_addr_t ipv4; + struct in6_addr ipv6; +} inet_address_t; + +/** + * Link state request message + */ +struct sitnl_link_req { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[256]; +}; + +/** + * Address request message + */ +struct sitnl_addr_req { + struct nlmsghdr n; + struct ifaddrmsg i; + char buf[256]; +}; + +/** + * Route request message + */ +struct sitnl_route_req { + struct nlmsghdr n; + struct rtmsg r; + char buf[256]; +}; + +typedef int (*sitnl_parse_reply_cb)(struct nlmsghdr *msg, void *arg); + +/** + * Object returned by route request operation + */ +struct sitnl_route_data_cb { + unsigned int iface; + inet_address_t gw; +}; + +/** + * Helper function used to easily add attributes to a rtnl message + */ +static int +sitnl_addattr(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) + { + msg(M_WARN, "%s: rtnl: message exceeded bound of %d", __func__, + maxlen); + return -EMSGSIZE; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + + if (!data) + { + memset(RTA_DATA(rta), 0, alen); + } + else + { + memcpy(RTA_DATA(rta), data, alen); + } + + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + + return 0; +} + +/** + * Open RTNL socket + */ +static int +sitnl_socket(void) +{ + int sndbuf = SNDBUF_SIZE; + int rcvbuf = RCVBUF_SIZE; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + msg(M_WARN, "%s: cannot open netlink socket", __func__); + return fd; + } + + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) + { + msg(M_WARN | M_ERRNO, "%s: SO_SNDBUF", __func__); + close(fd); + return -1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) + { + msg(M_WARN | M_ERRNO, "%s: SO_RCVBUF", __func__); + close(fd); + return -1; + } + + return fd; +} + +/** + * Bind socket to Netlink subsystem + */ +static int +sitnl_bind(int fd, uint32_t groups) +{ + socklen_t addr_len; + struct sockaddr_nl local; + + CLEAR(local); + + local.nl_family = AF_NETLINK; + local.nl_groups = groups; + + if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) + { + msg(M_WARN | M_ERRNO, "%s: cannot bind netlink socket", __func__); + return -errno; + } + + addr_len = sizeof(local); + if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) + { + msg(M_WARN | M_ERRNO, "%s: cannot getsockname", __func__); + return -errno; + } + + if (addr_len != sizeof(local)) + { + msg(M_WARN, "%s: wrong address length %d", __func__, addr_len); + return -EINVAL; + } + + if (local.nl_family != AF_NETLINK) + { + msg(M_WARN, "%s: wrong address family %d", __func__, local.nl_family); + return -EINVAL; + } + + return 0; +} + +/** + * Send Netlink message and run callback on reply (if specified) + */ +static int +sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups, + sitnl_parse_reply_cb cb, void *arg_cb) +{ + int len, rem_len, fd, ret, rcv_len; + struct sockaddr_nl nladdr; + struct nlmsgerr *err; + struct nlmsghdr *h; + unsigned int seq; + char buf[1024 * 16]; + struct iovec iov = + { + .iov_base = payload, + .iov_len = payload->nlmsg_len, + }; + struct msghdr nlmsg = + { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + CLEAR(nladdr); + + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + payload->nlmsg_seq = seq = time(NULL); + + /* no need to send reply */ + if (!cb) + { + payload->nlmsg_flags |= NLM_F_ACK; + } + + fd = sitnl_socket(); + if (fd < 0) + { + msg(M_WARN | M_ERRNO, "%s: can't open rtnl socket", __func__); + return -errno; + } + + ret = sitnl_bind(fd, 0); + if (ret < 0) + { + msg(M_WARN | M_ERRNO, "%s: can't bind rtnl socket", __func__); + ret = -errno; + goto out; + } + + ret = sendmsg(fd, &nlmsg, 0); + if (ret < 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: error on sendmsg()", __func__); + ret = -errno; + goto out; + } + + /* prepare buffer to store RTNL replies */ + memset(buf, 0, sizeof(buf)); + iov.iov_base = buf; + + while (1) + { + /* + * iov_len is modified by recvmsg(), therefore has to be initialized before + * using it again + */ + msg(D_RTNL, "%s: checking for received messages", __func__); + iov.iov_len = sizeof(buf); + rcv_len = recvmsg(fd, &nlmsg, 0); + msg(D_RTNL, "%s: rtnl: received %d bytes", __func__, rcv_len); + if (rcv_len < 0) + { + if ((errno == EINTR) || (errno == EAGAIN)) + { + msg(D_RTNL, "%s: interrupted call", __func__); + continue; + } + msg(M_WARN | M_ERRNO, "%s: rtnl: error on recvmsg()", __func__); + ret = -errno; + goto out; + } + + if (rcv_len == 0) + { + msg(M_WARN, "%s: rtnl: socket reached unexpected EOF", __func__); + ret = -EIO; + goto out; + } + + if (nlmsg.msg_namelen != sizeof(nladdr)) + { + msg(M_WARN, "%s: sender address length: %u (expected %zu)", + __func__, nlmsg.msg_namelen, sizeof(nladdr)); + ret = -EIO; + goto out; + } + + h = (struct nlmsghdr *)buf; + while (rcv_len >= (int)sizeof(*h)) + { + len = h->nlmsg_len; + rem_len = len - sizeof(*h); + + if ((rem_len < 0) || (len > rcv_len)) + { + if (nlmsg.msg_flags & MSG_TRUNC) + { + msg(M_WARN, "%s: truncated message", __func__); + ret = -EIO; + goto out; + } + msg(M_WARN, "%s: malformed message: len=%d", __func__, len); + ret = -EIO; + goto out; + } + +/* if (((int)nladdr.nl_pid != peer) || (h->nlmsg_pid != nladdr.nl_pid) + * || (h->nlmsg_seq != seq)) + * { + * rcv_len -= NLMSG_ALIGN(len); + * h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + * msg(M_DEBUG, "%s: skipping unrelated message. nl_pid:%d (peer:%d) nl_msg_pid:%d nl_seq:%d seq:%d", + * __func__, (int)nladdr.nl_pid, peer, h->nlmsg_pid, + * h->nlmsg_seq, seq); + * continue; + * } + */ + if (h->nlmsg_type == NLMSG_ERROR) + { + err = (struct nlmsgerr *)NLMSG_DATA(h); + if (rem_len < (int)sizeof(struct nlmsgerr)) + { + msg(M_WARN, "%s: ERROR truncated", __func__); + ret = -EIO; + } + else + { + if (!err->error) + { + ret = 0; + if (cb) + { + ret = cb(h, arg_cb); + } + } + else + { + msg(M_WARN, "%s: rtnl: generic error (%d): %s", + __func__, err->error, strerror(-err->error)); + ret = err->error; + } + } + goto out; + } + + if (cb) + { + ret = cb(h, arg_cb); + goto out; + } + else + { + msg(M_WARN, "%s: RTNL: unexpected reply", __func__); + } + + rcv_len -= NLMSG_ALIGN(len); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + } + + if (nlmsg.msg_flags & MSG_TRUNC) + { + msg(M_WARN, "%s: message truncated", __func__); + continue; + } + + if (rcv_len) + { + msg(M_WARN, "%s: rtnl: %d not parsed bytes", __func__, rcv_len); + ret = -1; + goto out; + } + } +out: + close(fd); + + return ret; +} + +typedef struct { + int addr_size; + inet_address_t gw; + char iface[IFNAMSIZ]; +} route_res_t; + +static int +sitnl_route_save(struct nlmsghdr *n, void *arg) +{ + route_res_t *res = arg; + struct rtmsg *r = NLMSG_DATA(n); + struct rtattr *rta = RTM_RTA(r); + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + unsigned int ifindex = 0; + + while (RTA_OK(rta, len)) + { + switch (rta->rta_type) + { + /* route interface */ + case RTA_OIF: + ifindex = *(unsigned int *)RTA_DATA(rta); + break; + + /* route prefix */ + case RTA_DST: + break; + + /* GW for the route */ + case RTA_GATEWAY: + memcpy(&res->gw, RTA_DATA(rta), res->addr_size); + break; + } + + rta = RTA_NEXT(rta, len); + } + + if (!if_indextoname(ifindex, res->iface)) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifname for index %d", + __func__, ifindex); + return -1; + } + + return 0; +} + +static int +sitnl_route_best_gw(sa_family_t af_family, const inet_address_t *dst, + void *best_gw, char *best_iface) +{ + struct sitnl_route_req req; + route_res_t res; + int ret = -EINVAL; + + ASSERT(best_gw); + ASSERT(best_iface); + + CLEAR(req); + CLEAR(res); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = RTM_GETROUTE; + req.n.nlmsg_flags = NLM_F_REQUEST; + + req.r.rtm_family = af_family; + + switch (af_family) + { + case AF_INET: + res.addr_size = sizeof(in_addr_t); + req.n.nlmsg_flags |= NLM_F_DUMP; + break; + + case AF_INET6: + res.addr_size = sizeof(struct in6_addr); + break; + + default: + /* unsupported */ + return -EINVAL; + } + + SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, dst, res.addr_size); + + ret = sitnl_send(&req.n, 0, 0, sitnl_route_save, &res); + if (ret < 0) + { + goto err; + } + + /* save result in output variables */ + memcpy(best_gw, &res.gw, res.addr_size); + strncpy(best_iface, res.iface, IFNAMSIZ); +err: + return ret; + +} + +/* used by iproute2 implementation too */ +int +net_route_v6_best_gw(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + struct in6_addr *best_gw, char *best_iface) +{ + inet_address_t dst_v6 = {0}; + char buf[INET6_ADDRSTRLEN]; + int ret; + + if (dst) + { + dst_v6.ipv6 = *dst; + } + + msg(D_ROUTE, "%s query: dst %s", __func__, + inet_ntop(AF_INET6, &dst_v6.ipv6, buf, sizeof(buf))); + + ret = sitnl_route_best_gw(AF_INET6, &dst_v6, best_gw, best_iface); + if (ret < 0) + { + return ret; + } + + msg(D_ROUTE, "%s result: via %s dev %s", __func__, + inet_ntop(AF_INET6, best_gw, buf, sizeof(buf)), best_iface); + + return ret; + +} + +#ifdef ENABLE_SITNL + +int +net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx) +{ + (void)c; + (void)ctx; + + return 0; +} + +void +net_ctx_reset(openvpn_net_ctx_t *ctx) +{ + (void)ctx; +} + +void +net_ctx_free(openvpn_net_ctx_t *ctx) +{ + (void)ctx; +} + +int +net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + in_addr_t *best_gw, char *best_iface) +{ + inet_address_t dst_v4 = {0}; + char buf[INET_ADDRSTRLEN]; + int ret; + + if (dst) + { + dst_v4.ipv4 = htonl(*dst); + } + + msg(D_ROUTE, "%s query: dst %s", __func__, + inet_ntop(AF_INET, &dst_v4.ipv4, buf, sizeof(buf))); + + ret = sitnl_route_best_gw(AF_INET, &dst_v4, best_gw, best_iface); + if (ret < 0) + { + return ret; + } + + msg(D_ROUTE, "%s result: via %s dev %s", __func__, + inet_ntop(AF_INET, best_gw, buf, sizeof(buf)), best_iface); + + /* result is expected in Host Order */ + *best_gw = ntohl(*best_gw); + + return ret; +} + +int +net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up) +{ + struct sitnl_link_req req; + int ifindex; + + CLEAR(req); + + if (!iface) + { + msg(M_WARN, "%s: passed NULL interface", __func__); + return -EINVAL; + } + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN, "%s: rtnl: cannot get ifindex for %s: %s", __func__, iface, + strerror(errno)); + return -ENOENT; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ifindex; + req.i.ifi_change |= IFF_UP; + if (up) + { + req.i.ifi_flags |= IFF_UP; + } + else + { + req.i.ifi_flags &= ~IFF_UP; + } + + msg(M_INFO, "%s: set %s %s", __func__, iface, up ? "up" : "down"); + + return sitnl_send(&req.n, 0, 0, NULL, NULL); +} + +int +net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, + uint32_t mtu) +{ + struct sitnl_link_req req; + int ifindex, ret = -1; + + CLEAR(req); + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, + iface); + return -1; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ifindex; + + SITNL_ADDATTR(&req.n, sizeof(req), IFLA_MTU, &mtu, 4); + + msg(M_INFO, "%s: mtu %u for %s", __func__, mtu, iface); + + ret = sitnl_send(&req.n, 0, 0, NULL, NULL); +err: + return ret; +} + +static int +sitnl_addr_set(int cmd, uint32_t flags, int ifindex, sa_family_t af_family, + const inet_address_t *local, const inet_address_t *remote, + int prefixlen) +{ + struct sitnl_addr_req req; + uint32_t size; + int ret = -EINVAL; + + CLEAR(req); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_type = cmd; + req.n.nlmsg_flags = NLM_F_REQUEST | flags; + + req.i.ifa_index = ifindex; + req.i.ifa_family = af_family; + + switch (af_family) + { + case AF_INET: + size = sizeof(struct in_addr); + break; + + case AF_INET6: + size = sizeof(struct in6_addr); + break; + + default: + msg(M_WARN, "%s: rtnl: unknown address family %d", __func__, + af_family); + return -EINVAL; + } + + /* if no prefixlen has been specified, assume host address */ + if (prefixlen == 0) + { + prefixlen = size * 8; + } + req.i.ifa_prefixlen = prefixlen; + + if (remote) + { + SITNL_ADDATTR(&req.n, sizeof(req), IFA_ADDRESS, remote, size); + } + + if (local) + { + SITNL_ADDATTR(&req.n, sizeof(req), IFA_LOCAL, local, size); + } + + ret = sitnl_send(&req.n, 0, 0, NULL, NULL); + if (ret == -EEXIST) + { + ret = 0; + } +err: + return ret; +} + +static int +sitnl_addr_ptp_add(sa_family_t af_family, const char *iface, + const inet_address_t *local, + const inet_address_t *remote) +{ + int ifindex; + + switch (af_family) + { + case AF_INET: + case AF_INET6: + break; + + default: + return -EINVAL; + } + + if (!iface) + { + msg(M_WARN, "%s: passed NULL interface", __func__); + return -EINVAL; + } + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN, "%s: cannot get ifindex for %s: %s", __func__, np(iface), + strerror(errno)); + return -ENOENT; + } + + return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, ifindex, + af_family, local, remote, 0); +} + +static int +sitnl_addr_ptp_del(sa_family_t af_family, const char *iface, + const inet_address_t *local) +{ + int ifindex; + + switch (af_family) + { + case AF_INET: + case AF_INET6: + break; + + default: + return -EINVAL; + } + + if (!iface) + { + msg(M_WARN, "%s: passed NULL interface", __func__); + return -EINVAL; + } + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: cannot get ifindex for %s", __func__, iface); + return -ENOENT; + } + + return sitnl_addr_set(RTM_DELADDR, 0, ifindex, af_family, local, NULL, 0); +} + +static int +sitnl_route_set(int cmd, uint32_t flags, int ifindex, sa_family_t af_family, + const void *dst, int prefixlen, + const void *gw, enum rt_class_t table, int metric, + enum rt_scope_t scope, int protocol, int type) +{ + struct sitnl_route_req req; + int ret = -1, size; + + CLEAR(req); + + switch (af_family) + { + case AF_INET: + size = sizeof(in_addr_t); + break; + + case AF_INET6: + size = sizeof(struct in6_addr); + break; + + default: + return -EINVAL; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = cmd; + req.n.nlmsg_flags = NLM_F_REQUEST | flags; + + req.r.rtm_family = af_family; + req.r.rtm_scope = scope; + req.r.rtm_protocol = protocol; + req.r.rtm_type = type; + req.r.rtm_dst_len = prefixlen; + + if (table < 256) + { + req.r.rtm_table = table; + } + else + { + req.r.rtm_table = RT_TABLE_UNSPEC; + SITNL_ADDATTR(&req.n, sizeof(req), RTA_TABLE, &table, 4); + } + + if (dst) + { + SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, dst, size); + } + + if (gw) + { + SITNL_ADDATTR(&req.n, sizeof(req), RTA_GATEWAY, gw, size); + } + + if (ifindex > 0) + { + SITNL_ADDATTR(&req.n, sizeof(req), RTA_OIF, &ifindex, 4); + } + + if (metric > 0) + { + SITNL_ADDATTR(&req.n, sizeof(req), RTA_PRIORITY, &metric, 4); + } + + ret = sitnl_send(&req.n, 0, 0, NULL, NULL); + if (ret == -EEXIST) + { + ret = 0; + } +err: + return ret; +} + +static int +sitnl_addr_add(sa_family_t af_family, const char *iface, + const inet_address_t *addr, int prefixlen) +{ + int ifindex; + + switch (af_family) + { + case AF_INET: + case AF_INET6: + break; + + default: + return -EINVAL; + } + + if (!iface) + { + msg(M_WARN, "%s: passed NULL interface", __func__); + return -EINVAL; + } + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, + iface); + return -ENOENT; + } + + return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, ifindex, + af_family, addr, NULL, prefixlen); +} + +static int +sitnl_addr_del(sa_family_t af_family, const char *iface, inet_address_t *addr, + int prefixlen) +{ + int ifindex; + + switch (af_family) + { + case AF_INET: + case AF_INET6: + break; + + default: + return -EINVAL; + } + + if (!iface) + { + msg(M_WARN, "%s: passed NULL interface", __func__); + return -EINVAL; + } + + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: cannot get ifindex for %s", __func__, + iface); + return -ENOENT; + } + + return sitnl_addr_set(RTM_DELADDR, 0, ifindex, af_family, addr, NULL, + prefixlen); +} + +int +net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + inet_address_t addr_v4 = { 0 }; + char buf[INET_ADDRSTRLEN]; + + if (!addr) + { + return -EINVAL; + } + + addr_v4.ipv4 = htonl(*addr); + + msg(M_INFO, "%s: %s/%d dev %s", __func__, + inet_ntop(AF_INET, &addr_v4.ipv4, buf, sizeof(buf)), prefixlen,iface); + + return sitnl_addr_add(AF_INET, iface, &addr_v4, prefixlen); +} + +int +net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + inet_address_t addr_v6 = { 0 }; + char buf[INET6_ADDRSTRLEN]; + + if (!addr) + { + return -EINVAL; + } + + addr_v6.ipv6 = *addr; + + msg(M_INFO, "%s: %s/%d dev %s", __func__, + inet_ntop(AF_INET6, &addr_v6.ipv6, buf, sizeof(buf)), prefixlen, iface); + + return sitnl_addr_add(AF_INET6, iface, &addr_v6, prefixlen); +} + +int +net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + inet_address_t addr_v4 = { 0 }; + char buf[INET_ADDRSTRLEN]; + + if (!addr) + { + return -EINVAL; + } + + addr_v4.ipv4 = htonl(*addr); + + msg(M_INFO, "%s: %s dev %s", __func__, + inet_ntop(AF_INET, &addr_v4.ipv4, buf, sizeof(buf)), iface); + + return sitnl_addr_del(AF_INET, iface, &addr_v4, prefixlen); +} + +int +net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + inet_address_t addr_v6 = { 0 }; + char buf[INET6_ADDRSTRLEN]; + + if (!addr) + { + return -EINVAL; + } + + addr_v6.ipv6 = *addr; + + msg(M_INFO, "%s: %s/%d dev %s", __func__, + inet_ntop(AF_INET6, &addr_v6.ipv6, buf, sizeof(buf)), prefixlen, iface); + + return sitnl_addr_del(AF_INET6, iface, &addr_v6, prefixlen); +} + +int +net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + inet_address_t local_v4 = { 0 }; + inet_address_t remote_v4 = { 0 }; + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + + if (!local) + { + return -EINVAL; + } + + local_v4.ipv4 = htonl(*local); + + if (remote) + { + remote_v4.ipv4 = htonl(*remote); + } + + msg(M_INFO, "%s: %s peer %s dev %s", __func__, + inet_ntop(AF_INET, &local_v4.ipv4, buf1, sizeof(buf1)), + inet_ntop(AF_INET, &remote_v4.ipv4, buf2, sizeof(buf2)), iface); + + return sitnl_addr_ptp_add(AF_INET, iface, &local_v4, &remote_v4); +} + +int +net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + inet_address_t local_v4 = { 0 }; + char buf[INET6_ADDRSTRLEN]; + + + if (!local) + { + return -EINVAL; + } + + local_v4.ipv4 = htonl(*local); + + msg(M_INFO, "%s: %s dev %s", __func__, + inet_ntop(AF_INET, &local_v4.ipv4, buf, sizeof(buf)), iface); + + return sitnl_addr_ptp_del(AF_INET, iface, &local_v4); +} + +static int +sitnl_route_add(const char *iface, sa_family_t af_family, const void *dst, + int prefixlen, const void *gw, uint32_t table, int metric) +{ + enum rt_scope_t scope = RT_SCOPE_UNIVERSE; + int ifindex = 0; + + if (iface) + { + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifindex for %s", + __func__, iface); + return -ENOENT; + } + } + + if (table == 0) + { + table = RT_TABLE_MAIN; + } + + if (!gw && iface) + { + scope = RT_SCOPE_LINK; + } + + return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, ifindex, + af_family, dst, prefixlen, gw, table, metric, scope, + RTPROT_BOOT, RTN_UNICAST); +} + +int +net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, + uint32_t table, int metric) +{ + in_addr_t *dst_ptr = NULL, *gw_ptr = NULL; + in_addr_t dst_be = 0, gw_be = 0; + char dst_str[INET_ADDRSTRLEN]; + char gw_str[INET_ADDRSTRLEN]; + + if (dst) + { + dst_be = htonl(*dst); + dst_ptr = &dst_be; + } + + if (gw) + { + gw_be = htonl(*gw); + gw_ptr = &gw_be; + } + + msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__, + inet_ntop(AF_INET, &dst_be, dst_str, sizeof(dst_str)), + prefixlen, inet_ntop(AF_INET, &gw_be, gw_str, sizeof(gw_str)), + np(iface), table, metric); + + return sitnl_route_add(iface, AF_INET, dst_ptr, prefixlen, gw_ptr, table, + metric); +} + +int +net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, + const char *iface, uint32_t table, int metric) +{ + inet_address_t dst_v6 = { 0 }; + inet_address_t gw_v6 = { 0 }; + char dst_str[INET6_ADDRSTRLEN]; + char gw_str[INET6_ADDRSTRLEN]; + + if (dst) + { + dst_v6.ipv6 = *dst; + } + + if (gw) + { + gw_v6.ipv6 = *gw; + } + + msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__, + inet_ntop(AF_INET6, &dst_v6.ipv6, dst_str, sizeof(dst_str)), + prefixlen, inet_ntop(AF_INET6, &gw_v6.ipv6, gw_str, sizeof(gw_str)), + np(iface), table, metric); + + return sitnl_route_add(iface, AF_INET6, dst, prefixlen, gw, table, + metric); +} + +static int +sitnl_route_del(const char *iface, sa_family_t af_family, inet_address_t *dst, + int prefixlen, inet_address_t *gw, uint32_t table, + int metric) +{ + int ifindex = 0; + + if (iface) + { + ifindex = if_nametoindex(iface); + if (ifindex == 0) + { + msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifindex for %s", + __func__, iface); + return -ENOENT; + } + } + + if (table == 0) + { + table = RT_TABLE_MAIN; + } + + return sitnl_route_set(RTM_DELROUTE, 0, ifindex, af_family, dst, prefixlen, + gw, table, metric, RT_SCOPE_NOWHERE, 0, 0); +} + +int +net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric) +{ + inet_address_t dst_v4 = { 0 }; + inet_address_t gw_v4 = { 0 }; + char dst_str[INET_ADDRSTRLEN]; + char gw_str[INET_ADDRSTRLEN]; + + if (dst) + { + dst_v4.ipv4 = htonl(*dst); + } + + if (gw) + { + gw_v4.ipv4 = htonl(*gw); + } + + msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__, + inet_ntop(AF_INET, &dst_v4.ipv4, dst_str, sizeof(dst_str)), + prefixlen, inet_ntop(AF_INET, &gw_v4.ipv4, gw_str, sizeof(gw_str)), + np(iface), table, metric); + + return sitnl_route_del(iface, AF_INET, &dst_v4, prefixlen, &gw_v4, table, + metric); +} + +int +net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, + const char *iface, uint32_t table, int metric) +{ + inet_address_t dst_v6 = { 0 }; + inet_address_t gw_v6 = { 0 }; + char dst_str[INET6_ADDRSTRLEN]; + char gw_str[INET6_ADDRSTRLEN]; + + if (dst) + { + dst_v6.ipv6 = *dst; + } + + if (gw) + { + gw_v6.ipv6 = *gw; + } + + msg(D_ROUTE, "%s: %s/%d via %s dev %s table %d metric %d", __func__, + inet_ntop(AF_INET6, &dst_v6.ipv6, dst_str, sizeof(dst_str)), + prefixlen, inet_ntop(AF_INET6, &gw_v6.ipv6, gw_str, sizeof(gw_str)), + np(iface), table, metric); + + return sitnl_route_del(iface, AF_INET6, &dst_v6, prefixlen, &gw_v6, + table, metric); +} + +#endif /* !ENABLE_SITNL */ + +#endif /* TARGET_LINUX */ diff --git a/src/openvpn/networking_sitnl.h b/src/openvpn/networking_sitnl.h new file mode 100644 index 0000000..6396b06 --- /dev/null +++ b/src/openvpn/networking_sitnl.h @@ -0,0 +1,28 @@ +/* + * Generic interface to platform specific networking code + * + * Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc> + * + * 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 NETWORKING_SITNL_H_ +#define NETWORKING_SITNL_H_ + +typedef char openvpn_net_iface_t; +typedef void *openvpn_net_ctx_t; + +#endif /* NETWORKING_SITNL_H_ */ diff --git a/src/openvpn/ntlm.c b/src/openvpn/ntlm.c index 077fa3e..e370748 100644 --- a/src/openvpn/ntlm.c +++ b/src/openvpn/ntlm.c @@ -314,8 +314,8 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, * byte order on the wire for the NTLM header is LE. */ const size_t hoff = 0x14; - unsigned long flags = buf2[hoff] | (buf2[hoff + 1] << 8) | - (buf2[hoff + 2] << 16) | (buf2[hoff + 3] << 24); + unsigned long flags = buf2[hoff] | (buf2[hoff + 1] << 8) + |(buf2[hoff + 2] << 16) | (buf2[hoff + 3] << 24); if ((flags & 0x00800000) == 0x00800000) { tib_len = buf2[0x28]; /* Get Target Information block size */ diff --git a/src/openvpn/occ-inline.h b/src/openvpn/occ-inline.h deleted file mode 100644 index 7f6f1b2..0000000 --- a/src/openvpn/occ-inline.h +++ /dev/null @@ -1,95 +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-2018 OpenVPN Inc <sales@openvpn.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef OCC_INLINE_H -#define OCC_INLINE_H - -#ifdef ENABLE_OCC - -/* - * Inline functions - */ - -static inline int -occ_reset_op(void) -{ - return -1; -} - -/* - * Should we send an OCC_REQUEST message? - */ -static inline void -check_send_occ_req(struct context *c) -{ - void check_send_occ_req_dowork(struct context *c); - - if (event_timeout_defined(&c->c2.occ_interval) - && event_timeout_trigger(&c->c2.occ_interval, - &c->c2.timeval, - (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0)) - { - check_send_occ_req_dowork(c); - } -} - -/* - * Should we send an MTU load test? - */ -static inline void -check_send_occ_load_test(struct context *c) -{ - void check_send_occ_load_test_dowork(struct context *c); - - if (event_timeout_defined(&c->c2.occ_mtu_load_test_interval) - && event_timeout_trigger(&c->c2.occ_mtu_load_test_interval, - &c->c2.timeval, - (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0)) - { - check_send_occ_load_test_dowork(c); - } -} - -/* - * Should we send an OCC message? - */ -static inline void -check_send_occ_msg(struct context *c) -{ - void check_send_occ_msg_dowork(struct context *c); - - if (c->c2.occ_op >= 0) - { - if (!TO_LINK_DEF(c)) - { - check_send_occ_msg_dowork(c); - } - else - { - tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */ - } - } -} - -#endif /* ifdef ENABLE_OCC */ -#endif /* ifndef OCC_INLINE_H */ diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c index 80504af..3ff351a 100644 --- a/src/openvpn/occ.c +++ b/src/openvpn/occ.c @@ -29,14 +29,10 @@ #include "syshead.h" -#ifdef ENABLE_OCC - #include "occ.h" - +#include "forward.h" #include "memdbg.h" -#include "forward-inline.h" -#include "occ-inline.h" /* * This random string identifies an OpenVPN @@ -426,10 +422,3 @@ process_received_occ_msg(struct context *c) } c->c2.buf.len = 0; /* don't pass packet on */ } - -#else /* ifdef ENABLE_OCC */ -static void -dummy(void) -{ -} -#endif /* ifdef ENABLE_OCC */ diff --git a/src/openvpn/occ.h b/src/openvpn/occ.h index f6ff5f9..504c8c4 100644 --- a/src/openvpn/occ.h +++ b/src/openvpn/occ.h @@ -24,8 +24,6 @@ #ifndef OCC_H #define OCC_H -#ifdef ENABLE_OCC - #include "forward.h" /* OCC_STRING_SIZE must be set to sizeof (occ_magic) */ @@ -90,5 +88,69 @@ is_occ_msg(const struct buffer *buf) void process_received_occ_msg(struct context *c); -#endif /* ifdef ENABLE_OCC */ +void check_send_occ_req_dowork(struct context *c); + +void check_send_occ_load_test_dowork(struct context *c); + +void check_send_occ_msg_dowork(struct context *c); + +/* + * Inline functions + */ + +static inline int +occ_reset_op(void) +{ + return -1; +} + +/* + * Should we send an OCC_REQUEST message? + */ +static inline void +check_send_occ_req(struct context *c) +{ + if (event_timeout_defined(&c->c2.occ_interval) + && event_timeout_trigger(&c->c2.occ_interval, + &c->c2.timeval, + (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0)) + { + check_send_occ_req_dowork(c); + } +} + +/* + * Should we send an MTU load test? + */ +static inline void +check_send_occ_load_test(struct context *c) +{ + if (event_timeout_defined(&c->c2.occ_mtu_load_test_interval) + && event_timeout_trigger(&c->c2.occ_mtu_load_test_interval, + &c->c2.timeval, + (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0)) + { + check_send_occ_load_test_dowork(c); + } +} + +/* + * Should we send an OCC message? + */ +static inline void +check_send_occ_msg(struct context *c) +{ + if (c->c2.occ_op >= 0) + { + if (!TO_LINK_DEF(c)) + { + check_send_occ_msg_dowork(c); + } + else + { + tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */ + } + } +} + #endif /* ifndef OCC_H */ diff --git a/src/openvpn/openssl_compat.h b/src/openvpn/openssl_compat.h index 8acc7d1..eb6c9c9 100644 --- a/src/openvpn/openssl_compat.h +++ b/src/openvpn/openssl_compat.h @@ -42,6 +42,7 @@ #include "buffer.h" +#include <openssl/rsa.h> #include <openssl/ssl.h> #include <openssl/x509.h> @@ -182,6 +183,12 @@ SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx) } #endif +/* This function is implemented as macro, so the configure check for the + * function may fail, so we check for both variants here */ +#if !defined(HAVE_SSL_CTX_SET1_GROUPS) && !defined(SSL_CTX_set1_groups) +#define SSL_CTX_set1_groups SSL_CTX_set1_curves +#endif + #if !defined(HAVE_X509_GET0_PUBKEY) /** * Get the public key from a X509 certificate @@ -204,8 +211,8 @@ X509_get0_pubkey(const X509 *x) * @param store X509 object store * @return the X509 object stack */ -static inline STACK_OF(X509_OBJECT) * -X509_STORE_get0_objects(X509_STORE *store) +static inline STACK_OF(X509_OBJECT) +*X509_STORE_get0_objects(X509_STORE *store) { return store ? store->objs : NULL; } @@ -270,20 +277,6 @@ EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey) } #endif -#if !defined(HAVE_EVP_PKEY_ID) -/** - * Get the PKEY type - * - * @param pkey Public key object - * @return The key type - */ -static inline int -EVP_PKEY_id(const EVP_PKEY *pkey) -{ - return pkey ? pkey->type : EVP_PKEY_NONE; -} -#endif - #if !defined(HAVE_EVP_PKEY_GET0_DSA) /** * Get the DSA object of a public key @@ -380,7 +373,7 @@ RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) return 1; } -#endif +#endif /* if !defined(HAVE_RSA_SET0_KEY) */ #if !defined(HAVE_RSA_BITS) /** @@ -494,9 +487,9 @@ RSA_meth_free(RSA_METHOD *meth) */ static inline int RSA_meth_set_pub_enc(RSA_METHOD *meth, - int (*pub_enc) (int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, - int padding)) + int (*pub_enc)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) { if (meth) { @@ -517,9 +510,9 @@ RSA_meth_set_pub_enc(RSA_METHOD *meth, */ static inline int RSA_meth_set_pub_dec(RSA_METHOD *meth, - int (*pub_dec) (int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, - int padding)) + int (*pub_dec)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) { if (meth) { @@ -540,9 +533,9 @@ RSA_meth_set_pub_dec(RSA_METHOD *meth, */ static inline int RSA_meth_set_priv_enc(RSA_METHOD *meth, - int (*priv_enc) (int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, - int padding)) + int (*priv_enc)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) { if (meth) { @@ -563,9 +556,9 @@ RSA_meth_set_priv_enc(RSA_METHOD *meth, */ static inline int RSA_meth_set_priv_dec(RSA_METHOD *meth, - int (*priv_dec) (int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, - int padding)) + int (*priv_dec)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) { if (meth) { @@ -585,7 +578,7 @@ RSA_meth_set_priv_dec(RSA_METHOD *meth, * @return 1 on success, 0 on error */ static inline int -RSA_meth_set_init(RSA_METHOD *meth, int (*init) (RSA *rsa)) +RSA_meth_set_init(RSA_METHOD *meth, int (*init)(RSA *rsa)) { if (meth) { @@ -605,11 +598,12 @@ RSA_meth_set_init(RSA_METHOD *meth, int (*init) (RSA *rsa)) * @return 1 on success, 0 on error */ static inline -int RSA_meth_set_sign(RSA_METHOD *meth, - int (*sign) (int type, const unsigned char *m, - unsigned int m_length, - unsigned char *sigret, unsigned int *siglen, - const RSA *rsa)) +int +RSA_meth_set_sign(RSA_METHOD *meth, + int (*sign)(int type, const unsigned char *m, + unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, + const RSA *rsa)) { meth->rsa_sign = sign; return 1; @@ -625,7 +619,7 @@ int RSA_meth_set_sign(RSA_METHOD *meth, * @return 1 on success, 0 on error */ static inline int -RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)) +RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa)) { if (meth) { @@ -680,7 +674,7 @@ RSA_meth_get0_app_data(const RSA_METHOD *meth) static inline int EC_GROUP_order_bits(const EC_GROUP *group) { - BIGNUM* order = BN_new(); + BIGNUM *order = BN_new(); EC_GROUP_get_order(group, order, NULL); int bits = BN_num_bits(order); BN_free(order); @@ -689,6 +683,14 @@ EC_GROUP_order_bits(const EC_GROUP *group) #endif /* SSLeay symbols have been renamed in OpenSSL 1.1 */ +#ifndef OPENSSL_VERSION +#define OPENSSL_VERSION SSLEAY_VERSION +#endif + +#ifndef HAVE_OPENSSL_VERSION +#define OpenSSL_version SSLeay_version +#endif + #if !defined(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT) #define RSA_F_RSA_OSSL_PRIVATE_ENCRYPT RSA_F_RSA_EAY_PRIVATE_ENCRYPT #endif @@ -704,18 +706,14 @@ SSL_CTX_get_min_proto_version(SSL_CTX *ctx) { return TLS1_VERSION; } -#ifdef SSL_OP_NO_TLSv1_1 if (!(sslopt & SSL_OP_NO_TLSv1_1)) { return TLS1_1_VERSION; } -#endif -#ifdef SSL_OP_NO_TLSv1_2 if (!(sslopt & SSL_OP_NO_TLSv1_2)) { return TLS1_2_VERSION; } -#endif return 0; } #endif /* SSL_CTX_get_min_proto_version */ @@ -727,18 +725,14 @@ static inline int SSL_CTX_get_max_proto_version(SSL_CTX *ctx) { long sslopt = SSL_CTX_get_options(ctx); -#ifdef SSL_OP_NO_TLSv1_2 if (!(sslopt & SSL_OP_NO_TLSv1_2)) { return TLS1_2_VERSION; } -#endif -#ifdef SSL_OP_NO_TLSv1_1 if (!(sslopt & SSL_OP_NO_TLSv1_1)) { return TLS1_1_VERSION; } -#endif if (!(sslopt & SSL_OP_NO_TLSv1)) { return TLS1_VERSION; diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index 3d244fc..857c5fa 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -37,8 +37,6 @@ #include "memdbg.h" -#include "forward-inline.h" - #define P2P_CHECK_SIG() EVENT_LOOP_CHECK_SIGNAL(c, process_signal_p2p, c); static bool @@ -48,28 +46,6 @@ process_signal_p2p(struct context *c) return process_signal(c); } -/* Write our PID to a file */ -static void -write_pid(const char *filename) -{ - if (filename) - { - unsigned int pid = 0; - FILE *fp = platform_fopen(filename, "w"); - if (!fp) - { - msg(M_ERR, "Open error on pid file %s", filename); - } - - pid = platform_getpid(); - fprintf(fp, "%u\n", pid); - if (fclose(fp)) - { - msg(M_ERR, "Close error on pid file %s", filename); - } - } -} - /**************************************************************************/ /** @@ -217,6 +193,8 @@ openvpn_main(int argc, char *argv[]) open_plugins(&c, true, OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE); #endif + net_ctx_init(&c, &c.net_ctx); + /* init verbosity and mute levels */ init_verb_mute(&c, IVM_LEVEL_1); @@ -236,7 +214,7 @@ openvpn_main(int argc, char *argv[]) } /* tun/tap persist command? */ - if (do_persist_tuntap(&c.options)) + if (do_persist_tuntap(&c.options, &c.net_ctx)) { break; } @@ -274,7 +252,7 @@ openvpn_main(int argc, char *argv[]) if (c.first_time) { c.did_we_daemonize = possibly_become_daemon(&c.options); - write_pid(c.options.writepid); + write_pid_file(c.options.writepid, c.options.chroot_dir); } #ifdef ENABLE_MANAGEMENT @@ -305,12 +283,10 @@ openvpn_main(int argc, char *argv[]) tunnel_point_to_point(&c); break; -#if P2MP_SERVER case MODE_SERVER: tunnel_server(&c); break; -#endif default: ASSERT(0); } @@ -332,6 +308,7 @@ openvpn_main(int argc, char *argv[]) env_set_destroy(c.es); uninit_options(&c.options); gc_reset(&c.gc); + net_ctx_free(&c.net_ctx); } while (c.sig->signal_received == SIGHUP); } diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h index ed7975c..a7b5977 100644 --- a/src/openvpn/openvpn.h +++ b/src/openvpn/openvpn.h @@ -42,10 +42,10 @@ #include "sig.h" #include "misc.h" #include "mbuf.h" +#include "pf.h" #include "pool.h" #include "plugin.h" #include "manage.h" -#include "pf.h" /* * Our global key schedules, packaged thusly @@ -54,7 +54,6 @@ struct key_schedule { -#ifdef ENABLE_CRYPTO /* which cipher, HMAC digest, and key sizes are we using? */ struct key_type key_type; @@ -67,9 +66,9 @@ struct key_schedule /* 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 */ + struct key_ctx tls_crypt_v2_server_key; + struct buffer tls_crypt_v2_wkc; /**< Wrapped client key */ + struct key_ctx auth_token_key; }; /* @@ -96,10 +95,8 @@ struct context_buffers struct buffer aux_buf; /* workspace buffers used by crypto routines */ -#ifdef ENABLE_CRYPTO struct buffer encrypt_buf; struct buffer decrypt_buf; -#endif /* workspace buffers for compression */ #ifdef USE_COMP @@ -193,12 +190,9 @@ struct context_1 bool socks_proxy_owned; #if P2MP - -#if P2MP_SERVER /* persist --ifconfig-pool db to file */ struct ifconfig_pool_persist *ifconfig_pool_persist; bool ifconfig_pool_persist_owned; -#endif /* if client mode, hash of option strings we pulled from server */ struct sha256_digest pulled_options_digest_save; @@ -216,6 +210,25 @@ struct context_1 #endif }; + +/* client authentication state, CAS_SUCCEEDED must be 0 since + * non multi code path still checks this variable but does not initialise it + * so the code depends on zero initialisation */ +enum client_connect_status { + CAS_SUCCEEDED=0, + CAS_PENDING, + CAS_PENDING_DEFERRED, + CAS_PENDING_DEFERRED_PARTIAL, /**< at least handler succeeded, no result yet*/ + CAS_FAILED, +}; + +static inline bool +is_cas_pending(enum client_connect_status cas) +{ + return cas == CAS_PENDING || cas == CAS_PENDING_DEFERRED + || cas == CAS_PENDING_DEFERRED_PARTIAL; +} + /** * Level 2 %context containing state that is reset on both \c SIGHUP and * \c SIGUSR1 restarts. @@ -307,7 +320,6 @@ struct context_2 struct event_timeout inactivity_interval; int inactivity_bytes; -#ifdef ENABLE_OCC /* the option strings must match across peers */ char *options_string_local; char *options_string_remote; @@ -315,7 +327,6 @@ struct context_2 int occ_op; /* INIT to -1 */ int occ_n_tries; struct event_timeout occ_interval; -#endif /* * Keep track of maximum packet size received so far @@ -327,15 +338,12 @@ struct context_2 int max_send_size_local; /* max packet size sent */ int max_send_size_remote; /* max packet size sent by remote */ -#ifdef ENABLE_OCC + /* remote wants us to send back a load test packet of this size */ int occ_mtu_load_size; struct event_timeout occ_mtu_load_test_interval; int occ_mtu_load_n_tries; -#endif - -#ifdef ENABLE_CRYPTO /* * TLS-mode crypto objects. @@ -368,8 +376,6 @@ struct context_2 struct event_timeout packet_id_persist_interval; -#endif /* ENABLE_CRYPTO */ - #ifdef USE_COMP struct compress_context *comp_context; /**< Compression context used by the @@ -424,13 +430,11 @@ struct context_2 /* indicates that the do_up_delay function has run */ bool do_up_ran; -#ifdef ENABLE_OCC /* indicates that we have received a SIGTERM when * options->explicit_exit_notification is enabled, * but we have not exited yet */ time_t explicit_exit_notification_time_wait; struct event_timeout explicit_exit_notification_interval; -#endif /* environmental variables to pass to scripts */ struct env_set *es; @@ -441,12 +445,8 @@ struct context_2 #if P2MP -#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; @@ -458,14 +458,8 @@ struct context_2 int push_ifconfig_ipv6_netbits; struct in6_addr push_ifconfig_ipv6_remote; - /* client authentication state, CAS_SUCCEEDED must be 0 */ -#define CAS_SUCCEEDED 0 -#define CAS_PENDING 1 -#define CAS_FAILED 2 -#define CAS_PARTIAL 3 /* at least one client-connect script/plugin - * succeeded while a later one in the chain failed */ - int context_auth; -#endif /* if P2MP_SERVER */ + + enum client_connect_status context_auth; struct event_timeout push_request_interval; int n_sent_push_requests; @@ -531,6 +525,8 @@ struct context struct env_set *es; /**< Set of environment variables. */ + openvpn_net_ctx_t net_ctx; /**< Networking API opaque context */ + struct signal_info *sig; /**< Internal error signaling object. */ struct plugin_list *plugins; /**< List of plug-ins. */ @@ -567,7 +563,6 @@ struct context * have been compiled in. */ -#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), \ @@ -575,22 +570,8 @@ struct context |(c->c2.tls_multi ? PD_TLS : 0) \ |(c->options.tls_auth_file ? c->c1.ks.key_type.hmac_length : 0), \ gc) -#else /* ifdef ENABLE_CRYPTO */ -#define TLS_MODE(c) (false) -#define PROTO_DUMP(buf, gc) format_hex(BPTR(buf), BLEN(buf), 80, gc) -#endif -#ifdef ENABLE_CRYPTO -#define MD5SUM(buf, len, gc) md5sum((buf), (len), 0, (gc)) -#else -#define MD5SUM(buf, len, gc) "[unavailable]" -#endif - -#ifdef ENABLE_CRYPTO #define CIPHER_ENABLED(c) (c->c1.ks.key_type.cipher != NULL) -#else -#define CIPHER_ENABLED(c) (false) -#endif /* this represents "disabled peer-id" */ #define MAX_PEER_ID 0xFFFFFF diff --git a/src/openvpn/openvpn.manifest b/src/openvpn/openvpn.manifest new file mode 100644 index 0000000..fa5b3d7 --- /dev/null +++ b/src/openvpn/openvpn.manifest @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + </application> + </compatibility> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <!-- + UAC settings: + - app should run at same integrity level as calling process + - app does not need to manipulate windows belonging to + higher-integrity-level processes + --> + <requestedExecutionLevel + level="asInvoker" + uiAccess="false" + /> + </requestedPrivileges> + </security> + </trustInfo> +</assembly> diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj index d1c0fde..5367979 100644 --- a/src/openvpn/openvpn.vcxproj +++ b/src/openvpn/openvpn.vcxproj @@ -1,105 +1,150 @@ <?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}</ProjectGuid> <RootNamespace>openvpn</RootNamespace> <Keyword>Win32Proj</Keyword> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v142</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> - <CharacterSet>Unicode</CharacterSet> - <PlatformToolset>v120</PlatformToolset> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <CharacterSet>NotSet</CharacterSet> + <PlatformToolset>v142</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Release.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Release.props" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Debug.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Debug.props" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Platform)-Output\$(Configuration)\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> - <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Platform)-Output\$(Configuration)\</OutDir> - <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir> - <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <GenerateManifest>false</GenerateManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <GenerateManifest>false</GenerateManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <GenerateManifest>false</GenerateManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <GenerateManifest>false</GenerateManifest> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> - <Optimization>Disabled</Optimization> - <AdditionalIncludeDirectories>$(SOURCEBASE);$(SOURCEBASE)/src/compat;$(SOURCEBASE)/include;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions> - <MinimalRebuild>true</MinimalRebuild> - <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> - <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>EditAndContinue</DebugInformationFormat> - <UndefinePreprocessorDefinitions>UNICODE</UndefinePreprocessorDefinitions> + <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions> + <WarningLevel>Level2</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> </ClCompile> - <ResourceCompile> - <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ResourceCompile> + <ResourceCompile /> <Link> - <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)</AdditionalDependencies> + <AdditionalDependencies>legacy_stdio_definitions.lib;Ncrypt.lib;libssl.lib;libcrypto.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)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions> + <WarningLevel>Level2</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + </ClCompile> + <ResourceCompile /> + <Link> + <AdditionalDependencies>legacy_stdio_definitions.lib;Ncrypt.lib;libssl.lib;libcrypto.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> - <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> - <Optimization>MaxSpeed</Optimization> - <IntrinsicFunctions>true</IntrinsicFunctions> - <AdditionalIncludeDirectories>$(SOURCEBASE);$(SOURCEBASE)/src/compat;$(SOURCEBASE)/include;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions> - <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> - <FunctionLevelLinking>true</FunctionLevelLinking> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> - <UndefinePreprocessorDefinitions>UNICODE</UndefinePreprocessorDefinitions> + <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions> + <WarningLevel>Level2</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + </ClCompile> + <ResourceCompile /> + <Link> + <AdditionalDependencies>legacy_stdio_definitions.lib;Ncrypt.lib;libssl.lib;libcrypto.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)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <SubSystem>Console</SubSystem> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions> + <WarningLevel>Level2</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> </ClCompile> - <ResourceCompile> - <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - </ResourceCompile> + <ResourceCompile /> <Link> - <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)</AdditionalDependencies> + <AdditionalDependencies>legacy_stdio_definitions.lib;Ncrypt.lib;libssl.lib;libcrypto.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> - <OptimizeReferences>true</OptimizeReferences> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="argv.c" /> + <ClCompile Include="auth_token.c" /> <ClCompile Include="base64.c" /> <ClCompile Include="block_dns.c" /> <ClCompile Include="buffer.c" /> @@ -112,6 +157,7 @@ <ClCompile Include="crypto.c" /> <ClCompile Include="crypto_openssl.c" /> <ClCompile Include="cryptoapi.c" /> + <ClCompile Include="env_set.c" /> <ClCompile Include="dhcp.c" /> <ClCompile Include="error.c" /> <ClCompile Include="event.c" /> @@ -156,6 +202,7 @@ <ClCompile Include="push.c" /> <ClCompile Include="reliable.c" /> <ClCompile Include="route.c" /> + <ClCompile Include="run_command.c" /> <ClCompile Include="schedule.c" /> <ClCompile Include="session_id.c" /> <ClCompile Include="shaper.c" /> @@ -164,15 +211,18 @@ <ClCompile Include="socks.c" /> <ClCompile Include="ssl.c" /> <ClCompile Include="ssl_openssl.c" /> + <ClCompile Include="ssl_ncp.c" /> <ClCompile Include="ssl_verify.c" /> <ClCompile Include="ssl_verify_openssl.c" /> <ClCompile Include="status.c" /> <ClCompile Include="tls_crypt.c" /> <ClCompile Include="tun.c" /> + <ClCompile Include="vlan.c" /> <ClCompile Include="win32.c" /> </ItemGroup> <ItemGroup> <ClInclude Include="argv.h" /> + <ClInclude Include="auth_token.h" /> <ClInclude Include="base64.h" /> <ClInclude Include="basic.h" /> <ClInclude Include="block_dns.h" /> @@ -189,11 +239,11 @@ <ClInclude Include="crypto_openssl.h" /> <ClInclude Include="cryptoapi.h" /> <ClInclude Include="dhcp.h" /> + <ClInclude Include="env_set.h" /> <ClInclude Include="errlevel.h" /> <ClInclude Include="error.h" /> <ClInclude Include="event.h" /> <ClInclude Include="fdmisc.h" /> - <ClInclude Include="forward-inline.h" /> <ClInclude Include="forward.h" /> <ClInclude Include="fragment.h" /> <ClInclude Include="gremlin.h" /> @@ -217,16 +267,13 @@ <ClInclude Include="mudp.h" /> <ClInclude Include="multi.h" /> <ClInclude Include="ntlm.h" /> - <ClInclude Include="occ-inline.h" /> <ClInclude Include="occ.h" /> <ClInclude Include="openvpn.h" /> <ClInclude Include="options.h" /> <ClInclude Include="otime.h" /> <ClInclude Include="packet_id.h" /> <ClInclude Include="perf.h" /> - <ClInclude Include="pf-inline.h" /> <ClInclude Include="pf.h" /> - <ClInclude Include="ping-inline.h" /> <ClInclude Include="ping.h" /> <ClInclude Include="pkcs11.h" /> <ClInclude Include="pkcs11_backend.h" /> @@ -239,7 +286,9 @@ <ClInclude Include="push.h" /> <ClInclude Include="pushlist.h" /> <ClInclude Include="reliable.h" /> + <ClInclude Include="ring_buffer.h" /> <ClInclude Include="route.h" /> + <ClInclude Include="run_command.h" /> <ClInclude Include="schedule.h" /> <ClInclude Include="session_id.h" /> <ClInclude Include="shaper.h" /> @@ -249,6 +298,7 @@ <ClInclude Include="ssl.h" /> <ClInclude Include="ssl_backend.h" /> <ClInclude Include="ssl_common.h" /> + <ClInclude Include="ssl_ncp.h" /> <ClInclude Include="ssl_openssl.h" /> <ClInclude Include="ssl_verify.h" /> <ClInclude Include="ssl_verify_backend.h" /> @@ -257,6 +307,7 @@ <ClInclude Include="syshead.h" /> <ClInclude Include="tls_crypt.h" /> <ClInclude Include="tun.h" /> + <ClInclude Include="vlan.h" /> <ClInclude Include="win32.h" /> </ItemGroup> <ItemGroup> @@ -272,7 +323,10 @@ <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> </ItemGroup> + <ItemGroup> + <Manifest Include="openvpn.manifest" /> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters index 30df5ec..cf5748c 100644 --- a/src/openvpn/openvpn.vcxproj.filters +++ b/src/openvpn/openvpn.vcxproj.filters @@ -228,6 +228,21 @@ <ClCompile Include="tls_crypt.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="env_set.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="run_command.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="auth_token.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vlan.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ssl_ncp.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="base64.h"> @@ -284,9 +299,6 @@ <ClInclude Include="fdmisc.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="forward-inline.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="forward.h"> <Filter>Header Files</Filter> </ClInclude> @@ -356,9 +368,6 @@ <ClInclude Include="ntlm.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="occ-inline.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="occ.h"> <Filter>Header Files</Filter> </ClInclude> @@ -377,15 +386,9 @@ <ClInclude Include="perf.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="pf-inline.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="pf.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="ping-inline.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="ping.h"> <Filter>Header Files</Filter> </ClInclude> @@ -488,10 +491,33 @@ <ClInclude Include="tls_crypt.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="env_set.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="run_command.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="auth_token.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="vlan.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ring_buffer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ssl_ncp.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="openvpn_win32_resources.rc"> <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <Manifest Include="openvpn.manifest"> + <Filter>Resource Files</Filter> + </Manifest> + </ItemGroup> </Project>
\ No newline at end of file diff --git a/src/openvpn/openvpn_win32_resources.rc b/src/openvpn/openvpn_win32_resources.rc index e4f1ee9..1ea5f87 100644 --- a/src/openvpn/openvpn_win32_resources.rc +++ b/src/openvpn/openvpn_win32_resources.rc @@ -7,6 +7,8 @@ #pragma code_page(65001) /* UTF8 */ +1 RT_MANIFEST "openvpn.manifest" + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL VS_VERSION_INFO VERSIONINFO diff --git a/src/openvpn/options.c b/src/openvpn/options.c index de30fcb..8bf82c5 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -41,9 +41,11 @@ #include "buffer.h" #include "error.h" #include "common.h" +#include "run_command.h" #include "shaper.h" #include "crypto.h" #include "ssl.h" +#include "ssl_ncp.h" #include "options.h" #include "misc.h" #include "socket.h" @@ -52,6 +54,7 @@ #include "win32.h" #include "push.h" #include "pool.h" +#include "proto.h" #include "helper.h" #include "manage.h" #include "forward.h" @@ -67,7 +70,6 @@ const char title_string[] = " [git:" CONFIGURE_GIT_REVISION CONFIGURE_GIT_FLAGS "]" #endif " " TARGET_ALIAS -#ifdef ENABLE_CRYPTO #if defined(ENABLE_CRYPTO_MBEDTLS) " [SSL (mbed TLS)]" #elif defined(ENABLE_CRYPTO_OPENSSL) @@ -75,7 +77,6 @@ const char title_string[] = #else " [SSL]" #endif /* defined(ENABLE_CRYPTO_MBEDTLS) */ -#endif /* ENABLE_CRYPTO */ #ifdef USE_COMP #ifdef ENABLE_LZO " [LZO]" @@ -103,9 +104,7 @@ const char title_string[] = " [MH/RECVDA]" #endif #endif -#ifdef HAVE_AEAD_CIPHER_MODES " [AEAD]" -#endif " built on " __DATE__ ; @@ -201,8 +200,10 @@ 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 'remote' in --ifconfig-ipv6\n" + " gateway default: taken from --route-ipv6-gateway or 'remote'\n" + " in --ifconfig-ipv6\n" "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n" + "--route-ipv6-gateway gw : Specify a default gateway for use with --route-ipv6.\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" " adding routes (may be 0). If not specified, routes will\n" @@ -226,10 +227,12 @@ 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" + "--block-ipv6 : (Client) Instead sending IPv6 to the server generate\n" + " ICMPv6 host unreachable messages on the client.\n" + " (Server) Instead of forwarding IPv6 packets send\n" + " ICMPv6 host unreachable packets to the client.\n" "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n" -#ifdef ENABLE_PUSH_PEER_INFO "--push-peer-info : (client only) push client info to server.\n" -#endif "--setenv name value : Set a custom environmental variable to pass to script.\n" "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to allow\n" " directives for future OpenVPN versions to be ignored.\n" @@ -274,9 +277,7 @@ static const char usage_message[] = " 'no' -- Never send DF (Don't Fragment) frames\n" " 'maybe' -- Use per-route hints\n" " 'yes' -- Always DF (Don't Fragment)\n" -#ifdef ENABLE_OCC "--mtu-test : Empirically measure and report MTU.\n" -#endif #ifdef ENABLE_FRAGMENT "--fragment max : Enable internal datagram fragmentation so that no UDP\n" " datagrams are sent which are larger than max bytes.\n" @@ -289,6 +290,9 @@ static const char usage_message[] = #if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK "--mark value : Mark encrypted packets being sent with value. The mark value\n" " can be matched in policy routing and packetfilter rules.\n" + "--bind-dev dev : Bind to the given device when making connection to a peer or\n" + " listening for connections. This allows sending encrypted packets\n" + " via a VRF present on the system.\n" #endif "--txqueuelen n : Set the tun/tap TX queue length to n (Linux only).\n" #ifdef ENABLE_MEMSTATS @@ -344,17 +348,16 @@ static const char usage_message[] = "--status file n : Write operational status to file every n seconds.\n" "--status-version [n] : Choose the status file format version number.\n" " Currently, n can be 1, 2, or 3 (default=1).\n" -#ifdef ENABLE_OCC "--disable-occ : Disable options consistency check between peers.\n" -#endif #ifdef ENABLE_DEBUG "--gremlin mask : Special stress testing mode (for debugging only).\n" #endif #if defined(USE_COMP) "--compress alg : Use compression algorithm alg\n" + "--allow-compression: Specify whether compression should be allowed\n" #if defined(ENABLE_LZO) "--comp-lzo : Use LZO compression -- may add up to 1 byte per\n" - " packet for uncompressible data.\n" + " packet for incompressible data.\n" "--comp-noadapt : Don't use adaptive compression when --comp-lzo\n" " is specified.\n" #endif @@ -401,8 +404,10 @@ static const char usage_message[] = "--plugin m [str]: Load plug-in module m passing str as an argument\n" " to its initialization function.\n" #endif + "--vlan-tagging : Enable 802.1Q-based VLAN tagging.\n" + "--vlan-accept tagged|untagged|all : Set VLAN tagging mode. Default is 'all'.\n" + "--vlan-pvid v : Sets the Port VLAN Identifier. Defaults to 1.\n" #if P2MP -#if P2MP_SERVER "\n" "Multi-Client Server options (when --mode server is used):\n" "--server network netmask : Helper option to easily configure server mode.\n" @@ -415,9 +420,6 @@ static const char usage_message[] = " client instance.\n" "--ifconfig-pool start-IP end-IP [netmask] : Set aside a pool of subnets\n" " to be dynamically allocated to connecting clients.\n" - "--ifconfig-pool-linear : (DEPRECATED) Use individual addresses rather \n" - " than /30 subnets\n in tun mode. Not compatible with\n" - " Windows clients.\n" "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n" " data to file, at seconds intervals (default=600).\n" " If seconds=0, file will be treated as read-only.\n" @@ -435,8 +437,6 @@ static const char usage_message[] = " Only valid in a client-specific config file.\n" "--disable : Client is disabled.\n" " Only valid in a client-specific config file.\n" - "--client-cert-not-required : (DEPRECATED) 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" @@ -448,7 +448,7 @@ static const char usage_message[] = " 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" + " to each client, replacing the password. Useful 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" @@ -487,7 +487,6 @@ static const char usage_message[] = " sessions to a web server at host:port. dir specifies an\n" " optional directory to write origin IP:port data.\n" #endif -#endif /* if P2MP_SERVER */ "\n" "Client options (when connecting to a multi-client server):\n" "--client : Helper option to easily configure client mode.\n" @@ -514,11 +513,8 @@ static const char usage_message[] = "--allow-recursive-routing : When this option is set, OpenVPN will not drop\n" " incoming tun packets with same destination as host.\n" #endif /* if P2MP */ -#ifdef ENABLE_OCC "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n" " server/remote. n = # of retries, default=1.\n" -#endif -#ifdef ENABLE_CRYPTO "\n" "Data Channel Encryption Options (must be compatible between peers):\n" "(These options are meaningful for both Static Key & TLS-mode)\n" @@ -535,8 +531,8 @@ 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" + "--data-ciphers list : List of ciphers that are allowed to be negotiated.\n" + "--ncp-disable : (DEPRECATED) 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 @@ -551,7 +547,6 @@ static const char usage_message[] = "--replay-window n [t] : Use a replay protection sliding window of size n\n" " and a time window of t seconds.\n" " Default n=%d t=%d\n" - "--no-iv : Disable cipher IV -- only allowed with CBC mode ciphers.\n" "--replay-persist file : Persist replay-protection state across sessions\n" " using file.\n" "--test-crypto : Run a self-test of crypto features enabled.\n" @@ -599,14 +594,17 @@ static const char usage_message[] = " Windows Certificate System Store.\n" #endif "--tls-cipher l : A list l of allowable TLS ciphers separated by : (optional).\n" - " : Use --show-tls to see a list of supported TLS ciphers.\n" + "--tls-ciphersuites l: A list of allowed TLS 1.3 cipher suites seperated by : (optional)\n" + " : Use --show-tls to see a list of supported TLS ciphers (suites).\n" "--tls-cert-profile p : Set the allowed certificate crypto algorithm profile\n" " (default=legacy).\n" "--tls-timeout n : Packet retransmit timeout on TLS control channel\n" " if no ACK from remote within n seconds (default=%d).\n" "--reneg-bytes n : Renegotiate data chan. key after n bytes sent and recvd.\n" "--reneg-pkts n : Renegotiate data chan. key after n packets sent and recvd.\n" - "--reneg-sec n : Renegotiate data chan. key after n seconds (default=%d).\n" + "--reneg-sec max [min] : Renegotiate data chan. key after at most max (default=%d)\n" + " and at least min (defaults to 90%% of max on servers and equal\n" + " to max on clients).\n" "--hand-window n : Data channel key exchange must finalize within n seconds\n" " of handshake initiation by any peer (default=%d).\n" "--tran-window n : Transition window -- old key can live this many seconds\n" @@ -625,6 +623,17 @@ static const char usage_message[] = " 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" + "--tls-crypt-v2 key : For clients: use key as a client-specific tls-crypt key.\n" + " For servers: use key to decrypt client-specific keys. For\n" + " key generation (--tls-crypt-v2-genkey): use key to\n" + " encrypt generated client-specific key. (See --tls-crypt.)\n" + "--genkey tls-crypt-v2-client [keyfile] [base64 metadata]: Generate a\n" + " fresh tls-crypt-v2 client key, and store to\n" + " keyfile. If supplied, include metadata in wrapped key.\n" + "--genkey tls-crypt-v2-server [keyfile] [base64 metadata]: Generate a\n" + " fresh tls-crypt-v2 server key, and store to keyfile\n" + "--tls-crypt-v2-verify cmd : Run command cmd to verify the metadata of the\n" + " client-supplied tls-crypt-v2 client key\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" @@ -643,7 +652,7 @@ static const char usage_message[] = " an explicit nsCertType designation t = 'client' | 'server'.\n" "--x509-track x : Save peer X509 attribute x in environment for use by\n" " plugins and management interface.\n" -#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 +#ifdef HAVE_EXPORT_KEYING_MATERIAL "--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 @@ -663,7 +672,7 @@ static const char usage_message[] = "--pkcs11-protected-authentication [0|1] ... : Use PKCS#11 protected authentication\n" " path. Set for each provider.\n" "--pkcs11-private-mode hex ... : PKCS#11 private key mode mask.\n" - " 0 : Try to determind automatically (default).\n" + " 0 : Try to determine automatically (default).\n" " 1 : Use Sign.\n" " 2 : Use SignRecover.\n" " 4 : Use Decrypt.\n" @@ -707,6 +716,7 @@ static const char usage_message[] = " which allow multiple addresses,\n" " --dhcp-option must be repeated.\n" " DOMAIN name : Set DNS suffix\n" + " DOMAIN-SEARCH entry : Add entry to DNS domain search list\n" " DNS addr : Set domain name server address(es) (IPv4 and IPv6)\n" " NTP : Set NTP server address(es)\n" " NBDD : Set NBDD server address(es)\n" @@ -730,9 +740,10 @@ 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 + "--windows-driver : Which tun driver to use?\n" + " tap-windows6 (default)\n" + " wintun\n" "--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" @@ -742,11 +753,9 @@ static const char usage_message[] = " to access TAP adapter.\n" #endif /* ifdef _WIN32 */ "\n" - "Generate a random key (only for non-TLS static key encryption mode):\n" - "--genkey : Generate a random key to be used as a shared secret,\n" - " for use with the --secret option.\n" - "--secret file : Write key to file.\n" -#endif /* ENABLE_CRYPTO */ + "Generate a new key :\n" + "--genkey secret file : Generate a new random key of type and write to file\n" + " (for use with --secret, --tls-auth or --tls-crypt)." #ifdef ENABLE_FEATURE_TUN_PERSIST "\n" "Tun/tap config mode (available with linux 2.4+):\n" @@ -812,9 +821,7 @@ init_options(struct options *o, const bool init_gc) o->resolve_retry_seconds = RESOLV_RETRY_INFINITE; o->resolve_in_advance = false; o->proto_force = -1; -#ifdef ENABLE_OCC o->occ = true; -#endif #ifdef ENABLE_MANAGEMENT o->management_log_history_cache = 250; o->management_echo_buffer_size = 100; @@ -823,9 +830,6 @@ init_options(struct options *o, const bool init_gc) #ifdef ENABLE_FEATURE_TUN_PERSIST o->persist_mode = 1; #endif -#ifdef TARGET_LINUX - o->tuntap_options.txqueuelen = 100; -#endif #ifdef _WIN32 #if 0 o->tuntap_options.ip_win32_type = IPW32_SET_ADAPTIVE; @@ -836,8 +840,10 @@ init_options(struct options *o, const bool init_gc) o->tuntap_options.dhcp_masq_offset = 0; /* use network address as internal DHCP server address */ o->route_method = ROUTE_METHOD_ADAPTIVE; o->block_outside_dns = false; + o->windows_driver = WINDOWS_DRIVER_TAP_WINDOWS6; #endif -#if P2MP_SERVER + o->vlan_accept = VLAN_ALL; + o->vlan_pvid = 1; o->real_hash_size = 256; o->virtual_hash_size = 256; o->n_bcast_buf = 256; @@ -846,17 +852,10 @@ init_options(struct options *o, const bool init_gc) o->max_routes_per_client = 256; o->stale_routes_check_interval = 0; o->ifconfig_pool_persist_refresh_freq = 600; -#endif #if P2MP o->scheduled_exit_interval = 5; #endif -#ifdef ENABLE_CRYPTO - o->ciphername = "BF-CBC"; -#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->prng_hash = "SHA1"; @@ -864,15 +863,14 @@ init_options(struct options *o, const bool init_gc) o->replay = true; o->replay_window = DEFAULT_SEQ_BACKTRACK; o->replay_time = DEFAULT_TIME_BACKTRACK; - o->use_iv = true; o->key_direction = KEY_DIRECTION_BIDIRECTIONAL; #ifdef ENABLE_PREDICTION_RESISTANCE o->use_prediction_resistance = false; #endif - o->key_method = 2; o->tls_timeout = 2; o->renegotiate_bytes = -1; o->renegotiate_seconds = 3600; + o->renegotiate_seconds_min = -1; o->handshake_window = 60; o->transition_window = 3600; o->tls_cert_profile = NULL; @@ -880,18 +878,16 @@ init_options(struct options *o, const bool init_gc) #ifdef ENABLE_X509ALTUSERNAME o->x509_username_field = X509_USERNAME_FIELD_DEFAULT; #endif -#endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 o->pkcs11_pin_cache_period = -1; #endif /* ENABLE_PKCS11 */ /* P2MP server context features */ -#if P2MP_SERVER o->auth_token_generate = false; /* Set default --tmp-dir */ #ifdef _WIN32 - /* On Windows, find temp dir via enviroment variables */ + /* On Windows, find temp dir via environment variables */ o->tmp_dir = win_get_tempdir(); #else /* Non-windows platforms use $TMPDIR, and if not set, default to '/tmp' */ @@ -901,7 +897,6 @@ init_options(struct options *o, const bool init_gc) o->tmp_dir = "/tmp"; } #endif /* _WIN32 */ -#endif /* P2MP_SERVER */ o->allow_recursive_routing = false; } @@ -957,6 +952,10 @@ pull_filter_type_name(int type) #define SHOW_PARM(name, value, format) msg(D_SHOW_PARMS, " " #name " = " format, (value)) #define SHOW_STR(var) SHOW_PARM(var, (o->var ? o->var : "[UNDEF]"), "'%s'") +#define SHOW_STR_INLINE(var) SHOW_PARM(var, \ + o->var ## _inline ? "[INLINE]" : \ + (o->var ? o->var : "[UNDEF]"), \ + "'%s'") #define SHOW_INT(var) SHOW_PARM(var, o->var, "%d") #define SHOW_UINT(var) SHOW_PARM(var, o->var, "%u") #define SHOW_UNSIGNED(var) SHOW_PARM(var, o->var, "0x%08x") @@ -994,7 +993,7 @@ setenv_settings(struct env_set *es, const struct options *o) setenv_int(es, "verb", o->verbosity); setenv_int(es, "daemon", o->daemon); setenv_int(es, "daemon_log_redirect", o->log); - setenv_unsigned(es, "daemon_start_time", time(NULL)); + setenv_long_long(es, "daemon_start_time", time(NULL)); setenv_int(es, "daemon_pid", platform_getpid()); if (o->connection_list) @@ -1083,7 +1082,6 @@ string_substitute(const char *src, int from, int to, struct gc_arena *gc) return ret; } -#ifdef ENABLE_CRYPTO static uint8_t * parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_arena *gc) { @@ -1125,13 +1123,22 @@ parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_aren } return ret; } -#endif /* ifdef ENABLE_CRYPTO */ #ifdef _WIN32 #ifndef ENABLE_SMALL static void +show_dhcp_option_list(const char *name, const char * const*array, int len) +{ + int i; + for (i = 0; i < len; ++i) + { + msg(D_SHOW_PARMS, " %s[%d] = %s", name, i, array[i] ); + } +} + +static void show_dhcp_option_addrs(const char *name, const in_addr_t *array, int len) { struct gc_arena gc = gc_new(); @@ -1166,6 +1173,7 @@ show_tuntap_options(const struct tuntap_options *o) show_dhcp_option_addrs("WINS", o->wins, o->wins_len); show_dhcp_option_addrs("NTP", o->ntp, o->ntp_len); show_dhcp_option_addrs("NBDD", o->nbdd, o->nbdd_len); + show_dhcp_option_list("DOMAIN-SEARCH", o->domain_search_list, o->domain_search_list_len); } #endif /* ifndef ENABLE_SMALL */ @@ -1216,6 +1224,23 @@ dhcp_option_address_parse(const char *name, const char *parm, in_addr_t *array, #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */ +static const char * +print_vlan_accept(enum vlan_acceptable_frames mode) +{ + switch (mode) + { + case VLAN_ONLY_TAGGED: + return "tagged"; + + case VLAN_ONLY_UNTAGGED_OR_PRIORITY: + return "untagged"; + + case VLAN_ALL: + return "all"; + } + return NULL; +} + #if P2MP #ifndef ENABLE_SMALL @@ -1225,7 +1250,6 @@ show_p2mp_parms(const struct options *o) { struct gc_arena gc = gc_new(); -#if P2MP_SERVER msg(D_SHOW_PARMS, " server_network = %s", print_in_addr_t(o->server_network, 0, &gc)); msg(D_SHOW_PARMS, " server_netmask = %s", print_in_addr_t(o->server_netmask, 0, &gc)); msg(D_SHOW_PARMS, " server_network_ipv6 = %s", print_in6_addr(o->server_network_ipv6, 0, &gc) ); @@ -1281,11 +1305,14 @@ show_p2mp_parms(const struct options *o) SHOW_BOOL(auth_user_pass_verify_script_via_file); SHOW_BOOL(auth_token_generate); SHOW_INT(auth_token_lifetime); + SHOW_STR_INLINE(auth_token_secret_file); #if PORT_SHARE SHOW_STR(port_share_host); SHOW_STR(port_share_port); #endif -#endif /* P2MP_SERVER */ + SHOW_BOOL(vlan_tagging); + msg(D_SHOW_PARMS, " vlan_accept = %s", print_vlan_accept(o->vlan_accept)); + SHOW_INT(vlan_pvid); SHOW_BOOL(client); SHOW_BOOL(pull); @@ -1296,8 +1323,6 @@ show_p2mp_parms(const struct options *o) #endif /* ! ENABLE_SMALL */ -#if P2MP_SERVER - static void option_iroute(struct options *o, const char *network_str, @@ -1345,7 +1370,6 @@ option_iroute_ipv6(struct options *o, ir->next = o->iroutes_ipv6; o->iroutes_ipv6 = ir; } -#endif /* P2MP_SERVER */ #endif /* P2MP */ #ifndef ENABLE_SMALL @@ -1383,9 +1407,7 @@ options_detach(struct options *o) gc_detach(&o->gc); o->routes = NULL; o->client_nat = NULL; -#if P2MP_SERVER clone_push_list(o); -#endif } void @@ -1451,9 +1473,13 @@ show_connection_entry(const struct connection_entry *o) #endif SHOW_INT(mssfix); -#ifdef ENABLE_OCC SHOW_INT(explicit_exit_notification); -#endif + + SHOW_STR_INLINE(tls_auth_file); + SHOW_PARM(key_direction, keydirection2ascii(o->key_direction, false, true), + "%s"); + SHOW_STR_INLINE(tls_crypt_file); + SHOW_STR_INLINE(tls_crypt_v2_file); } @@ -1511,14 +1537,13 @@ show_settings(const struct options *o) SHOW_INT(persist_mode); #endif -#ifdef ENABLE_CRYPTO SHOW_BOOL(show_ciphers); SHOW_BOOL(show_digests); SHOW_BOOL(show_engines); SHOW_BOOL(genkey); + SHOW_STR(genkey_filename); SHOW_STR(key_pass_file); SHOW_BOOL(show_tls_ciphers); -#endif SHOW_INT(connect_retry_max); show_connection_entries(o); @@ -1542,9 +1567,7 @@ show_settings(const struct options *o) #ifdef ENABLE_FEATURE_SHAPER SHOW_INT(shaper); #endif -#ifdef ENABLE_OCC SHOW_INT(mtu_test); -#endif SHOW_BOOL(mlock); @@ -1596,9 +1619,7 @@ show_settings(const struct options *o) SHOW_INT(status_file_version); SHOW_INT(status_file_update_freq); -#ifdef ENABLE_OCC SHOW_BOOL(occ); -#endif SHOW_INT(rcvbuf); SHOW_INT(sndbuf); #if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK @@ -1653,8 +1674,7 @@ show_settings(const struct options *o) } #endif -#ifdef ENABLE_CRYPTO - SHOW_STR(shared_secret_file); + SHOW_STR_INLINE(shared_secret_file); SHOW_PARM(key_direction, keydirection2ascii(o->key_direction, false, true), "%s"); SHOW_STR(ciphername); SHOW_BOOL(ncp_enabled); @@ -1671,7 +1691,6 @@ show_settings(const struct options *o) SHOW_INT(replay_window); SHOW_INT(replay_time); SHOW_STR(packet_id_file); - SHOW_BOOL(use_iv); SHOW_BOOL(test_crypto); #ifdef ENABLE_PREDICTION_RESISTANCE SHOW_BOOL(use_prediction_resistance); @@ -1679,30 +1698,29 @@ show_settings(const struct options *o) SHOW_BOOL(tls_server); SHOW_BOOL(tls_client); - SHOW_INT(key_method); - SHOW_STR(ca_file); + SHOW_STR_INLINE(ca_file); SHOW_STR(ca_path); SHOW_STR(dh_file); -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT 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); + SHOW_STR_INLINE(cert_file); + SHOW_STR_INLINE(extra_certs_file); -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if ((o->management_flags & MF_EXTERNAL_KEY)) { SHOW_PARM("priv_key_file","EXTERNAL_PRIVATE_KEY","%s"); } else #endif - SHOW_STR(priv_key_file); + SHOW_STR_INLINE(priv_key_file); #ifndef ENABLE_CRYPTO_MBEDTLS - SHOW_STR(pkcs12_file); + SHOW_STR_INLINE(pkcs12_file); #endif #ifdef ENABLE_CRYPTOAPI SHOW_STR(cryptoapi_cert); @@ -1714,7 +1732,7 @@ show_settings(const struct options *o) SHOW_STR(tls_export_cert); SHOW_INT(verify_x509_type); SHOW_STR(verify_x509_name); - SHOW_STR(crl_file); + SHOW_STR_INLINE(crl_file); SHOW_INT(ns_cert_type); { int i; @@ -1736,14 +1754,10 @@ show_settings(const struct options *o) SHOW_INT(transition_window); SHOW_BOOL(single_session); -#ifdef ENABLE_PUSH_PEER_INFO SHOW_BOOL(push_peer_info); -#endif SHOW_BOOL(tls_exit); - SHOW_STR(tls_auth_file); - SHOW_STR(tls_crypt_file); -#endif /* ENABLE_CRYPTO */ + SHOW_STR(tls_crypt_v2_metadata); #ifdef ENABLE_PKCS11 { @@ -1977,14 +1991,14 @@ options_postprocess_verify_ce(const struct options *options, const struct connec init_options(&defaults, true); -#ifdef ENABLE_CRYPTO if (options->test_crypto) { notnull(options->shared_secret_file, "key file (--secret)"); } else -#endif - notnull(options->dev, "TUN/TAP device (--dev)"); + { + notnull(options->dev, "TUN/TAP device (--dev)"); + } /* * Get tun/tap/null device type @@ -2025,10 +2039,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } if (options->inetd == INETD_NOWAIT -#ifdef ENABLE_CRYPTO - && !(options->tls_server || options->tls_client) -#endif - ) + && !(options->tls_server || options->tls_client)) { msg(M_USAGE, "--inetd nowait can only be used in TLS mode"); } @@ -2038,6 +2049,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "--inetd nowait only makes sense in --dev tap mode"); } + if (options->inetd) + { + msg(M_WARN, "DEPRECATED OPTION: --inetd mode is deprecated " + "and will be removed in OpenVPN 2.6"); + } if (options->lladdr && dev != DEV_TYPE_TAP) { @@ -2052,12 +2068,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT); } -#ifdef ENABLE_OCC if (!proto_is_udp(ce->proto) && options->mtu_test) { msg(M_USAGE, "--mtu-test only makes sense with --proto udp"); } -#endif /* will we be pulling options from server? */ #if P2MP @@ -2138,8 +2152,18 @@ options_postprocess_verify_ce(const struct options *options, const struct connec "passwords is STRONGLY discouraged and considered insecure"); } -#endif +#endif /* ifdef ENABLE_MANAGEMENT */ +#if defined(ENABLE_MANAGEMENT) + if ((tls_version_max() >= TLS_VER_1_3) + && (options->management_flags & MF_EXTERNAL_KEY) + && !(options->management_flags & (MF_EXTERNAL_KEY_NOPADDING)) + ) + { + msg(M_ERR, "management-external-key with OpenSSL 1.1.1 requires " + "the nopadding argument/support"); + } +#endif /* * Windows-specific options. */ @@ -2162,7 +2186,12 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "--dhcp-options requires --ip-win32 dynamic or adaptive"); } -#endif + + if (options->windows_driver == WINDOWS_DRIVER_WINTUN && dev != DEV_TYPE_TUN) + { + msg(M_USAGE, "--windows-driver wintun requires --dev tun"); + } +#endif /* ifdef _WIN32 */ /* * Check that protocol options make sense. @@ -2175,12 +2204,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } #endif -#ifdef ENABLE_OCC if (!proto_is_udp(ce->proto) && ce->explicit_exit_notification) { msg(M_USAGE, "--explicit-exit-notify can only be used with --proto udp"); } -#endif if (!ce->remote && ce->proto == PROTO_TCP_CLIENT) { @@ -2211,13 +2238,14 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "TCP server mode allows at most one --remote address"); } -#if P2MP_SERVER - /* * Check consistency of --mode server options. */ if (options->mode == MODE_SERVER) { +#ifdef TARGET_ANDROID + msg(M_FATAL, "--mode server not supported on Android"); +#endif if (!(dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP)) { msg(M_USAGE, "--mode server only works with --dev tun or --dev tap"); @@ -2308,9 +2336,12 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "--up-delay cannot be used with --mode server"); } - if (!options->ifconfig_pool_defined && options->ifconfig_pool_persist_filename) + if (!options->ifconfig_pool_defined + && !options->ifconfig_ipv6_pool_defined + && options->ifconfig_pool_persist_filename) { - msg(M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool"); + msg(M_USAGE, + "--ifconfig-pool-persist must be used with --ifconfig-pool or --ifconfig-ipv6-pool"); } if (options->ifconfig_ipv6_pool_defined && !options->ifconfig_ipv6_local) { @@ -2328,11 +2359,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "--ccd-exclusive must be used with --client-config-dir"); } - if (options->key_method != 2) + if (options->auth_token_generate && !options->renegotiate_seconds) { - msg(M_USAGE, "--mode server requires --key-method 2"); + msg(M_USAGE, "--auth-gen-token needs a non-infinite " + "--renegotiate_seconds setting"); } - { const bool ccnr = (options->auth_user_pass_verify_script || PLUGIN_OPTION_LIST(options) @@ -2351,6 +2382,22 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "--auth-user-pass-optional %s", postfix); } } + + if (options->vlan_tagging && dev != DEV_TYPE_TAP) + { + msg(M_USAGE, "--vlan-tagging must be used with --dev tap"); + } + if (!options->vlan_tagging) + { + if (options->vlan_accept != defaults.vlan_accept) + { + msg(M_USAGE, "--vlan-accept requires --vlan-tagging"); + } + if (options->vlan_pvid != defaults.vlan_pvid) + { + msg(M_USAGE, "--vlan-pvid requires --vlan-tagging"); + } + } } else { @@ -2401,7 +2448,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } 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"); + msg(M_USAGE, "--verify-client-cert requires --mode server"); } if (options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) { @@ -2440,38 +2487,18 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "--stale-routes-check requires --mode server"); } - if (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING)) + + if (options->vlan_tagging) { - msg(M_USAGE, "--compat-x509-names no-remapping requires --mode server"); + msg(M_USAGE, "--vlan-tagging requires --mode server"); } } -#endif /* P2MP_SERVER */ - -#ifdef ENABLE_CRYPTO - - if (options->ncp_enabled && !tls_check_ncp_cipher_list(options->ncp_ciphers)) - { - msg(M_USAGE, "NCP cipher list contains unsupported ciphers."); - } - if (options->ncp_enabled && !options->use_iv) - { - msg(M_USAGE, "--no-iv not allowed when NCP is enabled."); - } - if (!options->use_iv) - { - msg(M_WARN, "WARNING: --no-iv is deprecated and will be removed in 2.5"); - } if (options->keysize) { msg(M_WARN, "WARNING: --keysize is DEPRECATED and will be removed in OpenVPN 2.6"); } - if (!options->replay) - { - msg(M_WARN, "WARNING: --no-replay is DEPRECATED and will be removed in OpenVPN 2.5"); - } - /* * Check consistency of replay options */ @@ -2494,17 +2521,10 @@ options_postprocess_verify_ce(const struct options *options, const struct connec 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) " + "--verify-client-cert none|optional " "may accept clients which do not present a certificate"); } - if (options->key_method == 1) - { - 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."); - } - const int tls_version_max = (options->ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK; @@ -2540,7 +2560,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "Parameter --key cannot be used when --pkcs11-provider is also specified."); } -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if (options->management_flags & MF_EXTERNAL_KEY) { msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs11-provider is also specified."); @@ -2563,7 +2583,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } else #endif /* ifdef ENABLE_PKCS11 */ -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if ((options->management_flags & MF_EXTERNAL_KEY) && options->priv_key_file) { msg(M_USAGE, "--key and --management-external-key are mutually exclusive"); @@ -2600,7 +2620,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "Parameter --pkcs12 cannot be used when --cryptoapicert is also specified."); } -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if (options->management_flags & MF_EXTERNAL_KEY) { msg(M_USAGE, "Parameter --management-external-key cannot be used when --cryptoapicert is also specified."); @@ -2630,7 +2650,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { msg(M_USAGE, "Parameter --key cannot be used when --pkcs12 is also specified."); } -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if (options->management_flags & MF_EXTERNAL_KEY) { msg(M_USAGE, "Parameter --management-external-key cannot be used when --pkcs12 is also specified."); @@ -2663,7 +2683,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec { const int sum = -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT ((options->cert_file != NULL) || (options->management_flags & MF_EXTERNAL_CERT)) +((options->priv_key_file != NULL) || (options->management_flags & MF_EXTERNAL_KEY)); #else @@ -2687,20 +2707,25 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } else { -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if (!(options->management_flags & MF_EXTERNAL_CERT)) #endif notnull(options->cert_file, "certificate file (--cert) or PKCS#12 file (--pkcs12)"); -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT if (!(options->management_flags & MF_EXTERNAL_KEY)) #endif notnull(options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)"); } } - if (options->tls_auth_file && options->tls_crypt_file) + if (ce->tls_auth_file && ce->tls_crypt_file) { msg(M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive"); } + if (options->tls_client && ce->tls_crypt_v2_file + && (ce->tls_auth_file || ce->tls_crypt_file)) + { + msg(M_USAGE, "--tls-crypt-v2, --tls-auth and --tls-crypt are mutually exclusive in client mode"); + } } else { @@ -2736,13 +2761,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec MUST_BE_UNDEF(transition_window); MUST_BE_UNDEF(tls_auth_file); MUST_BE_UNDEF(tls_crypt_file); + MUST_BE_UNDEF(tls_crypt_v2_file); MUST_BE_UNDEF(single_session); -#ifdef ENABLE_PUSH_PEER_INFO MUST_BE_UNDEF(push_peer_info); -#endif MUST_BE_UNDEF(tls_exit); MUST_BE_UNDEF(crl_file); - MUST_BE_UNDEF(key_method); MUST_BE_UNDEF(ns_cert_type); MUST_BE_UNDEF(remote_cert_ku[0]); MUST_BE_UNDEF(remote_cert_eku); @@ -2759,7 +2782,6 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } } #undef MUST_BE_UNDEF -#endif /* ENABLE_CRYPTO */ #if P2MP if (options->auth_user_pass_file && !options->pull) @@ -2776,7 +2798,6 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) { const int dev = dev_type_enum(o->dev, o->dev_type); -#if P2MP_SERVER if (o->server_defined || o->server_bridge_defined || o->server_bridge_proxy_dhcp) { if (ce->proto == PROTO_TCP) @@ -2784,7 +2805,7 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) ce->proto = PROTO_TCP_SERVER; } } -#endif + #if P2MP if (o->client) { @@ -2865,6 +2886,56 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) } } + /* + * Set per-connection block tls-auth/crypt/crypto-v2 fields if undefined. + * + * At the end only one of these will be really set because the parser + * logic prevents configurations where more are set. + */ + if (!ce->tls_auth_file && !ce->tls_crypt_file && !ce->tls_crypt_v2_file) + { + ce->tls_auth_file = o->tls_auth_file; + ce->tls_auth_file_inline = o->tls_auth_file_inline; + ce->key_direction = o->key_direction; + + ce->tls_crypt_file = o->tls_crypt_file; + ce->tls_crypt_file_inline = o->tls_crypt_file_inline; + + ce->tls_crypt_v2_file = o->tls_crypt_v2_file; + ce->tls_crypt_v2_file_inline = o->tls_crypt_v2_file_inline; + } + + /* pre-cache tls-auth/crypt key file if persist-key was specified and keys + * were not already embedded in the config file + */ + if (o->persist_key) + { + if (ce->tls_auth_file && !ce->tls_auth_file_inline) + { + struct buffer in = buffer_read_from_file(ce->tls_auth_file, &o->gc); + if (!buf_valid(&in)) + { + msg(M_FATAL, "Cannot pre-load tls-auth keyfile (%s)", + ce->tls_auth_file); + } + + ce->tls_auth_file = (char *)in.data; + ce->tls_auth_file_inline = true; + } + + if (ce->tls_crypt_file && !ce->tls_crypt_file_inline) + { + struct buffer in = buffer_read_from_file(ce->tls_crypt_file, &o->gc); + if (!buf_valid(&in)) + { + msg(M_FATAL, "Cannot pre-load tls-crypt keyfile (%s)", + ce->tls_crypt_file); + } + + ce->tls_crypt_file = (char *)in.data; + ce->tls_crypt_file_inline = true; + } + } } #ifdef _WIN32 @@ -2900,9 +2971,19 @@ options_postprocess_mutate_invariant(struct options *options) } #ifdef _WIN32 + /* when using wintun, kernel doesn't send DHCP requests, so don't use it */ + if (options->windows_driver == WINDOWS_DRIVER_WINTUN + && (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ || options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE)) + { + options->tuntap_options.ip_win32_type = IPW32_SET_NETSH; + } + if ((dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP) && !options->route_delay_defined) { - if (options->mode == MODE_POINT_TO_POINT) + /* delay may only be necessary when we perform DHCP handshake */ + const bool dhcp = (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ) + || (options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE); + if ((options->mode == MODE_POINT_TO_POINT) && dhcp) { options->route_delay_defined = true; options->route_delay = 5; /* Vista sometimes has a race without this */ @@ -2916,15 +2997,12 @@ options_postprocess_mutate_invariant(struct options *options) } remap_redirect_gateway_flags(options); -#endif -#if P2MP_SERVER /* * Check consistency of --mode server options. */ if (options->mode == MODE_SERVER) { -#ifdef _WIN32 /* * We need to explicitly set --tap-sleep because * we do not schedule event timers in the top-level context. @@ -2935,9 +3013,8 @@ options_postprocess_mutate_invariant(struct options *options) options->tuntap_options.tap_sleep = options->route_delay; } options->route_delay_defined = false; -#endif } -#endif +#endif /* ifdef _WIN32 */ #ifdef DEFAULT_PKCS11_MODULE /* If p11-kit is present on the system then load its p11-kit-proxy.so @@ -2969,6 +3046,67 @@ options_postprocess_verify(const struct options *o) } static void +options_postprocess_cipher(struct options *o) +{ + if (!o->pull && !(o->mode == MODE_SERVER)) + { + /* we are in the classic P2P mode */ + o->ncp_enabled = false; + msg( M_WARN, "Cipher negotiation is disabled since neither " + "P2MP client nor server mode is enabled"); + + /* If the cipher is not set, use the old default of BF-CBC. We will + * warn that this is deprecated on cipher initialisation, no need + * to warn here as well */ + if (!o->ciphername) + { + o->ciphername = "BF-CBC"; + } + return; + } + + /* pull or P2MP mode */ + if (!o->ciphername) + { + if (!o->ncp_enabled) + { + msg(M_USAGE, "--ncp-disable needs an explicit --cipher or " + "--data-ciphers-fallback config option"); + } + + msg(M_WARN, "--cipher is not set. Previous OpenVPN version defaulted to " + "BF-CBC as fallback when cipher negotiation failed in this case. " + "If you need this fallback please add '--data-ciphers-fallback " + "BF-CBC' to your configuration and/or add BF-CBC to " + "--data-ciphers."); + + /* We still need to set the ciphername to BF-CBC since various other + * parts of OpenVPN assert that the ciphername is set */ + o->ciphername = "BF-CBC"; + } + else if (!o->enable_ncp_fallback + && !tls_item_in_cipher_list(o->ciphername, o->ncp_ciphers)) + { + msg(M_WARN, "DEPRECATED OPTION: --cipher set to '%s' but missing in" + " --data-ciphers (%s). Future OpenVPN version will " + "ignore --cipher for cipher negotiations. " + "Add '%s' to --data-ciphers or change --cipher '%s' to " + "--data-ciphers-fallback '%s' to silence this warning.", + o->ciphername, o->ncp_ciphers, o->ciphername, + o->ciphername, o->ciphername); + o->enable_ncp_fallback = true; + + /* Append the --cipher to ncp_ciphers to allow it in NCP */ + size_t newlen = strlen(o->ncp_ciphers) + 1 + strlen(o->ciphername) + 1; + char *ncp_ciphers = gc_malloc(newlen, false, &o->gc); + + ASSERT(openvpn_snprintf(ncp_ciphers, newlen, "%s:%s", o->ncp_ciphers, + o->ciphername)); + o->ncp_ciphers = ncp_ciphers; + } +} + +static void options_postprocess_mutate(struct options *o) { int i; @@ -2980,8 +3118,18 @@ options_postprocess_mutate(struct options *o) helper_keepalive(o); helper_tcp_nodelay(o); + options_postprocess_cipher(o); options_postprocess_mutate_invariant(o); + if (o->ncp_enabled) + { + o->ncp_ciphers = mutate_ncp_cipher_list(o->ncp_ciphers, &o->gc); + if (o->ncp_ciphers == NULL) + { + msg(M_USAGE, "NCP cipher list contains unsupported ciphers or is too long."); + } + } + if (o->remote_list && !o->connection_list) { /* @@ -3015,7 +3163,6 @@ options_postprocess_mutate(struct options *o) options_postprocess_mutate_ce(o, o->connection_list->array[i]); } -#ifdef ENABLE_CRYPTO if (o->tls_server) { /* Check that DH file is specified, or explicitly disabled */ @@ -3029,20 +3176,9 @@ options_postprocess_mutate(struct options *o) { /* DH file is only meaningful in a tls-server context. */ msg(M_WARN, "WARNING: Ignoring option 'dh' in tls-client mode, please only " - "include this in your server configuration"); + "include this in your server configuration"); 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) { @@ -3064,12 +3200,11 @@ options_postprocess_mutate(struct options *o) */ #ifndef ENABLE_SMALL /** Expect people using the stripped down version to know what they do */ -#define CHKACC_FILE (1<<0) /** Check for a file/directory precense */ -#define CHKACC_DIRPATH (1<<1) /** Check for directory precense where a file should reside */ +#define CHKACC_FILE (1<<0) /** Check for a file/directory presence */ +#define CHKACC_DIRPATH (1<<1) /** Check for directory presence where a file should reside */ #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 */ +#define CHKACC_ACPTSTDIN (1<<3) /** If filename is stdin, it's allowed and "exists" */ +#define CHKACC_PRIVATE (1<<4) /** 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) @@ -3082,12 +3217,6 @@ check_file_access(const int type, const char *file, const int mode, const char * return false; } - /* If this may be an inline file, and the proper inline "filename" is set - no issues */ - if ((type & CHKACC_INLINE) && streq(file, INLINE_FILE_TAG) ) - { - return false; - } - /* If stdin is allowed and the file name is 'stdin', then do no * further checks as stdin is always available */ @@ -3099,7 +3228,7 @@ check_file_access(const int type, const char *file, const int mode, const char * /* Is the directory path leading to the given file accessible? */ if (type & CHKACC_DIRPATH) { - char *fullpath = string_alloc(file, NULL); /* POSIX dirname() implementaion may modify its arguments */ + char *fullpath = string_alloc(file, NULL); /* POSIX dirname() implementation may modify its arguments */ char *dirpath = dirname(fullpath); if (platform_access(dirpath, mode|X_OK) != 0) @@ -3149,7 +3278,7 @@ check_file_access(const int type, const char *file, const int mode, const char * msg(M_NOPREFIX | M_OPTERR | M_ERRNO, "%s fails with '%s'", opt, file); } - /* Return true if an error occured */ + /* Return true if an error occurred */ return (errcode != 0 ? true : false); } @@ -3192,6 +3321,38 @@ check_file_access_chroot(const char *chroot, const int type, const char *file, c return ret; } +/** + * A wrapper for check_file_access_chroot() that returns false immediately if + * the file is inline (and therefore there is no access to check) + */ +static bool +check_file_access_chroot_inline(bool is_inline, const char *chroot, + const int type, const char *file, + const int mode, const char *opt) +{ + if (is_inline) + { + return false; + } + + return check_file_access_chroot(chroot, type, file, mode, opt); +} + +/** + * A wrapper for check_file_access() that returns false immediately if the file + * is inline (and therefore there is no access to check) + */ +static bool +check_file_access_inline(bool is_inline, const int type, const char *file, + const int mode, const char *opt) +{ + if (is_inline) + { + return false; + } + + return check_file_access(type, file, mode, opt); +} /* * Verifies that the path in the "command" that comes after certain script options (e.g., --up) is a @@ -3241,7 +3402,7 @@ check_cmd_access(const char *command, const char *opt, const char *chroot) return_code = true; } - argv_reset(&argv); + argv_free(&argv); return return_code; } @@ -3255,48 +3416,80 @@ options_postprocess_filechecks(struct options *options) { bool errs = false; -#ifdef ENABLE_CRYPTO /* ** SSL/TLS/crypto related files ** */ - 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"); - errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->cert_file, R_OK, "--cert"); - errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->extra_certs_file, R_OK, - "--extra-certs"); -#ifdef MANAGMENT_EXTERNAL_KEY + errs |= check_file_access_inline(options->dh_file_inline, CHKACC_FILE, + options->dh_file, R_OK, "--dh"); + + errs |= check_file_access_inline(options->ca_file_inline, CHKACC_FILE, + options->ca_file, R_OK, "--ca"); + + errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, + options->ca_path, R_OK, "--capath"); + + errs |= check_file_access_inline(options->cert_file_inline, CHKACC_FILE, + options->cert_file, R_OK, "--cert"); + + errs |= check_file_access_inline(options->extra_certs_file, CHKACC_FILE, + options->extra_certs_file, R_OK, + "--extra-certs"); + +#ifdef ENABLE_MANAGMENT if (!(options->management_flags & MF_EXTERNAL_KEY)) #endif { - errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE|CHKACC_PRIVATE, - options->priv_key_file, R_OK, "--key"); + errs |= check_file_access_inline(options->priv_key_file_inline, + CHKACC_FILE|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"); + + errs |= check_file_access_inline(options->pkcs12_file_inline, + CHKACC_FILE|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, + 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|CHKACC_INLINE, - options->crl_file, R_OK, "--crl-verify"); + errs |= check_file_access_chroot_inline(options->crl_file_inline, + options->chroot_dir, + CHKACC_FILE, options->crl_file, + R_OK, "--crl-verify"); + } + + ASSERT(options->connection_list); + for (int i = 0; i < options->connection_list->len; ++i) + { + struct connection_entry *ce = options->connection_list->array[i]; + + errs |= check_file_access_inline(ce->tls_auth_file_inline, + CHKACC_FILE|CHKACC_PRIVATE, + ce->tls_auth_file, R_OK, + "--tls-auth"); + errs |= check_file_access_inline(ce->tls_crypt_file_inline, + CHKACC_FILE|CHKACC_PRIVATE, + ce->tls_crypt_file, R_OK, + "--tls-crypt"); + errs |= check_file_access_inline(ce->tls_crypt_v2_file_inline, + CHKACC_FILE|CHKACC_PRIVATE, + ce->tls_crypt_v2_file, R_OK, + "--tls-crypt-v2"); } - 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_inline(options->shared_secret_file_inline, + CHKACC_FILE|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"); /* ** Password files ** */ 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|CHKACC_PRIVATE, options->management_user_pass, R_OK, @@ -3319,18 +3512,13 @@ options_postprocess_filechecks(struct options *options) R_OK|W_OK, "--status"); /* ** Config related ** */ -#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_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"); errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, options->tmp_dir, R_OK|W_OK|X_OK, "Temporary directory (--tmp-dir)"); -#endif /* P2MP_SERVER */ - if (errs) { msg(M_USAGE, "Please correct these errors."); @@ -3436,9 +3624,6 @@ pre_pull_restore(struct options *o, struct gc_arena *gc) } #endif /* if P2MP */ - -#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 @@ -3450,7 +3635,7 @@ 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; @@ -3458,18 +3643,16 @@ calc_options_string_link_mtu(const struct options *o, const struct frame *frame) init_key_type(&fake_kt, o->ciphername, o->authname, o->keysize, true, false); frame_remove_from_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)); + crypto_adjust_frame_parameters(&fake_frame, &fake_kt, 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 @@ -3504,7 +3687,6 @@ calc_options_string_link_mtu(const struct options *o, const struct frame *frame) * --keysize * --secret * --no-replay - * --no-iv * * SSL Options: * @@ -3518,6 +3700,7 @@ char * options_string(const struct options *o, const struct frame *frame, struct tuntap *tt, + openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc) { @@ -3531,14 +3714,21 @@ 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 %u", (unsigned int) calc_options_string_link_mtu(o, frame)); + /* the link-mtu that we send has only a meaning if have a fixed + * cipher (p2p) or have a fallback cipher configured for older non + * ncp clients. But not sending it will make even 2.4 complain + * about it being missing. So still send it. */ + 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", proto_remote(o->ce.proto, remote)); + bool p2p_nopull = o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o); /* 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->ifconfig_ipv6_local && o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o)) + if (o->ifconfig_ipv6_local && p2p_nopull) { buf_printf(&out, ",tun-ipv6"); } @@ -3560,14 +3750,15 @@ options_string(const struct options *o, NULL, NULL, false, - NULL); + NULL, + ctx); if (tt) { tt_local = true; } } - if (tt && o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o)) + if (tt && p2p_nopull) { const char *ios = ifconfig_options_string(tt, remote, o->ifconfig_nowarn, gc); if (ios && strlen(ios)) @@ -3595,8 +3786,6 @@ options_string(const struct options *o, } #endif -#ifdef ENABLE_CRYPTO - #define TLS_CLIENT (o->tls_client) #define TLS_SERVER (o->tls_server) @@ -3625,9 +3814,14 @@ options_string(const struct options *o, init_key_type(&kt, o->ciphername, o->authname, o->keysize, true, false); - - buf_printf(&out, ",cipher %s", - translate_cipher_name_to_openvpn(cipher_kt_name(kt.cipher))); + /* Only announce the cipher to our peer if we are willing to + * support it */ + const char *ciphername = cipher_kt_name(kt.cipher); + if (p2p_nopull || !o->ncp_enabled + || tls_item_in_cipher_list(ciphername, o->ncp_ciphers)) + { + buf_printf(&out, ",cipher %s", ciphername); + } buf_printf(&out, ",auth %s", md_kt_name(kt.digest)); buf_printf(&out, ",keysize %d", kt.cipher_length * 8); if (o->shared_secret_file) @@ -3638,10 +3832,6 @@ options_string(const struct options *o, { buf_printf(&out, ",no-replay"); } - if (!o->use_iv) - { - buf_printf(&out, ",no-iv"); - } #ifdef ENABLE_PREDICTION_RESISTANCE if (o->use_prediction_resistance) @@ -3657,7 +3847,7 @@ options_string(const struct options *o, { if (TLS_CLIENT || TLS_SERVER) { - if (o->tls_auth_file) + if (o->ce.tls_auth_file) { buf_printf(&out, ",tls-auth"); } @@ -3665,10 +3855,7 @@ options_string(const struct options *o, * 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); - } + buf_printf(&out, ",key-method %d", KEY_METHOD_2); } if (remote) @@ -3698,8 +3885,6 @@ options_string(const struct options *o, #undef TLS_CLIENT #undef TLS_SERVER -#endif /* ENABLE_CRYPTO */ - return BSTR(&out); } @@ -3754,7 +3939,8 @@ options_warning_safe_scan2(const int msglevel, || strprefix(p1, "keydir ") || strprefix(p1, "proto ") || strprefix(p1, "tls-auth ") - || strprefix(p1, "tun-ipv6")) + || strprefix(p1, "tun-ipv6") + || strprefix(p1, "cipher ")) { return; } @@ -3888,8 +4074,6 @@ options_string_version(const char *s, struct gc_arena *gc) return BSTR(&out); } -#endif /* ENABLE_OCC */ - char * options_string_extract_option(const char *options_string,const char *opt_name, struct gc_arena *gc) @@ -3958,6 +4142,33 @@ foreign_option(struct options *o, char *argv[], int len, struct env_set *es) } } +#ifdef _WIN32 +/** + * Parses --windows-driver config option + * + * @param str value of --windows-driver option + * @param msglevel msglevel to report parsing error + * @return enum windows_driver_type driver type, WINDOWS_DRIVER_UNSPECIFIED on unknown --windows-driver value + */ +static enum windows_driver_type +parse_windows_driver(const char *str, const int msglevel) +{ + if (streq(str, "tap-windows6")) + { + return WINDOWS_DRIVER_TAP_WINDOWS6; + } + else if (streq(str, "wintun")) + { + return WINDOWS_DRIVER_WINTUN; + } + else + { + msg(msglevel, "--windows-driver must be tap-windows6 or wintun"); + return WINDOWS_DRIVER_UNSPECIFIED; + } +} +#endif + /* * parse/print topology coding */ @@ -4081,7 +4292,6 @@ usage(void) struct options o; init_options(&o, true); -#ifdef ENABLE_CRYPTO fprintf(fp, usage_message, title_string, o.ce.connect_retry_seconds, @@ -4093,15 +4303,6 @@ usage(void) o.replay_window, o.replay_time, o.tls_timeout, o.renegotiate_seconds, o.handshake_window, o.transition_window); -#else /* 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); -#endif fflush(fp); #endif /* ENABLE_SMALL */ @@ -4129,20 +4330,15 @@ show_windows_version(const unsigned int flags) void show_library_versions(const unsigned int flags) { -#ifdef ENABLE_CRYPTO -#define SSL_LIB_VER_STR get_ssl_library_version() -#else -#define SSL_LIB_VER_STR "" -#endif #ifdef ENABLE_LZO #define LZO_LIB_VER_STR ", LZO ", lzo_version_string() #else #define LZO_LIB_VER_STR "", "" #endif - msg(flags, "library versions: %s%s%s", SSL_LIB_VER_STR, LZO_LIB_VER_STR); + msg(flags, "library versions: %s%s%s", get_ssl_library_version(), + LZO_LIB_VER_STR); -#undef SSL_LIB_VER_STR #undef LZO_LIB_VER_STR } @@ -4164,7 +4360,7 @@ usage_version(void) msg(M_INFO|M_NOPREFIX, "special build: %s", CONFIGURE_SPECIAL_BUILD); #endif #endif - openvpn_exit(OPENVPN_EXIT_STATUS_USAGE); /* exit point */ + openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); } void @@ -4482,25 +4678,26 @@ read_inline_file(struct in_src *is, const char *close_tag, struct gc_arena *gc) static bool check_inline_file(struct in_src *is, char *p[], struct gc_arena *gc) { - bool ret = false; + bool is_inline = false; + if (p[0] && !p[1]) { char *arg = p[0]; if (arg[0] == '<' && arg[strlen(arg)-1] == '>') { struct buffer close_tag; - arg[strlen(arg)-1] = '\0'; - p[0] = string_alloc(arg+1, gc); - p[1] = string_alloc(INLINE_FILE_TAG, gc); + + arg[strlen(arg) - 1] = '\0'; + p[0] = string_alloc(arg + 1, gc); close_tag = alloc_buf(strlen(p[0]) + 4); buf_printf(&close_tag, "</%s>", p[0]); - p[2] = read_inline_file(is, BSTR(&close_tag), gc); - p[3] = NULL; + p[1] = read_inline_file(is, BSTR(&close_tag), gc); + p[2] = NULL; free_buf(&close_tag); - ret = true; + is_inline = true; } } - return ret; + return is_inline; } static bool @@ -4513,7 +4710,8 @@ check_inline_file_via_fp(FILE *fp, char *p[], struct gc_arena *gc) } static bool -check_inline_file_via_buf(struct buffer *multiline, char *p[], struct gc_arena *gc) +check_inline_file_via_buf(struct buffer *multiline, char *p[], + struct gc_arena *gc) { struct in_src is; is.type = IS_TYPE_BUF; @@ -4524,6 +4722,7 @@ check_inline_file_via_buf(struct buffer *multiline, char *p[], struct gc_arena * static void add_option(struct options *options, char *p[], + bool is_inline, const char *file, int line, const int level, @@ -4581,9 +4780,13 @@ read_config_file(struct options *options, } if (parse_line(line + offset, p, SIZE(p)-1, file, line_num, msglevel, &options->gc)) { + bool is_inline; + bypass_doubledash(&p[0]); - check_inline_file_via_fp(fp, p, &options->gc); - add_option(options, p, file, line_num, level, msglevel, permission_mask, option_types_found, es); + is_inline = check_inline_file_via_fp(fp, p, &options->gc); + add_option(options, p, is_inline, file, line_num, level, + msglevel, permission_mask, option_types_found, + es); } } if (fp != stdin) @@ -4626,9 +4829,12 @@ read_config_string(const char *prefix, ++line_num; if (parse_line(line, p, SIZE(p)-1, prefix, line_num, msglevel, &options->gc)) { + bool is_inline; + bypass_doubledash(&p[0]); - check_inline_file_via_buf(&multiline, p, &options->gc); - add_option(options, p, prefix, line_num, 0, msglevel, permission_mask, option_types_found, es); + is_inline = check_inline_file_via_buf(&multiline, p, &options->gc); + add_option(options, p, is_inline, prefix, line_num, 0, msglevel, + permission_mask, option_types_found, es); } CLEAR(p); } @@ -4659,7 +4865,8 @@ parse_argv(struct options *options, CLEAR(p); p[0] = "config"; p[1] = argv[1]; - add_option(options, p, NULL, 0, 0, msglevel, permission_mask, option_types_found, es); + add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, + option_types_found, es); } else { @@ -4693,7 +4900,8 @@ parse_argv(struct options *options, } } } - add_option(options, p, NULL, 0, 0, msglevel, permission_mask, option_types_found, es); + add_option(options, p, false, NULL, 0, 0, msglevel, permission_mask, + option_types_found, es); i += j - 1; } } @@ -4764,7 +4972,8 @@ apply_push_options(struct options *options, } if (parse_line(line, p, SIZE(p)-1, file, line_num, msglevel, &options->gc)) { - add_option(options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es); + add_option(options, p, false, file, line_num, 0, msglevel, + permission_mask, option_types_found, es); } } return true; @@ -4803,7 +5012,13 @@ options_string_import(struct options *options, #if P2MP -#define VERIFY_PERMISSION(mask) { if (!verify_permission(p[0], file, line, (mask), permission_mask, option_types_found, msglevel, options)) {goto err;}} +#define VERIFY_PERMISSION(mask) { \ + if (!verify_permission(p[0], file, line, (mask), permission_mask, \ + option_types_found, msglevel, options, is_inline)) \ + { \ + goto err; \ + } \ +} static bool verify_permission(const char *name, @@ -4813,7 +5028,8 @@ verify_permission(const char *name, const unsigned int allowed, unsigned int *found, const int msglevel, - struct options *options) + struct options *options, + bool is_inline) { if (!(type & allowed)) { @@ -4821,6 +5037,13 @@ verify_permission(const char *name, return false; } + if (is_inline && !(type & OPT_P_INLINE)) + { + msg(msglevel, "option '%s' is not expected to be inline (%s:%d)", name, + file, line); + return false; + } + if (found) { *found |= type; @@ -4927,10 +5150,31 @@ set_user_script(struct options *options, #endif } +#ifdef USE_COMP +static void +show_compression_warning(struct compress_options *info) +{ + if (comp_non_stub_enabled(info)) + { + /* + * Check if already displayed the strong warning and enabled full + * compression + */ + if (!(info->flags & COMP_F_ALLOW_COMPRESS)) + { + msg(M_WARN, "WARNING: Compression for receiving enabled. " + "Compression has been used in the past to break encryption. " + "Sent packets are not compressed unless \"allow-compression yes\" " + "is also set."); + } + } +} +#endif static void add_option(struct options *options, char *p[], + bool is_inline, const char *file, int line, const int level, @@ -4997,13 +5241,15 @@ add_option(struct options *options, struct route_gateway_info rgi; struct route_ipv6_gateway_info rgi6; struct in6_addr remote = IN6ADDR_ANY_INIT; + openvpn_net_ctx_t net_ctx; VERIFY_PERMISSION(OPT_P_GENERAL); if (p[1]) { get_ipv6_addr(p[1], &remote, NULL, M_WARN); } - get_default_gateway(&rgi); - get_default_gateway_ipv6(&rgi6, &remote); + net_ctx_init(NULL, &net_ctx); + get_default_gateway(&rgi, &net_ctx); + get_default_gateway_ipv6(&rgi6, &remote, &net_ctx); print_default_gateway(M_INFO, &rgi, &rgi6); openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); /* exit point */ } @@ -5128,10 +5374,34 @@ add_option(struct options *options, 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") && !p[1]) +#ifdef ENABLE_MANAGEMENT + else if (streq(p[0], "management-external-key")) { VERIFY_PERMISSION(OPT_P_GENERAL); + for (int j = 1; j < MAX_PARMS && p[j] != NULL; ++j) + { + if (streq(p[j], "nopadding")) + { + options->management_flags |= MF_EXTERNAL_KEY_NOPADDING; + } + else if (streq(p[j], "pkcs1")) + { + options->management_flags |= MF_EXTERNAL_KEY_PKCS1PAD; + } + else + { + msg(msglevel, "Unknown management-external-key flag: %s", p[j]); + } + } + /* + * When no option is present, assume that only PKCS1 + * padding is supported + */ + if (!(options->management_flags + &(MF_EXTERNAL_KEY_NOPADDING | MF_EXTERNAL_KEY_PKCS1PAD))) + { + options->management_flags |= MF_EXTERNAL_KEY_PKCS1PAD; + } options->management_flags |= MF_EXTERNAL_KEY; } else if (streq(p[0], "management-external-cert") && p[1] && !p[2]) @@ -5140,7 +5410,7 @@ add_option(struct options *options, options->management_flags |= MF_EXTERNAL_CERT; options->management_certificate = p[1]; } -#endif +#endif /* ifdef ENABLE_MANAGEMENT */ #ifdef MANAGEMENT_DEF_AUTH else if (streq(p[0], "management-client-auth") && !p[1]) { @@ -5191,12 +5461,10 @@ add_option(struct options *options, { options->mode = MODE_POINT_TO_POINT; } -#if P2MP_SERVER else if (streq(p[1], "server")) { options->mode = MODE_SERVER; } -#endif else { msg(msglevel, "Bad --mode parameter: %s", p[1]); @@ -5213,6 +5481,13 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->dev_type = p[1]; } +#ifdef _WIN32 + else if (streq(p[0], "windows-driver") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->windows_driver = parse_windows_driver(p[1], M_FATAL); + } +#endif else if (streq(p[0], "dev-node") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -5310,15 +5585,16 @@ add_option(struct options *options, } else if (streq(p[0], "connection") && p[1] && !p[3]) { - VERIFY_PERMISSION(OPT_P_GENERAL); - if (streq(p[1], INLINE_FILE_TAG) && p[2]) + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); + if (is_inline) { struct options sub; struct connection_entry *e; init_options(&sub, true); sub.ce = options->ce; - read_config_string("[CONNECTION-OPTIONS]", &sub, p[2], msglevel, OPT_P_CONNECTION, option_types_found, es); + read_config_string("[CONNECTION-OPTIONS]", &sub, p[1], msglevel, + OPT_P_CONNECTION, option_types_found, es); if (!sub.ce.remote) { msg(msglevel, "Each 'connection' block must contain exactly one 'remote' directive"); @@ -5799,13 +6075,11 @@ add_option(struct options *options, 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") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->mtu_test = true; } -#endif else if (streq(p[0], "nice") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_NICE); @@ -5844,6 +6118,13 @@ add_option(struct options *options, } } } +#ifdef TARGET_LINUX + else if (streq (p[0], "bind-dev") && p[1]) + { + VERIFY_PERMISSION (OPT_P_SOCKFLAGS); + options->bind_dev = p[1]; + } +#endif else if (streq(p[0], "txqueuelen") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -5999,17 +6280,10 @@ add_option(struct options *options, else if (streq(p[0], "http-proxy-user-pass") && p[1]) { struct http_proxy_options *ho; - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); ho = init_http_proxy_options_once(&options->ce.http_proxy_options, &options->gc); - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - ho->auth_file = p[2]; - ho->inline_creds = true; - } - else - { - ho->auth_file = p[1]; - } + ho->auth_file = p[1]; + ho->inline_creds = is_inline; } else if (streq(p[0], "http-proxy-retry") || streq(p[0], "socks-proxy-retry")) { @@ -6116,7 +6390,6 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_TIMER); options->ping_timer_remote = true; } -#ifdef ENABLE_OCC else if (streq(p[0], "explicit-exit-notify") && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION|OPT_P_EXPLICIT_NOTIFY); @@ -6129,7 +6402,6 @@ add_option(struct options *options, options->ce.explicit_exit_notification = 1; } } -#endif else if (streq(p[0], "persist-tun") && !p[1]) { VERIFY_PERMISSION(OPT_P_PERSIST); @@ -6227,6 +6499,18 @@ add_option(struct options *options, } } } + else if (streq(p[0], "route-ipv6-gateway") && p[1] && !p[2]) + { + if (ipv6_addr_safe(p[1])) + { + options->route_ipv6_default_gateway = p[1]; + } + else + { + msg(msglevel, "route-ipv6-gateway parm '%s' must be a valid address", p[1]); + goto err; + } + } else if (streq(p[0], "route-metric") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_ROUTE); @@ -6316,6 +6600,18 @@ add_option(struct options *options, int j; VERIFY_PERMISSION(OPT_P_ROUTE); rol_check_alloc(options); + + if (options->routes->flags & RG_ENABLE) + { + msg(M_WARN, + "WARNING: You have specified redirect-gateway and " + "redirect-private at the same time (or the same option " + "multiple times). This is not well supported and may lead to " + "unexpected results"); + } + + options->routes->flags |= RG_ENABLE; + if (streq(p[0], "redirect-gateway")) { options->routes->flags |= RG_REROUTE_GW; @@ -6353,7 +6649,7 @@ add_option(struct options *options, } else if (streq(p[j], "!ipv4")) { - options->routes->flags &= ~RG_REROUTE_GW; + options->routes->flags &= ~(RG_REROUTE_GW | RG_ENABLE); } else { @@ -6365,7 +6661,11 @@ add_option(struct options *options, /* we need this here to handle pushed --redirect-gateway */ remap_redirect_gateway_flags(options); #endif - options->routes->flags |= RG_ENABLE; + } + else if (streq(p[0], "block-ipv6") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_ROUTE); + options->block_ipv6 = true; } else if (streq(p[0], "remote-random-hostname") && !p[1]) { @@ -6384,12 +6684,10 @@ add_option(struct options *options, msg(msglevel, "this is a generic configuration and cannot directly be used"); goto err; } -#ifdef ENABLE_PUSH_PEER_INFO else if (streq(p[1], "PUSH_PEER_INFO") && !p[2]) { options->push_peer_info = true; } -#endif else if (streq(p[1], "SERVER_POLL_TIMEOUT") && p[2]) { options->ce.connect_timeout = positive_atoi(p[2]); @@ -6412,7 +6710,7 @@ add_option(struct options *options, else if (streq(p[0], "script-security") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); - script_security = atoi(p[1]); + script_security_set(atoi(p[1])); } else if (streq(p[0], "mssfix") && !p[2]) { @@ -6427,15 +6725,12 @@ add_option(struct options *options, } } -#ifdef ENABLE_OCC else if (streq(p[0], "disable-occ") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->occ = false; } -#endif #if P2MP -#if P2MP_SERVER else if (streq(p[0], "server") && p[1] && p[2] && !p[4]) { const int lev = M_WARN; @@ -6479,9 +6774,12 @@ add_option(struct options *options, msg(msglevel, "error parsing --server-ipv6 parameter"); goto err; } - if (netbits < 64 || netbits > 112) + if (netbits < 64 || netbits > 124) { - msg( msglevel, "--server-ipv6 settings: only /64../112 supported right now (not /%d)", netbits ); + msg(msglevel, + "--server-ipv6 settings: network must be between /64 and /124 (not /%d)", + netbits); + goto err; } options->server_ipv6_defined = true; @@ -6583,12 +6881,6 @@ add_option(struct options *options, options->ifconfig_pool_persist_refresh_freq = positive_atoi(p[2]); } } - else if (streq(p[0], "ifconfig-pool-linear") && !p[1]) - { - VERIFY_PERMISSION(OPT_P_GENERAL); - options->topology = TOP_P2P; - msg(M_WARN, "DEPRECATED OPTION: --ifconfig-pool-linear, use --topology p2p instead"); - } else if (streq(p[0], "ifconfig-ipv6-pool") && p[1] && !p[2]) { const int lev = M_WARN; @@ -6601,9 +6893,11 @@ add_option(struct options *options, msg(msglevel, "error parsing --ifconfig-ipv6-pool parameters"); goto err; } - if (netbits < 64 || netbits > 112) + if (netbits < 64 || netbits > 124) { - msg( msglevel, "--ifconfig-ipv6-pool settings: only /64../112 supported right now (not /%d)", netbits ); + msg(msglevel, + "--ifconfig-ipv6-pool settings: network must be between /64 and /124 (not /%d)", + netbits); goto err; } @@ -6667,8 +6961,7 @@ add_option(struct options *options, 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"); + msg(M_FATAL, "REMOVED OPTION: --client-cert-not-required, use '--verify-client-cert none' instead"); } else if (streq(p[0], "verify-client-cert") && !p[2]) { @@ -6741,11 +7034,30 @@ 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")) + else if (streq(p[0], "auth-gen-token") && !p[3]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->auth_token_generate = true; options->auth_token_lifetime = p[1] ? positive_atoi(p[1]) : 0; + if (p[2]) + { + if (streq(p[2], "external-auth")) + { + options->auth_token_call_auth = true; + } + else + { + msg(msglevel, "Invalid argument to auth-gen-token: %s", p[2]); + } + } + + } + else if (streq(p[0], "auth-gen-token-secret") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); + options->auth_token_secret_file = p[1]; + options->auth_token_secret_file_inline = is_inline; + } else if (streq(p[0], "client-connect") && p[1]) { @@ -6964,7 +7276,6 @@ add_option(struct options *options, options->stale_routes_ageing_time = ageing_time; options->stale_routes_check_interval = check_interval; } -#endif /* P2MP_SERVER */ else if (streq(p[0], "client") && !p[1]) { @@ -6998,7 +7309,7 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); auth_retry_set(msglevel, p[1]); } -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT else if (streq(p[0], "static-challenge") && p[1] && p[2] && !p[3]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -7015,7 +7326,7 @@ add_option(struct options *options, #ifdef _WIN32 VERIFY_PERMISSION(OPT_P_GENERAL); HANDLE process = GetCurrentProcess(); - HANDLE handle = (HANDLE) atoi(p[1]); + HANDLE handle = (HANDLE) atoll(p[1]); if (!DuplicateHandle(process, handle, process, &options->msg_channel, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { @@ -7151,7 +7462,7 @@ add_option(struct options *options, { if (strstr(p[2], ":")) { - ipv6dns=true; + ipv6dns = true; foreign_option(options, p, 3, es); dhcp_option_dns6_parse(p[2], o->dns6, &o->dns6_len, msglevel); } @@ -7172,6 +7483,18 @@ add_option(struct options *options, { dhcp_option_address_parse("NBDD", p[2], o->nbdd, &o->nbdd_len, msglevel); } + else if (streq(p[1], "DOMAIN-SEARCH") && p[2]) + { + if (o->domain_search_list_len < N_SEARCH_LIST_LEN) + { + o->domain_search_list[o->domain_search_list_len++] = p[2]; + } + else + { + msg(msglevel, "--dhcp-option %s: maximum of %d search entries can be specified", + p[1], N_SEARCH_LIST_LEN); + } + } else if (streq(p[1], "DISABLE-NBT") && !p[2]) { o->disable_nbt = 1; @@ -7345,29 +7668,80 @@ add_option(struct options *options, } #endif #if defined(USE_COMP) + else if (streq(p[0], "allow-compression") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + + if (streq(p[1], "no")) + { + options->comp.flags = + COMP_F_ALLOW_STUB_ONLY|COMP_F_ADVERTISE_STUBS_ONLY; + if (comp_non_stub_enabled(&options->comp)) + { + msg(msglevel, "'--allow-compression no' conflicts with " + " enabling compression"); + } + } + else if (options->comp.flags & COMP_F_ALLOW_STUB_ONLY) + { + /* Also printed on a push to hint at configuration problems */ + msg(msglevel, "Cannot set allow-compression to '%s' " + "after set to 'no'", p[1]); + goto err; + } + else if (streq(p[1], "asym")) + { + options->comp.flags &= ~COMP_F_ALLOW_COMPRESS; + } + else if (streq(p[1], "yes")) + { + msg(M_WARN, "WARNING: Compression for sending and receiving enabled. Compression has " + "been used in the past to break encryption. Allowing compression allows " + "attacks that break encryption. Using \"--allow-compression yes\" is " + "strongly discouraged for common usage. See --compress in the manual " + "page for more information "); + + options->comp.flags |= COMP_F_ALLOW_COMPRESS; + } + else + { + msg(msglevel, "bad allow-compression option: %s -- " + "must be 'yes', 'no', or 'asym'", p[1]); + goto err; + } + } else if (streq(p[0], "comp-lzo") && !p[2]) { VERIFY_PERMISSION(OPT_P_COMP); + /* All lzo variants do not use swap */ + options->comp.flags &= ~COMP_F_SWAP; #if defined(ENABLE_LZO) if (p[1] && streq(p[1], "no")) #endif { options->comp.alg = COMP_ALG_STUB; - options->comp.flags = 0; + options->comp.flags &= ~COMP_F_ADAPTIVE; } #if defined(ENABLE_LZO) + else if (options->comp.flags & COMP_F_ALLOW_STUB_ONLY) + { + /* Also printed on a push to hint at configuration problems */ + msg(msglevel, "Cannot set comp-lzo to '%s', " + "allow-compression is set to 'no'", p[1]); + goto err; + } else if (p[1]) { if (streq(p[1], "yes")) { options->comp.alg = COMP_ALG_LZO; - options->comp.flags = 0; + options->comp.flags &= ~COMP_F_ADAPTIVE; } else if (streq(p[1], "adaptive")) { options->comp.alg = COMP_ALG_LZO; - options->comp.flags = COMP_F_ADAPTIVE; + options->comp.flags |= COMP_F_ADAPTIVE; } else { @@ -7378,12 +7752,17 @@ add_option(struct options *options, else { options->comp.alg = COMP_ALG_LZO; - options->comp.flags = COMP_F_ADAPTIVE; + options->comp.flags |= COMP_F_ADAPTIVE; } + show_compression_warning(&options->comp); #endif /* if defined(ENABLE_LZO) */ } else if (streq(p[0], "comp-noadapt") && !p[1]) { + /* + * We do not need to check here if we allow compression since + * it only modifies a flag if compression is enabled + */ VERIFY_PERMISSION(OPT_P_COMP); options->comp.flags &= ~COMP_F_ADAPTIVE; } @@ -7395,30 +7774,36 @@ add_option(struct options *options, if (streq(p[1], "stub")) { options->comp.alg = COMP_ALG_STUB; - options->comp.flags = (COMP_F_SWAP|COMP_F_ADVERTISE_STUBS_ONLY); + 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; + options->comp.flags |= COMP_F_ADVERTISE_STUBS_ONLY; + } + else if (options->comp.flags & COMP_F_ALLOW_STUB_ONLY) + { + /* Also printed on a push to hint at configuration problems */ + msg(msglevel, "Cannot set compress to '%s', " + "allow-compression is set to 'no'", p[1]); + goto err; } #if defined(ENABLE_LZO) else if (streq(p[1], "lzo")) { options->comp.alg = COMP_ALG_LZO; - options->comp.flags = 0; + options->comp.flags &= ~(COMP_F_ADAPTIVE | COMP_F_SWAP); } #endif #if defined(ENABLE_LZ4) else if (streq(p[1], "lz4")) { options->comp.alg = COMP_ALG_LZ4; - options->comp.flags = COMP_F_SWAP; + 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 @@ -7430,11 +7815,11 @@ add_option(struct options *options, else { options->comp.alg = COMP_ALG_STUB; - options->comp.flags = COMP_F_SWAP; + options->comp.flags |= COMP_F_SWAP; } + show_compression_warning(&options->comp); } #endif /* USE_COMP */ -#ifdef ENABLE_CRYPTO else if (streq(p[0], "show-ciphers") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -7454,10 +7839,19 @@ add_option(struct options *options, { int key_direction; + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION); + key_direction = ascii2keydirection(msglevel, p[1]); if (key_direction >= 0) { - options->key_direction = key_direction; + if (permission_mask & OPT_P_GENERAL) + { + options->key_direction = key_direction; + } + else if (permission_mask & OPT_P_CONNECTION) + { + options->ce.key_direction = key_direction; + } } else { @@ -7466,12 +7860,10 @@ add_option(struct options *options, } else if (streq(p[0], "secret") && p[1] && !p[3]) { - VERIFY_PERMISSION(OPT_P_GENERAL); - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->shared_secret_file_inline = p[2]; - } - else if (p[2]) + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); + options->shared_secret_file = p[1]; + options->shared_secret_file_inline = is_inline; + if (!is_inline && p[2]) { int key_direction; @@ -7485,12 +7877,48 @@ add_option(struct options *options, goto err; } } - options->shared_secret_file = p[1]; } - else if (streq(p[0], "genkey") && !p[1]) + else if (streq(p[0], "genkey") && !p[4]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->genkey = true; + if (!p[1]) + { + options->genkey_type = GENKEY_SECRET; + } + else + { + if (streq(p[1], "secret") || streq(p[1], "tls-auth") + || streq(p[1], "tls-crypt")) + { + options->genkey_type = GENKEY_SECRET; + } + else if (streq(p[1], "tls-crypt-v2-server")) + { + options->genkey_type = GENKEY_TLS_CRYPTV2_SERVER; + } + else if (streq(p[1], "tls-crypt-v2-client")) + { + options->genkey_type = GENKEY_TLS_CRYPTV2_CLIENT; + if (p[3]) + { + options->genkey_extra_data = p[3]; + } + } + else if (streq(p[1], "auth-token")) + { + options->genkey_type = GENKEY_AUTH_TOKEN; + } + else + { + msg(msglevel, "unknown --genkey type: %s", p[1]); + } + + } + if (p[2]) + { + options->genkey_filename = p[2]; + } } else if (streq(p[0], "auth") && p[1] && !p[2]) { @@ -7499,18 +7927,33 @@ add_option(struct options *options, } else if (streq(p[0], "cipher") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_NCP); + VERIFY_PERMISSION(OPT_P_NCP|OPT_P_INSTANCE); options->ciphername = p[1]; } - else if (streq(p[0], "ncp-ciphers") && p[1] && !p[2]) + else if (streq(p[0], "data-ciphers-fallback") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE); + options->ciphername = p[1]; + options->enable_ncp_fallback = true; + } + else if ((streq(p[0], "data-ciphers") || streq(p[0], "ncp-ciphers")) + && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE); + if (streq(p[0], "ncp-ciphers")) + { + msg(M_INFO, "Note: Treating option '--ncp-ciphers' as " + " '--data-ciphers' (renamed in OpenVPN 2.5)."); + } options->ncp_ciphers = p[1]; } else if (streq(p[0], "ncp-disable") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE); options->ncp_enabled = false; + msg(M_WARN, "DEPRECATED OPTION: ncp-disable. Disabling " + "cipher negotiation is a deprecated debug feature that " + "will be removed in OpenVPN 2.6"); } else if (streq(p[0], "prng") && p[1] && !p[3]) { @@ -7588,11 +8031,6 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->mute_replay_warnings = true; } - else if (streq(p[0], "no-iv") && !p[1]) - { - VERIFY_PERMISSION(OPT_P_GENERAL); - options->use_iv = false; - } else if (streq(p[0], "replay-persist") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -7644,7 +8082,7 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->show_tls_ciphers = true; } - else if (streq(p[0], "show-curves") && !p[1]) + else if ((streq(p[0], "show-curves") || streq(p[0], "show-groups")) && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->show_curves = true; @@ -7652,6 +8090,9 @@ add_option(struct options *options, else if (streq(p[0], "ecdh-curve") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); + msg(M_WARN, "Consider setting groups/curves preference with " + "tls-groups instead of forcing a specific curve with " + "ecdh-curve."); options->ecdh_curve = p[1]; } else if (streq(p[0], "tls-server") && !p[1]) @@ -7664,14 +8105,11 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->tls_client = true; } - else if (streq(p[0], "ca") && p[1] && ((streq(p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) + else if (streq(p[0], "ca") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->ca_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->ca_file_inline = p[2]; - } + options->ca_file_inline = is_inline; } #ifndef ENABLE_CRYPTO_MBEDTLS else if (streq(p[0], "capath") && p[1] && !p[2]) @@ -7680,32 +8118,23 @@ add_option(struct options *options, options->ca_path = 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]) + else if (streq(p[0], "dh") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->dh_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->dh_file_inline = p[2]; - } + options->dh_file_inline = is_inline; } - else if (streq(p[0], "cert") && p[1] && ((streq(p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) + else if (streq(p[0], "cert") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->cert_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->cert_file_inline = p[2]; - } + options->cert_file_inline = is_inline; } - else if (streq(p[0], "extra-certs") && p[1] && ((streq(p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) + else if (streq(p[0], "extra-certs") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->extra_certs_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->extra_certs_file_inline = p[2]; - } + options->extra_certs_file_inline = is_inline; } else if (streq(p[0], "verify-hash") && p[1] && !p[3]) { @@ -7734,14 +8163,11 @@ add_option(struct options *options, options->cryptoapi_cert = p[1]; } #endif - else if (streq(p[0], "key") && p[1] && ((streq(p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) + else if (streq(p[0], "key") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->priv_key_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->priv_key_file_inline = p[2]; - } + options->priv_key_file_inline = is_inline; } else if (streq(p[0], "tls-version-min") && p[1] && !p[3]) { @@ -7772,14 +8198,11 @@ add_option(struct options *options, options->ssl_flags |= (ver << SSLF_TLS_VERSION_MAX_SHIFT); } #ifndef ENABLE_CRYPTO_MBEDTLS - else if (streq(p[0], "pkcs12") && p[1] && ((streq(p[1], INLINE_FILE_TAG) && p[2]) || !p[2]) && !p[3]) + else if (streq(p[0], "pkcs12") && p[1] && !p[2]) { - VERIFY_PERMISSION(OPT_P_GENERAL); + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); options->pkcs12_file = p[1]; - if (streq(p[1], INLINE_FILE_TAG) && p[2]) - { - options->pkcs12_file_inline = p[2]; - } + options->pkcs12_file_inline = is_inline; } #endif /* ENABLE_CRYPTO_MBEDTLS */ else if (streq(p[0], "askpass") && !p[2]) @@ -7815,13 +8238,11 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->single_session = true; } -#ifdef ENABLE_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") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); @@ -7842,19 +8263,21 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->cipher_list_tls13 = 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]) + else if (streq(p[0], "tls-groups") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_GENERAL); + options->tls_groups = p[1]; + } + else if (streq(p[0], "crl-verify") && p[1] && ((p[2] && streq(p[2], "dir")) + || !p[2])) + { + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE); 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]; - } + options->crl_file_inline = is_inline; } else if (streq(p[0], "tls-verify") && p[1]) { @@ -7874,49 +8297,24 @@ add_option(struct options *options, options->tls_export_cert = p[1]; } #endif -#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 + else if (streq(p[0], "compat-names")) { VERIFY_PERMISSION(OPT_P_GENERAL); - 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. This will be removed in OpenVPN 2.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); - } + msg(msglevel, "--compat-names was removed in OpenVPN 2.5. " + "Update your configuration."); + goto err; } else if (streq(p[0], "no-name-remapping") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); - 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. This will be removed in OpenVPN 2.5."); - compat_flag(COMPAT_FLAG_SET | COMPAT_NAMES); - compat_flag(COMPAT_FLAG_SET | COMPAT_NO_NAME_REMAPPING); -#endif + msg(msglevel, "--no-name-remapping was removed in OpenVPN 2.5. " + "Update your configuration."); + goto err; } 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 (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NAMES)) - { - msg(msglevel, "you cannot use --verify-x509-name with " - "--compat-names or --no-name-remapping"); - goto err; - } if (p[2]) { if (streq(p[2], "subject")) @@ -8012,10 +8410,14 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_TLS_PARMS); options->renegotiate_packets = positive_atoi(p[1]); } - else if (streq(p[0], "reneg-sec") && p[1] && !p[2]) + else if (streq(p[0], "reneg-sec") && p[1] && !p[3]) { VERIFY_PERMISSION(OPT_P_TLS_PARMS); options->renegotiate_seconds = positive_atoi(p[1]); + if (p[2]) + { + options->renegotiate_seconds_min = positive_atoi(p[2]); + } } else if (streq(p[0], "hand-window") && p[1] && !p[2]) { @@ -8029,51 +8431,75 @@ add_option(struct options *options, } 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]) - { - options->tls_auth_file_inline = p[2]; - } - else if (p[2]) + int key_direction = -1; + + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION|OPT_P_INLINE); + + if (permission_mask & OPT_P_GENERAL) { - int key_direction; + options->tls_auth_file = p[1]; + options->tls_auth_file_inline = is_inline; - key_direction = ascii2keydirection(msglevel, p[2]); - if (key_direction >= 0) + if (!is_inline && p[2]) { + key_direction = ascii2keydirection(msglevel, p[2]); + if (key_direction < 0) + { + goto err; + } options->key_direction = key_direction; } - else + + } + else if (permission_mask & OPT_P_CONNECTION) + { + options->ce.tls_auth_file = p[1]; + options->ce.tls_auth_file_inline = is_inline; + options->ce.key_direction = KEY_DIRECTION_BIDIRECTIONAL; + + if (!is_inline && p[2]) { - goto err; + key_direction = ascii2keydirection(msglevel, p[2]); + if (key_direction < 0) + { + goto err; + } + options->ce.key_direction = key_direction; } } - options->tls_auth_file = 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]) + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION|OPT_P_INLINE); + if (permission_mask & OPT_P_GENERAL) { - options->tls_crypt_inline = p[2]; + options->tls_crypt_file = p[1]; + options->tls_crypt_file_inline = is_inline; + } + else if (permission_mask & OPT_P_CONNECTION) + { + options->ce.tls_crypt_file = p[1]; + options->ce.tls_crypt_file_inline = is_inline; } - options->tls_crypt_file = p[1]; } - else if (streq(p[0], "key-method") && p[1] && !p[2]) + else if (streq(p[0], "tls-crypt-v2") && p[1] && !p[3]) { - int key_method; - - VERIFY_PERMISSION(OPT_P_GENERAL); - key_method = atoi(p[1]); - if (key_method < KEY_METHOD_MIN || key_method > KEY_METHOD_MAX) + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION|OPT_P_INLINE); + if (permission_mask & OPT_P_GENERAL) { - msg(msglevel, "key_method parameter (%d) must be >= %d and <= %d", - key_method, - KEY_METHOD_MIN, - KEY_METHOD_MAX); - goto err; + options->tls_crypt_v2_file = p[1]; + options->tls_crypt_v2_file_inline = is_inline; + } + else if (permission_mask & OPT_P_CONNECTION) + { + options->ce.tls_crypt_v2_file = p[1]; + options->ce.tls_crypt_v2_file_inline = is_inline; } - options->key_method = key_method; + } + else if (streq(p[0], "tls-crypt-v2-verify") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->tls_crypt_v2_verify_script = p[1]; } else if (streq(p[0], "x509-track") && p[1] && !p[2]) { @@ -8118,7 +8544,6 @@ add_option(struct options *options, options->x509_username_field = p[1]; } #endif /* ENABLE_X509ALTUSERNAME */ -#endif /* ENABLE_CRYPTO */ #ifdef ENABLE_PKCS11 else if (streq(p[0], "show-pkcs11-ids") && !p[3]) { @@ -8234,7 +8659,7 @@ add_option(struct options *options, options->use_peer_id = true; options->peer_id = atoi(p[1]); } -#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 +#ifdef HAVE_EXPORT_KEYING_MATERIAL else if (streq(p[0], "keying-material-exporter") && p[1] && p[2]) { int ekm_length = positive_atoi(p[2]); @@ -8256,12 +8681,51 @@ add_option(struct options *options, options->keying_material_exporter_label = p[1]; options->keying_material_exporter_length = ekm_length; } -#endif /* if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 */ +#endif /* HAVE_EXPORT_KEYING_MATERIAL */ else if (streq(p[0], "allow-recursive-routing") && !p[1]) { VERIFY_PERMISSION(OPT_P_GENERAL); options->allow_recursive_routing = true; } + else if (streq(p[0], "vlan-tagging") && !p[1]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->vlan_tagging = true; + } + else if (streq(p[0], "vlan-accept") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + if (streq(p[1], "tagged")) + { + options->vlan_accept = VLAN_ONLY_TAGGED; + } + else if (streq(p[1], "untagged")) + { + options->vlan_accept = VLAN_ONLY_UNTAGGED_OR_PRIORITY; + } + else if (streq(p[1], "all")) + { + options->vlan_accept = VLAN_ALL; + } + else + { + msg(msglevel, "--vlan-accept must be 'tagged', 'untagged' or 'all'"); + goto err; + } + } + else if (streq(p[0], "vlan-pvid") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INSTANCE); + options->vlan_pvid = positive_atoi(p[1]); + if (options->vlan_pvid < OPENVPN_8021Q_MIN_VID + || options->vlan_pvid > OPENVPN_8021Q_MAX_VID) + { + msg(msglevel, + "the parameter of --vlan-pvid parameters must be >= %u and <= %u", + OPENVPN_8021Q_MIN_VID, OPENVPN_8021Q_MAX_VID); + goto err; + } + } else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index f3cafea..877e939 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -41,9 +41,7 @@ #include "comp.h" #include "pushlist.h" #include "clinat.h" -#ifdef ENABLE_CRYPTO #include "crypto_backend.h" -#endif /* @@ -81,7 +79,7 @@ struct options_pre_pull }; #endif -#if defined(ENABLE_CRYPTO) && !defined(ENABLE_CRYPTO_OPENSSL) && !defined(ENABLE_CRYPTO_MBEDTLS) +#if !defined(ENABLE_CRYPTO_OPENSSL) && !defined(ENABLE_CRYPTO_MBEDTLS) #error "At least one of OpenSSL or mbed TLS needs to be defined." #endif @@ -132,6 +130,20 @@ struct connection_entry #define CE_MAN_QUERY_REMOTE_MASK (0x07) #define CE_MAN_QUERY_REMOTE_SHIFT (2) unsigned int flags; + + /* Shared secret used for TLS control channel authentication */ + const char *tls_auth_file; + bool tls_auth_file_inline; + int key_direction; + + /* Shared secret used for TLS control channel authenticated encryption */ + const char *tls_crypt_file; + bool tls_crypt_file_inline; + + /* Client-specific secret or server key used for TLS control channel + * authenticated encryption v2 */ + const char *tls_crypt_v2_file; + bool tls_crypt_v2_file_inline; }; struct remote_entry @@ -157,6 +169,13 @@ struct remote_list struct remote_entry *array[CONNECTION_LIST_SIZE]; }; +enum vlan_acceptable_frames +{ + VLAN_ONLY_TAGGED, + VLAN_ONLY_UNTAGGED_OR_PRIORITY, + VLAN_ALL, +}; + struct remote_host_store { #define RH_HOST_LEN 80 @@ -165,6 +184,13 @@ struct remote_host_store char port[RH_PORT_LEN]; }; +enum genkey_type { + GENKEY_SECRET, + GENKEY_TLS_CRYPTV2_CLIENT, + GENKEY_TLS_CRYPTV2_SERVER, + GENKEY_AUTH_TOKEN +}; + /* Command line options */ struct options { @@ -188,7 +214,6 @@ struct options bool persist_config; int persist_mode; -#ifdef ENABLE_CRYPTO const char *key_pass_file; bool show_ciphers; bool show_digests; @@ -196,7 +221,9 @@ struct options bool show_tls_ciphers; bool show_curves; bool genkey; -#endif + enum genkey_type genkey_type; + const char *genkey_filename; + const char *genkey_extra_data; /* Networking parms */ int connect_retry_max; @@ -235,9 +262,7 @@ struct options int proto_force; -#ifdef ENABLE_OCC bool mtu_test; -#endif #ifdef ENABLE_MEMSTATS char *memstats_fn; @@ -325,6 +350,7 @@ struct options /* mark value */ int mark; + char *bind_dev; /* socket flags */ unsigned int sockflags; @@ -333,6 +359,7 @@ struct options const char *route_script; const char *route_predown_script; const char *route_default_gateway; + const char *route_ipv6_default_gateway; int route_default_metric; bool route_noexec; int route_delay; @@ -340,15 +367,14 @@ struct options bool route_delay_defined; struct route_option_list *routes; struct route_ipv6_option_list *routes_ipv6; /* IPv6 */ + bool block_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 */ struct client_nat_option_list *client_nat; -#ifdef ENABLE_OCC /* Enable options consistency check between peers */ bool occ; -#endif #ifdef ENABLE_MANAGEMENT const char *management_addr; @@ -375,7 +401,6 @@ struct options #if P2MP -#if P2MP_SERVER /* the tmp dir is for now only used in the P2P server context */ const char *tmp_dir; bool server_defined; @@ -429,6 +454,7 @@ struct options bool push_ifconfig_constraint_defined; in_addr_t push_ifconfig_constraint_network; in_addr_t push_ifconfig_constraint_netmask; + bool push_ifconfig_ipv4_blocked; /* IPv4 */ bool push_ifconfig_ipv6_defined; /* IPv6 */ struct in6_addr push_ifconfig_ipv6_local; /* IPv6 */ int push_ifconfig_ipv6_netbits; /* IPv6 */ @@ -446,13 +472,17 @@ 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; + bool auth_token_gen_secret_file; + bool auth_token_call_auth; + int auth_token_lifetime; + const char *auth_token_secret_file; + bool auth_token_secret_file_inline; + #if PORT_SHARE char *port_share_host; char *port_share_port; const char *port_share_journal_dir; #endif -#endif /* if P2MP_SERVER */ bool client; bool pull; /* client pull of config options from server */ @@ -463,17 +493,18 @@ struct options int scheduled_exit_interval; -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT struct static_challenge_info sc_info; #endif #endif /* if P2MP */ -#ifdef ENABLE_CRYPTO /* Cipher parms */ const char *shared_secret_file; - const char *shared_secret_file_inline; + bool shared_secret_file_inline; int key_direction; const char *ciphername; + bool enable_ncp_fallback; /**< If defined fall back to + * ciphername if NCP fails */ bool ncp_enabled; const char *ncp_ciphers; const char *authname; @@ -486,7 +517,6 @@ struct options int replay_window; int replay_time; const char *packet_id_file; - bool use_iv; bool test_crypto; #ifdef ENABLE_PREDICTION_RESISTANCE bool use_prediction_resistance; @@ -496,14 +526,21 @@ struct options bool tls_server; bool tls_client; const char *ca_file; + bool ca_file_inline; const char *ca_path; const char *dh_file; + bool dh_file_inline; const char *cert_file; + bool cert_file_inline; const char *extra_certs_file; + bool extra_certs_file_inline; const char *priv_key_file; + bool priv_key_file_inline; const char *pkcs12_file; + bool pkcs12_file_inline; const char *cipher_list; const char *cipher_list_tls13; + const char *tls_groups; const char *tls_cert_profile; const char *ecdh_curve; const char *tls_verify; @@ -511,14 +548,7 @@ struct options const char *verify_x509_name; const char *tls_export_cert; const char *crl_file; - - 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 */ + bool crl_file_inline; int ns_cert_type; /* set to 0, NS_CERT_CHECK_SERVER, or NS_CERT_CHECK_CLIENT */ unsigned remote_cert_ku[MAX_PARMS]; @@ -540,10 +570,6 @@ struct options #ifdef ENABLE_CRYPTOAPI const char *cryptoapi_cert; #endif - - /* data channel key exchange method */ - int key_method; - /* Per-packet timeout on control channel */ int tls_timeout; @@ -551,6 +577,7 @@ struct options int renegotiate_bytes; int renegotiate_packets; int renegotiate_seconds; + int renegotiate_seconds_min; /* Data channel key handshake must finalize * within n seconds of handshake initiation. */ @@ -566,23 +593,28 @@ struct options /* Shared secret used for TLS control channel authentication */ const char *tls_auth_file; - const char *tls_auth_file_inline; + bool tls_auth_file_inline; /* Shared secret used for TLS control channel authenticated encryption */ const char *tls_crypt_file; - const char *tls_crypt_inline; + bool tls_crypt_file_inline; + + /* Client-specific secret or server key used for TLS control channel + * authenticated encryption v2 */ + const char *tls_crypt_v2_file; + bool tls_crypt_v2_file_inline; + + const char *tls_crypt_v2_metadata; + + const char *tls_crypt_v2_verify_script; /* Allow only one session */ bool single_session; -#ifdef ENABLE_PUSH_PEER_INFO bool push_peer_info; -#endif bool tls_exit; -#endif /* ENABLE_CRYPTO */ - const struct x509_track *x509_track; /* special state parms */ @@ -595,17 +627,22 @@ struct options bool show_net_up; int route_method; bool block_outside_dns; + enum windows_driver_type windows_driver; #endif bool use_peer_id; uint32_t peer_id; -#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10001000 +#ifdef HAVE_EXPORT_KEYING_MATERIAL /* Keying Material Exporters [RFC 5705] */ const char *keying_material_exporter_label; int keying_material_exporter_length; #endif + bool vlan_tagging; + enum vlan_acceptable_frames vlan_accept; + uint16_t vlan_pvid; + struct pull_filter_list *pull_filter_list; /* Useful when packets sent by openvpn itself are not subject @@ -635,7 +672,7 @@ struct options #define OPT_P_MTU (1<<14) /* TODO */ #define OPT_P_NICE (1<<15) #define OPT_P_PUSH (1<<16) -#define OPT_P_INSTANCE (1<<17) +#define OPT_P_INSTANCE (1<<17) /**< allowed in ccd, client-connect etc*/ #define OPT_P_CONFIG (1<<18) #define OPT_P_EXPLICIT_NOTIFY (1<<19) #define OPT_P_ECHO (1<<20) @@ -647,15 +684,14 @@ struct options #define OPT_P_SOCKFLAGS (1<<26) #define OPT_P_CONNECTION (1<<27) #define OPT_P_PEER_ID (1<<28) +#define OPT_P_INLINE (1<<29) #define OPT_P_DEFAULT (~(OPT_P_INSTANCE|OPT_P_PULL_MODE)) #if P2MP #define PULL_DEFINED(opt) ((opt)->pull) -#if P2MP_SERVER #define PUSH_DEFINED(opt) ((opt)->push_list) #endif -#endif #ifndef PULL_DEFINED #define PULL_DEFINED(opt) (false) @@ -718,13 +754,12 @@ void show_settings(const struct options *o); bool string_defined_equal(const char *s1, const char *s2); -#ifdef ENABLE_OCC - const char *options_string_version(const char *s, struct gc_arena *gc); char *options_string(const struct options *o, const struct frame *frame, struct tuntap *tt, + openvpn_net_ctx_t *ctx, bool remote, struct gc_arena *gc); @@ -736,8 +771,6 @@ bool options_cmp_equal(char *actual, const char *expected); void options_warning(char *actual, const char *expected); -#endif - /** * Given an OpenVPN options string, extract the value of an option. * diff --git a/src/openvpn/otime.c b/src/openvpn/otime.c index 805aac9..640168a 100644 --- a/src/openvpn/otime.c +++ b/src/openvpn/otime.c @@ -88,9 +88,9 @@ const char * tv_string(const struct timeval *tv, struct gc_arena *gc) { struct buffer out = alloc_buf_gc(64, gc); - buf_printf(&out, "[%d/%d]", - (int) tv->tv_sec, - (int )tv->tv_usec); + buf_printf(&out, "[%" PRIi64 "/%ld]", + (int64_t)tv->tv_sec, + (long)tv->tv_usec); return BSTR(&out); } @@ -103,7 +103,7 @@ const char * tv_string_abs(const struct timeval *tv, struct gc_arena *gc) { return time_string((time_t) tv->tv_sec, - (int) tv->tv_usec, + (long) tv->tv_usec, true, gc); } @@ -127,12 +127,15 @@ time_string(time_t t, int usec, bool show_usec, struct gc_arena *gc) } t = tv.tv_sec; - buf_printf(&out, "%s", ctime(&t)); - buf_rmtail(&out, '\n'); + struct tm *tm = localtime(&t); + + buf_printf(&out, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); if (show_usec && tv.tv_usec) { - buf_printf(&out, " us=%d", (int)tv.tv_usec); + buf_printf(&out, " us=%ld", (long)tv.tv_usec); } return BSTR(&out); @@ -198,10 +201,10 @@ time_test(void) t = time(NULL); gettimeofday(&tv, NULL); #if 1 - msg(M_INFO, "t=%u s=%u us=%u", - (unsigned int)t, - (unsigned int)tv.tv_sec, - (unsigned int)tv.tv_usec); + msg(M_INFO, "t=%" PRIi64 " s=%" PRIi64 " us=%ld", + (int64_t)t, + (int64_t)tv.tv_sec, + (long)tv.tv_usec); #endif } } diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c index d58761b..0c74487 100644 --- a/src/openvpn/packet_id.c +++ b/src/openvpn/packet_id.c @@ -38,8 +38,6 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - #include "packet_id.h" #include "misc.h" #include "integer.h" @@ -349,7 +347,7 @@ packet_id_send_update(struct packet_id_send *p, bool long_form) bool packet_id_write(struct packet_id_send *p, struct buffer *buf, bool long_form, - bool prepend) + bool prepend) { if (!packet_id_send_update(p, long_form)) { @@ -608,14 +606,14 @@ packet_id_debug_print(int msglevel, } buf_printf(&out, "%c", c); } - buf_printf(&out, "] " time_format ":" packet_id_format, (time_type)p->time, (packet_id_print_type)p->id); + buf_printf(&out, "] %" PRIi64 ":" packet_id_format, (int64_t)p->time, (packet_id_print_type)p->id); if (pin) { - buf_printf(&out, " " time_format ":" packet_id_format, (time_type)pin->time, (packet_id_print_type)pin->id); + buf_printf(&out, " %" PRIi64 ":" packet_id_format, (int64_t)pin->time, (packet_id_print_type)pin->id); } - buf_printf(&out, " t=" time_format "[%d]", - (time_type)prev_now, + buf_printf(&out, " t=%" PRIi64 "[%d]", + (int64_t)prev_now, (int)(prev_now - tv.tv_sec)); buf_printf(&out, " r=[%d,%d,%d,%d,%d]", @@ -668,8 +666,8 @@ packet_id_interactive_test(void) { packet_id_reap_test(&pid.rec); test = packet_id_test(&pid.rec, &pin); - printf("packet_id_test (" time_format ", " packet_id_format ") returned %d\n", - (time_type)pin.time, + printf("packet_id_test (%" PRIi64 ", " packet_id_format ") returned %d\n", + (int64_t)pin.time, (packet_id_print_type)pin.id, test); if (test) @@ -681,8 +679,8 @@ packet_id_interactive_test(void) { long_form = (count < 20); packet_id_alloc_outgoing(&pid.send, &pin, long_form); - printf("(" time_format "(" packet_id_format "), %d)\n", - (time_type)pin.time, + printf("(%" PRIi64 "(" packet_id_format "), %d)\n", + (int64_t)pin.time, (packet_id_print_type)pin.id, long_form); if (pid.send.id == 10) @@ -695,5 +693,3 @@ packet_id_interactive_test(void) packet_id_free(&pid); } #endif /* ifdef PID_TEST */ - -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/packet_id.h b/src/openvpn/packet_id.h index f984e7c..3b58da2 100644 --- a/src/openvpn/packet_id.h +++ b/src/openvpn/packet_id.h @@ -27,8 +27,6 @@ * attempts to replay them back later. */ -#ifdef ENABLE_CRYPTO - #ifndef PACKET_ID_H #define PACKET_ID_H @@ -260,12 +258,12 @@ bool packet_id_read(struct packet_id_net *pin, struct buffer *buf, bool long_for * @param p Packet ID state. * @param buf Buffer to write the packet ID too * @param long_form If true, also update and write time_t to buf - * @param prepend If true, prepend to buffer, otherwise apppend. + * @param prepend If true, prepend to buffer, otherwise append. * * @return true if successful, false otherwise. */ bool packet_id_write(struct packet_id_send *p, struct buffer *buf, - bool long_form, bool prepend); + bool long_form, bool prepend); /* * Inline functions. @@ -342,4 +340,3 @@ packet_id_reap_test(struct packet_id_rec *p) } #endif /* PACKET_ID_H */ -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/pf-inline.h b/src/openvpn/pf-inline.h deleted file mode 100644 index 90cc41c..0000000 --- a/src/openvpn/pf-inline.h +++ /dev/null @@ -1,63 +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-2018 OpenVPN Inc <sales@openvpn.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#if defined(ENABLE_PF) && !defined(PF_INLINE_H) -#define PF_INLINE_H - -/* - * Inline functions - */ - -#define PCT_SRC 1 -#define PCT_DEST 2 -static inline bool -pf_c2c_test(const struct context *src, const struct context *dest, const char *prefix) -{ - bool pf_cn_test(struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix); - - return (!src->c2.pf.enabled || pf_cn_test(src->c2.pf.pfs, dest->c2.tls_multi, PCT_DEST, prefix)) - && (!dest->c2.pf.enabled || pf_cn_test(dest->c2.pf.pfs, src->c2.tls_multi, PCT_SRC, prefix)); -} - -static inline bool -pf_addr_test(const struct context *src, const struct mroute_addr *dest, const char *prefix) -{ - bool pf_addr_test_dowork(const struct context *src, const struct mroute_addr *dest, const char *prefix); - - if (src->c2.pf.enabled) - { - return pf_addr_test_dowork(src, dest, prefix); - } - else - { - return true; - } -} - -static inline bool -pf_kill_test(const struct pf_set *pfs) -{ - return pfs->kill; -} - -#endif /* if defined(ENABLE_PF) && !defined(PF_INLINE_H) */ diff --git a/src/openvpn/pf.c b/src/openvpn/pf.c index 7277ae6..f9bbfb5 100644 --- a/src/openvpn/pf.c +++ b/src/openvpn/pf.c @@ -35,9 +35,9 @@ #include "init.h" #include "memdbg.h" +#include "pf.h" #include "ssl_verify.h" -#include "pf-inline.h" static void pf_destroy(struct pf_set *pfs) @@ -547,9 +547,7 @@ pf_check_reload(struct context *c) const int wakeup_transition = 60; bool reloaded = false; - if (c->c2.pf.enabled - && c->c2.pf.filename - && event_timeout_trigger(&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT)) + if (c->c2.pf.filename) { platform_stat_t s; if (!platform_stat(c->c2.pf.filename, &s)) @@ -618,19 +616,18 @@ pf_load_from_buffer_list(struct context *c, const struct buffer_list *config) void pf_init_context(struct context *c) { - struct gc_arena gc = gc_new(); #ifdef PLUGIN_PF if (plugin_defined(c->plugins, OPENVPN_PLUGIN_ENABLE_PF)) { - const char *pf_file = create_temp_file(c->options.tmp_dir, "pf", &gc); - if (pf_file) + c->c2.pf.filename = platform_create_temp_file(c->options.tmp_dir, "pf", + &c->c2.gc); + if (c->c2.pf.filename) { - setenv_str(c->c2.es, "pf_file", pf_file); + setenv_str(c->c2.es, "pf_file", c->c2.pf.filename); if (plugin_call(c->plugins, OPENVPN_PLUGIN_ENABLE_PF, NULL, NULL, c->c2.es) == OPENVPN_PLUGIN_FUNC_SUCCESS) { event_timeout_init(&c->c2.pf.reload, 1, now); - c->c2.pf.filename = string_alloc(pf_file, &c->c2.gc); c->c2.pf.enabled = true; #ifdef ENABLE_DEBUG if (check_debug_level(D_PF_DEBUG)) @@ -639,10 +636,12 @@ pf_init_context(struct context *c) } #endif } - else - { - msg(M_WARN, "WARNING: OPENVPN_PLUGIN_ENABLE_PF disabled"); - } + } + if (!c->c2.pf.enabled) + { + msg(M_WARN, "WARNING: failed to init PF plugin, rejecting client."); + register_signal(c, SIGUSR1, "plugin-pf-init-failed"); + return; } } #endif /* ifdef PLUGIN_PF */ @@ -658,7 +657,6 @@ pf_init_context(struct context *c) #endif } #endif - gc_free(&gc); } void diff --git a/src/openvpn/pf.h b/src/openvpn/pf.h index ff75a00..c64d21b 100644 --- a/src/openvpn/pf.h +++ b/src/openvpn/pf.h @@ -31,6 +31,9 @@ #define PF_MAX_LINE_LEN 256 +#define PCT_SRC 1 +#define PCT_DEST 2 + struct context; struct ipv4_subnet { @@ -75,7 +78,7 @@ struct pf_context { bool enabled; struct pf_set *pfs; #ifdef PLUGIN_PF - char *filename; + const char *filename; time_t file_last_mod; unsigned int n_check_reload; struct event_timeout reload; @@ -101,4 +104,44 @@ void pf_context_print(const struct pf_context *pfc, const char *prefix, const in #endif +bool pf_addr_test_dowork(const struct context *src, + const struct mroute_addr *dest, const char *prefix); + +static inline bool +pf_addr_test(const struct pf_context *src_pf, const struct context *src, + const struct mroute_addr *dest, const char *prefix) +{ + if (src_pf->enabled) + { + return pf_addr_test_dowork(src, dest, prefix); + } + else + { + return true; + } +} + +/* + * Inline functions + */ + +bool pf_cn_test(struct pf_set *pfs, const struct tls_multi *tm, const int type, + const char *prefix); + +static inline bool +pf_c2c_test(const struct pf_context *src_pf, const struct tls_multi *src, + const struct pf_context *dest_pf, const struct tls_multi *dest, + const char *prefix) +{ + return (!src_pf->enabled || pf_cn_test(src_pf->pfs, dest, PCT_DEST, prefix)) + && (!dest_pf->enabled || pf_cn_test(dest_pf->pfs, src, PCT_SRC, + prefix)); +} + +static inline bool +pf_kill_test(const struct pf_set *pfs) +{ + return pfs->kill; +} + #endif /* if defined(ENABLE_PF) && !defined(OPENVPN_PF_H) */ diff --git a/src/openvpn/ping-inline.h b/src/openvpn/ping-inline.h deleted file mode 100644 index 1a5c8bc..0000000 --- a/src/openvpn/ping-inline.h +++ /dev/null @@ -1,64 +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-2018 OpenVPN Inc <sales@openvpn.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef PING_INLINE_H -#define PING_INLINE_H - -/* - * Should we exit or restart due to ping (or other authenticated packet) - * not received in n seconds? - */ -static inline void -check_ping_restart(struct context *c) -{ - void check_ping_restart_dowork(struct context *c); - - if (c->options.ping_rec_timeout - && event_timeout_trigger(&c->c2.ping_rec_interval, - &c->c2.timeval, - (!c->options.ping_timer_remote - || link_socket_actual_defined(&c->c1.link_socket_addr.actual)) - ? ETT_DEFAULT : 15)) - { - check_ping_restart_dowork(c); - } -} - -/* - * Should we ping the remote? - */ -static inline void -check_ping_send(struct context *c) -{ - void check_ping_send_dowork(struct context *c); - - if (c->options.ping_send_timeout - && event_timeout_trigger(&c->c2.ping_send_interval, - &c->c2.timeval, - !TO_LINK_DEF(c) ? ETT_DEFAULT : 1)) - { - check_ping_send_dowork(c); - } -} - -#endif /* ifndef PING_INLINE_H */ diff --git a/src/openvpn/ping.c b/src/openvpn/ping.c index 208170d..aa176fd 100644 --- a/src/openvpn/ping.c +++ b/src/openvpn/ping.c @@ -33,7 +33,6 @@ #include "memdbg.h" -#include "ping-inline.h" /* * This random string identifies an OpenVPN ping packet. @@ -47,12 +46,8 @@ const uint8_t ping_string[] = { 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 }; -/* - * Should we exit or restart due to ping (or other authenticated packet) - * not received in n seconds? - */ void -check_ping_restart_dowork(struct context *c) +trigger_ping_timeout_signal(struct context *c) { struct gc_arena gc = gc_new(); switch (c->options.ping_rec_timeout_action) diff --git a/src/openvpn/ping.h b/src/openvpn/ping.h index 05793b4..6feaa87 100644 --- a/src/openvpn/ping.h +++ b/src/openvpn/ping.h @@ -43,4 +43,46 @@ is_ping_msg(const struct buffer *buf) return buf_string_match(buf, ping_string, PING_STRING_SIZE); } -#endif +/** + * Trigger the correct signal on a --ping timeout + * depending if --ping-exit is set (SIGTERM) or not + * (SIGUSR1) + */ +void trigger_ping_timeout_signal(struct context *c); + +void check_ping_send_dowork(struct context *c); + +/* + * Should we exit or restart due to ping (or other authenticated packet) + * not received in n seconds? + */ +static inline void +check_ping_restart(struct context *c) +{ + if (c->options.ping_rec_timeout + && event_timeout_trigger(&c->c2.ping_rec_interval, + &c->c2.timeval, + (!c->options.ping_timer_remote + || link_socket_actual_defined(&c->c1.link_socket_addr.actual)) + ? ETT_DEFAULT : 15)) + { + trigger_ping_timeout_signal(c); + } +} + +/* + * Should we ping the remote? + */ +static inline void +check_ping_send(struct context *c) +{ + if (c->options.ping_send_timeout + && event_timeout_trigger(&c->c2.ping_send_interval, + &c->c2.timeval, + !TO_LINK_DEF(c) ? ETT_DEFAULT : 1)) + { + check_ping_send_dowork(c); + } +} + +#endif /* ifndef PING_H */ diff --git a/src/openvpn/pkcs11_mbedtls.c b/src/openvpn/pkcs11_mbedtls.c index 7620624..bd704e0 100644 --- a/src/openvpn/pkcs11_mbedtls.c +++ b/src/openvpn/pkcs11_mbedtls.c @@ -39,60 +39,89 @@ #include "errlevel.h" #include "pkcs11_backend.h" #include "ssl_verify_backend.h" -#include <mbedtls/pkcs11.h> #include <mbedtls/x509.h> -int -pkcs11_init_tls_session(pkcs11h_certificate_t certificate, - struct tls_root_ctx *const ssl_ctx) +static bool +pkcs11_get_x509_cert(pkcs11h_certificate_t pkcs11_cert, mbedtls_x509_crt *cert) { - int ret = 1; + unsigned char *cert_blob = NULL; + size_t cert_blob_size = 0; + bool ret = false; - 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)) + if (pkcs11h_certificate_getCertificateBlob(pkcs11_cert, NULL, + &cert_blob_size) != CKR_OK) { - msg(M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + msg(M_WARN, "PKCS#11: Cannot retrieve certificate object size"); 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)) + check_malloc_return((cert_blob = calloc(1, cert_blob_size))); + if (pkcs11h_certificate_getCertificateBlob(pkcs11_cert, cert_blob, + &cert_blob_size) != CKR_OK) { - msg(M_FATAL, "PKCS#11: Cannot initialize mbed TLS private key object"); + msg(M_WARN, "PKCS#11: Cannot retrieve certificate 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))) + if (!mbed_ok(mbedtls_x509_crt_parse(cert, cert_blob, cert_blob_size))) { + msg(M_WARN, "PKCS#11: Could not parse certificate"); goto cleanup; } - ret = 0; - + ret = true; cleanup: + free(cert_blob); return ret; } +static bool +pkcs11_sign(void *pkcs11_cert, const void *src, size_t src_len, + void *dst, size_t dst_len) +{ + return CKR_OK == pkcs11h_certificate_signAny(pkcs11_cert, CKM_RSA_PKCS, + src, src_len, dst, &dst_len); +} + +int +pkcs11_init_tls_session(pkcs11h_certificate_t certificate, + struct tls_root_ctx *const ssl_ctx) +{ + ASSERT(NULL != ssl_ctx); + + ssl_ctx->pkcs11_cert = certificate; + + ALLOC_OBJ_CLEAR(ssl_ctx->crt_chain, mbedtls_x509_crt); + if (!pkcs11_get_x509_cert(certificate, ssl_ctx->crt_chain)) + { + msg(M_WARN, "PKCS#11: Cannot initialize certificate"); + return 1; + } + + if (tls_ctx_use_external_signing_func(ssl_ctx, pkcs11_sign, certificate)) + { + msg(M_WARN, "PKCS#11: Cannot register signing function"); + return 1; + } + + return 0; +} + char * pkcs11_certificate_dn(pkcs11h_certificate_t cert, struct gc_arena *gc) { char *ret = NULL; - mbedtls_x509_crt mbed_crt = {0}; + mbedtls_x509_crt mbed_crt = { 0 }; - if (mbedtls_pkcs11_x509_cert_bind(&mbed_crt, cert)) + if (!pkcs11_get_x509_cert(cert, &mbed_crt)) { - msg(M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + msg(M_WARN, "PKCS#11: Cannot retrieve mbed TLS certificate object"); goto cleanup; } if (!(ret = x509_get_subject(&mbed_crt, gc))) { - msg(M_FATAL, "PKCS#11: mbed TLS cannot parse subject"); + msg(M_WARN, "PKCS#11: mbed TLS cannot parse subject"); goto cleanup; } @@ -107,23 +136,21 @@ pkcs11_certificate_serial(pkcs11h_certificate_t cert, char *serial, size_t serial_len) { int ret = 1; + mbedtls_x509_crt mbed_crt = { 0 }; - mbedtls_x509_crt mbed_crt = {0}; - - if (mbedtls_pkcs11_x509_cert_bind(&mbed_crt, cert)) + if (!pkcs11_get_x509_cert(cert, &mbed_crt)) { - msg(M_FATAL, "PKCS#11: Cannot retrieve mbed TLS certificate object"); + msg(M_WARN, "PKCS#11: Cannot retrieve mbed TLS certificate object"); goto cleanup; } - if (-1 == mbedtls_x509_serial_gets(serial, serial_len, &mbed_crt.serial)) + if (mbedtls_x509_serial_gets(serial, serial_len, &mbed_crt.serial) < 0) { - msg(M_FATAL, "PKCS#11: mbed TLS cannot parse serial"); + msg(M_WARN, "PKCS#11: mbed TLS cannot parse serial"); goto cleanup; } ret = 0; - cleanup: mbedtls_x509_crt_free(&mbed_crt); diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c index fbffd0f..53d07f9 100644 --- a/src/openvpn/platform.c +++ b/src/openvpn/platform.c @@ -30,7 +30,9 @@ #include "syshead.h" #include "buffer.h" +#include "crypto.h" #include "error.h" +#include "misc.h" #include "win32.h" #include "memdbg.h" @@ -335,3 +337,150 @@ platform_stat(const char *path, platform_stat_t *buf) #endif } +/* create a temporary filename in directory */ +const char * +platform_create_temp_file(const char *directory, const char *prefix, struct gc_arena *gc) +{ + int fd; + const char *retfname = NULL; + unsigned int attempts = 0; + char fname[256] = { 0 }; + const char *fname_fmt = PACKAGE "_%.*s_%08lx%08lx.tmp"; + const int max_prefix_len = sizeof(fname) - (sizeof(PACKAGE) + 7 + (2 * 8)); + + while (attempts < 6) + { + ++attempts; + + if (!openvpn_snprintf(fname, sizeof(fname), fname_fmt, max_prefix_len, + prefix, (unsigned long) get_random(), + (unsigned long) get_random())) + { + msg(M_WARN, "ERROR: temporary filename too long"); + return NULL; + } + + retfname = platform_gen_path(directory, fname, gc); + if (!retfname) + { + msg(M_WARN, "Failed to create temporary filename and path"); + return NULL; + } + + /* Atomically create the file. Errors out if the file already + * exists. */ + fd = platform_open(retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); + if (fd != -1) + { + close(fd); + return retfname; + } + else if (fd == -1 && errno != EEXIST) + { + /* Something else went wrong, no need to retry. */ + msg(M_WARN | M_ERRNO, "Could not create temporary file '%s'", + retfname); + return NULL; + } + } + + msg(M_WARN, "Failed to create temporary file after %i attempts", attempts); + return NULL; +} + +/* + * Put a directory and filename together. + */ +const char * +platform_gen_path(const char *directory, const char *filename, + struct gc_arena *gc) +{ +#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 + const int CC_PATH_RESERVED = CC_SLASH; +#endif + + if (!gc) + { + return NULL; /* Would leak memory otherwise */ + } + + const char *safe_filename = string_mod_const(filename, CC_PRINT, CC_PATH_RESERVED, '_', gc); + + if (safe_filename + && strcmp(safe_filename, ".") + && strcmp(safe_filename, "..") +#ifdef _WIN32 + && win_safe_filename(safe_filename) +#endif + ) + { + const size_t outsize = strlen(safe_filename) + (directory ? strlen(directory) : 0) + 16; + struct buffer out = alloc_buf_gc(outsize, gc); + char dirsep[2]; + + dirsep[0] = OS_SPECIFIC_DIRSEP; + dirsep[1] = '\0'; + + if (directory) + { + buf_printf(&out, "%s%s", directory, dirsep); + } + buf_printf(&out, "%s", safe_filename); + + return BSTR(&out); + } + else + { + return NULL; + } +} + +bool +platform_absolute_pathname(const char *pathname) +{ + if (pathname) + { + const int c = pathname[0]; +#ifdef _WIN32 + return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\'); +#else + return c == '/'; +#endif + } + else + { + return false; + } +} + +/* return true if filename can be opened for read */ +bool +platform_test_file(const char *filename) +{ + bool ret = false; + if (filename) + { + FILE *fp = platform_fopen(filename, "r"); + if (fp) + { + fclose(fp); + ret = true; + } + else + { + if (openvpn_errno() == EACCES) + { + msg( M_WARN | M_ERRNO, "Could not access file '%s'", filename); + } + } + } + + dmsg(D_TEST_FILE, "TEST FILE '%s' [%d]", + filename ? filename : "UNDEF", + ret); + + return ret; +} diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h index 288937d..091fc9c 100644 --- a/src/openvpn/platform.h +++ b/src/openvpn/platform.h @@ -49,6 +49,7 @@ #endif #include "basic.h" +#include "buffer.h" /* Get/Set UID of process */ @@ -143,4 +144,21 @@ typedef struct stat platform_stat_t; #endif int platform_stat(const char *path, platform_stat_t *buf); +/** + * Create a temporary file in directory, returns the filename of the created + * file. + */ +const char *platform_create_temp_file(const char *directory, const char *prefix, + struct gc_arena *gc); + +/** Put a directory and filename together. */ +const char *platform_gen_path(const char *directory, const char *filename, + struct gc_arena *gc); + +/** Return true if pathname is absolute. */ +bool platform_absolute_pathname(const char *pathname); + +/** Return true if filename can be opened for read. */ +bool platform_test_file(const char *filename); + #endif /* ifndef PLATFORM_H */ diff --git a/src/openvpn/plugin.c b/src/openvpn/plugin.c index 0ab99ab..8b351c4 100644 --- a/src/openvpn/plugin.c +++ b/src/openvpn/plugin.c @@ -104,6 +104,12 @@ plugin_type_name(const int type) case OPENVPN_PLUGIN_CLIENT_CONNECT_V2: return "PLUGIN_CLIENT_CONNECT"; + case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER: + return "PLUGIN_CLIENT_CONNECT_DEFER"; + + case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2: + return "PLUGIN_CLIENT_CONNECT_DEFER_V2"; + case OPENVPN_PLUGIN_CLIENT_DISCONNECT: return "PLUGIN_CLIENT_DISCONNECT"; @@ -161,12 +167,13 @@ plugin_option_list_new(struct gc_arena *gc) } bool -plugin_option_list_add(struct plugin_option_list *list, char **p, struct gc_arena *gc) +plugin_option_list_add(struct plugin_option_list *list, char **p, + struct gc_arena *gc) { if (list->n < MAX_PLUGINS) { struct plugin_option *o = &list->plugins[list->n++]; - o->argv = make_extended_arg_array(p, gc); + o->argv = make_extended_arg_array(p, false, gc); if (o->argv[0]) { o->so_pathname = o->argv[0]; @@ -250,7 +257,7 @@ plugin_init_item(struct plugin *p, const struct plugin_option *o) * was parsed. * */ - if (!absolute_pathname(p->so_pathname) + if (!platform_absolute_pathname(p->so_pathname) && p->so_pathname[0] != '.') { char full[PATH_MAX]; @@ -260,7 +267,7 @@ plugin_init_item(struct plugin *p, const struct plugin_option *o) } else { - rel = !absolute_pathname(p->so_pathname); + rel = !platform_absolute_pathname(p->so_pathname); p->handle = dlopen(p->so_pathname, RTLD_NOW); } if (!p->handle) @@ -272,7 +279,7 @@ plugin_init_item(struct plugin *p, const struct plugin_option *o) #else /* ifndef _WIN32 */ - rel = !absolute_pathname(p->so_pathname); + rel = !platform_absolute_pathname(p->so_pathname); p->module = LoadLibraryW(wide_string(p->so_pathname, &gc)); if (!p->module) { @@ -520,11 +527,9 @@ plugin_call_item(const struct plugin *p, const int type, const struct argv *av, struct openvpn_plugin_string_list **retlist, - const char **envp -#ifdef ENABLE_CRYPTO - , int certdepth, + const char **envp, + int certdepth, openvpn_x509_cert_t *current_cert -#endif ) { int status = OPENVPN_PLUGIN_FUNC_SUCCESS; @@ -553,14 +558,8 @@ plugin_call_item(const struct plugin *p, (const char **const) envp, p->plugin_handle, per_client_context, -#ifdef ENABLE_CRYPTO (current_cert ? certdepth : -1), - current_cert -#else - -1, - NULL -#endif - }; + current_cert }; struct openvpn_plugin_args_func_return retargs; @@ -594,7 +593,7 @@ plugin_call_item(const struct plugin *p, p->so_pathname); } - argv_reset(&a); + argv_free(&a); gc_free(&gc); } return status; @@ -789,11 +788,9 @@ plugin_call_ssl(const struct plugin_list *pl, const int type, const struct argv *av, struct plugin_return *pr, - struct env_set *es -#ifdef ENABLE_CRYPTO - , int certdepth, + struct env_set *es, + int certdepth, openvpn_x509_cert_t *current_cert -#endif ) { if (pr) @@ -821,11 +818,9 @@ plugin_call_ssl(const struct plugin_list *pl, type, av, pr ? &pr->list[i] : NULL, - envp -#ifdef ENABLE_CRYPTO - ,certdepth, + envp, + certdepth, current_cert -#endif ); switch (status) { diff --git a/src/openvpn/plugin.h b/src/openvpn/plugin.h index ec2d1fe..bf4d71b 100644 --- a/src/openvpn/plugin.h +++ b/src/openvpn/plugin.h @@ -106,7 +106,8 @@ struct plugin_return struct plugin_option_list *plugin_option_list_new(struct gc_arena *gc); -bool plugin_option_list_add(struct plugin_option_list *list, char **p, struct gc_arena *gc); +bool plugin_option_list_add(struct plugin_option_list *list, char **p, + struct gc_arena *gc); #ifndef ENABLE_SMALL void plugin_option_list_print(const struct plugin_option_list *list, int msglevel); @@ -127,11 +128,9 @@ int plugin_call_ssl(const struct plugin_list *pl, const int type, const struct argv *av, struct plugin_return *pr, - struct env_set *es -#ifdef ENABLE_CRYPTO - , int current_cert_depth, + struct env_set *es, + int current_cert_depth, openvpn_x509_cert_t *current_cert -#endif ); void plugin_list_close(struct plugin_list *pl); @@ -189,11 +188,9 @@ plugin_call_ssl(const struct plugin_list *pl, const int type, const struct argv *av, struct plugin_return *pr, - struct env_set *es -#ifdef ENABLE_CRYPTO - , int current_cert_depth, + struct env_set *es, + int current_cert_depth, openvpn_x509_cert_t *current_cert -#endif ) { return 0; @@ -208,11 +205,9 @@ plugin_call(const struct plugin_list *pl, struct plugin_return *pr, struct env_set *es) { - return plugin_call_ssl(pl, type, av, pr, es -#ifdef ENABLE_CRYPTO - , -1, NULL -#endif - ); + return plugin_call_ssl(pl, type, av, pr, es, -1, NULL); } +void plugin_abort(void); + #endif /* OPENVPN_PLUGIN_H */ diff --git a/src/openvpn/pool.c b/src/openvpn/pool.c index da28bc0..1f74ac5 100644 --- a/src/openvpn/pool.c +++ b/src/openvpn/pool.c @@ -147,61 +147,126 @@ ifconfig_pool_verify_range(const int msglevel, const in_addr_t start, const in_a } struct ifconfig_pool * -ifconfig_pool_init(int type, in_addr_t start, in_addr_t end, - const bool duplicate_cn, +ifconfig_pool_init(const bool ipv4_pool, enum pool_type type, in_addr_t start, + in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits ) { struct gc_arena gc = gc_new(); struct ifconfig_pool *pool = NULL; + int pool_ipv4_size = -1, pool_ipv6_size = -1; ASSERT(start <= end && end - start < IFCONFIG_POOL_MAX); ALLOC_OBJ_CLEAR(pool, struct ifconfig_pool); - pool->type = type; pool->duplicate_cn = duplicate_cn; - switch (type) + pool->ipv4.enabled = ipv4_pool; + + if (pool->ipv4.enabled) { - case IFCONFIG_POOL_30NET: - pool->base = start & ~3; - pool->size = (((end | 3) + 1) - pool->base) >> 2; - break; + pool->ipv4.type = type; + switch (pool->ipv4.type) + { + case IFCONFIG_POOL_30NET: + pool->ipv4.base = start & ~3; + pool_ipv4_size = (((end | 3) + 1) - pool->ipv4.base) >> 2; + break; - case IFCONFIG_POOL_INDIV: - pool->base = start; - pool->size = end - start + 1; - break; + case IFCONFIG_POOL_INDIV: + pool->ipv4.base = start; + pool_ipv4_size = end - start + 1; + break; - default: - ASSERT(0); + default: + ASSERT(0); + } + + if (pool_ipv4_size < 2) + { + msg(M_FATAL, "IPv4 pool size is too small (%d), must be at least 2", + pool_ipv4_size); + } + + msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv4: base=%s size=%d", + print_in_addr_t(pool->ipv4.base, 0, &gc), pool_ipv4_size); + + pool->size = pool_ipv4_size; } /* IPv6 pools are always "INDIV" type */ - pool->ipv6 = ipv6_pool; + pool->ipv6.enabled = ipv6_pool; - if (pool->ipv6) + if (pool->ipv6.enabled) { - pool->base_ipv6 = ipv6_base; - pool->size_ipv6 = ipv6_netbits>96 ? ( 1<<(128-ipv6_netbits) ) + /* the host portion of the address will always be contained in the last + * 4 bytes, therefore we can just extract that and use it as base in + * integer form + */ + uint32_t base = (ipv6_base.s6_addr[12] << 24) + | (ipv6_base.s6_addr[13] << 16) + | (ipv6_base.s6_addr[14] << 8) + | ipv6_base.s6_addr[15]; + /* some bits of the last 4 bytes may still be part of the network + * portion of the address, therefore we need to set them to 0 + */ + if ((128 - ipv6_netbits) < 32) + { + /* extract only the bits that are really part of the host portion of + * the address. + * + * Example: if we have netbits=31, the first bit has to be zero'd, + * the following operation first computes mask=0x3fffff and then + * uses mask to extract the wanted bits from base + */ + uint32_t mask = (1 << (128 - ipv6_netbits) ) - 1; + base &= mask; + } + + pool->ipv6.base = ipv6_base; + pool_ipv6_size = ipv6_netbits >= 112 + ? (1 << (128 - ipv6_netbits)) - base : IFCONFIG_POOL_MAX; - msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s", - pool->size, pool->size_ipv6, ipv6_netbits, - print_in6_addr( pool->base_ipv6, 0, &gc )); + if (pool_ipv6_size < 2) + { + msg(M_FATAL, "IPv6 pool size is too small (%d), must be at least 2", + pool_ipv6_size); + } + + msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: base=%s size=%d netbits=%d", + print_in6_addr(pool->ipv6.base, 0, &gc), pool_ipv6_size, + ipv6_netbits); - /* the current code is very simple and assumes that the IPv6 - * pool is at least as big as the IPv4 pool, and we don't need - * to do separate math etc. for IPv6 + /* if there is no v4 pool, or the v6 pool is smaller, use + * v6 pool size as "unified pool size" */ - ASSERT( pool->size < pool->size_ipv6 ); + if (pool->size <= 0 || pool_ipv6_size < pool->size) + { + pool->size = pool_ipv6_size; + } } - ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size); + if (pool->ipv4.enabled && pool->ipv6.enabled) + { + if (pool_ipv4_size < pool_ipv6_size) + { + msg(M_INFO, "NOTE: IPv4 pool size is %d, IPv6 pool size is %d. " + "IPv4 pool size limits the number of clients that can be " + "served from the pool", pool_ipv4_size, pool_ipv6_size); + } + else if (pool_ipv4_size > pool_ipv6_size) + { + msg(M_WARN, "WARNING: IPv4 pool size is %d, IPv6 pool size is %d. " + "IPv6 pool size limits the number of clients that can be " + "served from the pool. This is likely a MISTAKE - please check " + "your configuration", pool_ipv4_size, pool_ipv6_size); + } + } - msg(D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d", - print_in_addr_t(pool->base, 0, &gc), - pool->size, pool->ipv6 ); + ASSERT(pool->size > 0); + + ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size); gc_free(&gc); return pool; @@ -213,6 +278,7 @@ ifconfig_pool_free(struct ifconfig_pool *pool) if (pool) { int i; + for (i = 0; i < pool->size; ++i) { ifconfig_pool_entry_free(&pool->list[i], true); @@ -239,32 +305,35 @@ ifconfig_pool_acquire(struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *r ipe->common_name = string_alloc(common_name, NULL); } - switch (pool->type) + if (pool->ipv4.enabled && local && remote) { - case IFCONFIG_POOL_30NET: + switch (pool->ipv4.type) { - in_addr_t b = pool->base + (i << 2); - *local = b + 1; - *remote = b + 2; - break; - } + case IFCONFIG_POOL_30NET: + { + in_addr_t b = pool->ipv4.base + (i << 2); + *local = b + 1; + *remote = b + 2; + break; + } - case IFCONFIG_POOL_INDIV: - { - in_addr_t b = pool->base + i; - *local = 0; - *remote = b; - break; - } + case IFCONFIG_POOL_INDIV: + { + in_addr_t b = pool->ipv4.base + i; + *local = 0; + *remote = b; + break; + } - default: - ASSERT(0); + default: + ASSERT(0); + } } /* IPv6 pools are always INDIV (--linear) */ - if (pool->ipv6 && remote_ipv6) + if (pool->ipv6.enabled && remote_ipv6) { - *remote_ipv6 = add_in6_addr( pool->base_ipv6, i ); + *remote_ipv6 = add_in6_addr(pool->ipv6.base, i); } } return i; @@ -274,6 +343,7 @@ bool ifconfig_pool_release(struct ifconfig_pool *pool, ifconfig_pool_handle hand, const bool hard) { bool ret = false; + if (pool && hand >= 0 && hand < pool->size) { ifconfig_pool_entry_free(&pool->list[hand], hard); @@ -286,22 +356,23 @@ ifconfig_pool_release(struct ifconfig_pool *pool, ifconfig_pool_handle hand, con * private access functions */ +/* currently handling IPv4 logic only */ static ifconfig_pool_handle ifconfig_pool_ip_base_to_handle(const struct ifconfig_pool *pool, const in_addr_t addr) { ifconfig_pool_handle ret = -1; - switch (pool->type) + switch (pool->ipv4.type) { case IFCONFIG_POOL_30NET: { - ret = (addr - pool->base) >> 2; + ret = (addr - pool->ipv4.base) >> 2; break; } case IFCONFIG_POOL_INDIV: { - ret = (addr - pool->base); + ret = (addr - pool->ipv4.base); break; } @@ -317,24 +388,64 @@ ifconfig_pool_ip_base_to_handle(const struct ifconfig_pool *pool, const in_addr_ return ret; } +static ifconfig_pool_handle +ifconfig_pool_ipv6_base_to_handle(const struct ifconfig_pool *pool, + const struct in6_addr *in_addr) +{ + ifconfig_pool_handle ret; + uint32_t base, addr; + + /* IPv6 pool is always IFCONFIG_POOL_INDIV. + * + * We assume the offset can't be larger than 2^32-1, therefore we compute + * the difference only among the last 4 bytes like if they were two 32bit + * long integers. The rest of the address must match. + */ + for (int i = 0; i < (12); i++) + { + if (pool->ipv6.base.s6_addr[i] != in_addr->s6_addr[i]) + { + return -1; + } + } + + base = (pool->ipv6.base.s6_addr[12] << 24) + | (pool->ipv6.base.s6_addr[13] << 16) + | (pool->ipv6.base.s6_addr[14] << 8) + | pool->ipv6.base.s6_addr[15]; + + addr = (in_addr->s6_addr[12] << 24) + | (in_addr->s6_addr[13] << 16) + | (in_addr->s6_addr[14] << 8) + | in_addr->s6_addr[15]; + + ret = addr - base; + if (ret < 0 || ret >= pool->size) + { + ret = -1; + } + + return ret; +} + static in_addr_t ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand) { in_addr_t ret = 0; - if (hand >= 0 && hand < pool->size) + if (pool->ipv4.enabled && hand >= 0 && hand < pool->size) { - switch (pool->type) + switch (pool->ipv4.type) { case IFCONFIG_POOL_30NET: { - ret = pool->base + (hand << 2); + ret = pool->ipv4.base + (hand << 2); break; } case IFCONFIG_POOL_INDIV: { - ret = pool->base + hand; + ret = pool->ipv4.base + hand; break; } @@ -349,29 +460,26 @@ ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, ifconfig_pool_ static struct in6_addr ifconfig_pool_handle_to_ipv6_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand) { - struct in6_addr ret = in6addr_any; + struct in6_addr ret = IN6ADDR_ANY_INIT; /* IPv6 pools are always INDIV (--linear) */ - if (hand >= 0 && hand < pool->size_ipv6) + if (pool->ipv6.enabled && hand >= 0 && hand < pool->size) { - ret = add_in6_addr( pool->base_ipv6, hand ); + ret = add_in6_addr( pool->ipv6.base, hand ); } return ret; } static void -ifconfig_pool_set(struct ifconfig_pool *pool, const char *cn, const in_addr_t addr, const bool fixed) +ifconfig_pool_set(struct ifconfig_pool *pool, const char *cn, + ifconfig_pool_handle h, const bool fixed) { - ifconfig_pool_handle h = ifconfig_pool_ip_base_to_handle(pool, addr); - if (h >= 0) - { - struct ifconfig_pool_entry *e = &pool->list[h]; - ifconfig_pool_entry_free(e, true); - e->in_use = false; - e->common_name = string_alloc(cn, NULL); - e->last_release = now; - e->fixed = fixed; - } + struct ifconfig_pool_entry *e = &pool->list[h]; + ifconfig_pool_entry_free(e, true); + e->in_use = false; + e->common_name = string_alloc(cn, NULL); + e->last_release = now; + e->fixed = fixed; } static void @@ -385,23 +493,26 @@ ifconfig_pool_list(const struct ifconfig_pool *pool, struct status_output *out) for (i = 0; i < pool->size; ++i) { const struct ifconfig_pool_entry *e = &pool->list[i]; + struct in6_addr ip6; + in_addr_t ip; + const char *ip6_str = ""; + const char *ip_str = ""; + if (e->common_name) { - const in_addr_t ip = ifconfig_pool_handle_to_ip_base(pool, i); - if (pool->ipv6) + if (pool->ipv4.enabled) { - struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base(pool, i); - status_printf(out, "%s,%s,%s", - e->common_name, - print_in_addr_t(ip, 0, &gc), - print_in6_addr(ip6, 0, &gc)); + ip = ifconfig_pool_handle_to_ip_base(pool, i); + ip_str = print_in_addr_t(ip, 0, &gc); } - else + + if (pool->ipv6.enabled) { - status_printf(out, "%s,%s", - e->common_name, - print_in_addr_t(ip, 0, &gc)); + ip6 = ifconfig_pool_handle_to_ipv6_base(pool, i); + ip6_str = print_in6_addr(ip6, 0, &gc); } + + status_printf(out, "%s,%s,%s", e->common_name, ip_str, ip6_str); } } gc_free(&gc); @@ -475,16 +586,17 @@ ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool * const int buf_size = 128; update_time(); + if (persist && persist->file && pool) { struct gc_arena gc = gc_new(); struct buffer in = alloc_buf_gc(256, &gc); - char *cn_buf; - char *ip_buf; + char *cn_buf, *ip_buf, *ip6_buf; int line = 0; ALLOC_ARRAY_CLEAR_GC(cn_buf, char, buf_size, &gc); ALLOC_ARRAY_CLEAR_GC(ip_buf, char, buf_size, &gc); + ALLOC_ARRAY_CLEAR_GC(ip6_buf, char, buf_size, &gc); while (true) { @@ -494,28 +606,109 @@ ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool * break; } ++line; - if (BLEN(&in)) + if (!BLEN(&in)) + { + continue; + } + + int c = *BSTR(&in); + if (c == '#' || c == ';') { - int c = *BSTR(&in); - if (c == '#' || c == ';') + continue; + } + + msg(M_INFO, "ifconfig_pool_read(), in='%s'", BSTR(&in)); + + /* The expected format of a line is: "CN,IP4,IP6". + * + * IP4 or IP6 may be empty when respectively no v4 or v6 pool + * was previously specified. + * + * This means that accepted strings can be: + * - CN,IP4,IP6 + * - CN,IP4 + * - CN,,IP6 + */ + if (!buf_parse(&in, ',', cn_buf, buf_size) + || !buf_parse(&in, ',', ip_buf, buf_size)) + { + continue; + } + + ifconfig_pool_handle h = -1, h6 = -1; + + if (strlen(ip_buf) > 0) + { + bool v4_ok = true; + in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &v4_ok, + NULL); + + if (!v4_ok) + { + msg(M_WARN, "pool: invalid IPv4 (%s) for CN=%s", ip_buf, + cn_buf); + } + else { - continue; + h = ifconfig_pool_ip_base_to_handle(pool, addr); + if (h < 0) + { + msg(M_WARN, + "pool: IPv4 (%s) out of pool range for CN=%s", + ip_buf, cn_buf); + } } - msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6", - BSTR(&in) ); + } + + if (buf_parse(&in, ',', ip6_buf, buf_size) && strlen(ip6_buf) > 0) + { + struct in6_addr addr6; - if (buf_parse(&in, ',', cn_buf, buf_size) - && buf_parse(&in, ',', ip_buf, buf_size)) + if (!get_ipv6_addr(ip6_buf, &addr6, NULL, M_WARN)) { - bool succeeded; - const in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL); - if (succeeded) + msg(M_WARN, "pool: invalid IPv6 (%s) for CN=%s", ip6_buf, + cn_buf); + } + else + { + h6 = ifconfig_pool_ipv6_base_to_handle(pool, &addr6); + if (h6 < 0) { - msg( M_INFO, "succeeded -> ifconfig_pool_set()"); - ifconfig_pool_set(pool, cn_buf, addr, persist->fixed); + msg(M_WARN, + "pool: IPv6 (%s) out of pool range for CN=%s", + ip6_buf, cn_buf); + } + + /* Rely on IPv6 if no IPv4 was provided or the one provided + * was not valid + */ + if (h < 0) + { + h = h6; } } } + + /* at the moment IPv4 and IPv6 share the same pool, therefore offsets + * have to match for the same client. + * + * If offsets differ we use the IPv4, therefore warn the user about this. + */ + if ((h6 >= 0) && (h != h6)) + { + msg(M_WARN, + "pool: IPv4 (%s) and IPv6 (%s) have different offsets! Relying on IPv4", + ip_buf, ip6_buf); + } + + /* if at least one among v4 and v6 was properly parsed, attempt + * setting an handle for this client + */ + if (h >= 0) + { + msg(M_INFO, "succeeded -> ifconfig_pool_set(hand=%d)",h); + ifconfig_pool_set(pool, cn_buf, h, persist->fixed); + } } ifconfig_pool_msg(pool, D_IFCONFIG_POOL); diff --git a/src/openvpn/pool.h b/src/openvpn/pool.h index 6de28ac..b06424c 100644 --- a/src/openvpn/pool.h +++ b/src/openvpn/pool.h @@ -34,8 +34,11 @@ #define IFCONFIG_POOL_MAX 65536 #define IFCONFIG_POOL_MIN_NETBITS 16 -#define IFCONFIG_POOL_30NET 0 -#define IFCONFIG_POOL_INDIV 1 +enum pool_type +{ + IFCONFIG_POOL_30NET, + IFCONFIG_POOL_INDIV +}; struct ifconfig_pool_entry { @@ -47,13 +50,17 @@ struct ifconfig_pool_entry struct ifconfig_pool { - in_addr_t base; - int size; - int type; bool duplicate_cn; - bool ipv6; - struct in6_addr base_ipv6; - unsigned int size_ipv6; + struct { + bool enabled; + enum pool_type type; + in_addr_t base; + } ipv4; + struct { + bool enabled; + struct in6_addr base; + } ipv6; + int size; struct ifconfig_pool_entry *list; }; @@ -65,7 +72,12 @@ struct ifconfig_pool_persist typedef int ifconfig_pool_handle; -struct ifconfig_pool *ifconfig_pool_init(int type, in_addr_t start, in_addr_t end, const bool duplicate_cn, const bool ipv6_pool, const struct in6_addr ipv6_base, const int ipv6_netbits ); +struct ifconfig_pool *ifconfig_pool_init(const bool ipv4_pool, + enum pool_type type, in_addr_t start, + in_addr_t end, const bool duplicate_cn, + const bool ipv6_pool, + const struct in6_addr ipv6_base, + const int ipv6_netbits); void ifconfig_pool_free(struct ifconfig_pool *pool); diff --git a/src/openvpn/proto.c b/src/openvpn/proto.c index 87c18e8..6f4d929 100644 --- a/src/openvpn/proto.c +++ b/src/openvpn/proto.c @@ -38,17 +38,17 @@ * If raw tunnel packet is IPv<X>, return true and increment * buffer offset to start of IP header. */ -static -bool -is_ipv_X( int tunnel_type, struct buffer *buf, int ip_ver ) +static bool +is_ipv_X(int tunnel_type, struct buffer *buf, int ip_ver) { int offset; + uint16_t proto; const struct openvpn_iphdr *ih; verify_align_4(buf); if (tunnel_type == DEV_TYPE_TUN) { - if (BLEN(buf) < (int) sizeof(struct openvpn_iphdr)) + if (BLEN(buf) < sizeof(struct openvpn_iphdr)) { return false; } @@ -57,24 +57,46 @@ is_ipv_X( int tunnel_type, struct buffer *buf, int ip_ver ) else if (tunnel_type == DEV_TYPE_TAP) { const struct openvpn_ethhdr *eh; - if (BLEN(buf) < (int)(sizeof(struct openvpn_ethhdr) - + sizeof(struct openvpn_iphdr))) + if (BLEN(buf) < (sizeof(struct openvpn_ethhdr) + + sizeof(struct openvpn_iphdr))) { return false; } - eh = (const struct openvpn_ethhdr *) BPTR(buf); - if (ntohs(eh->proto) != (ip_ver == 6 ? OPENVPN_ETH_P_IPV6 : OPENVPN_ETH_P_IPV4)) + eh = (const struct openvpn_ethhdr *)BPTR(buf); + + /* start by assuming this is a standard Eth fram */ + proto = eh->proto; + offset = sizeof(struct openvpn_ethhdr); + + /* if this is a 802.1q frame, parse the header using the according + * format + */ + if (proto == htons(OPENVPN_ETH_P_8021Q)) + { + const struct openvpn_8021qhdr *evh; + if (BLEN(buf) < (sizeof(struct openvpn_ethhdr) + + sizeof(struct openvpn_iphdr))) + { + return false; + } + + evh = (const struct openvpn_8021qhdr *)BPTR(buf); + + proto = evh->proto; + offset = sizeof(struct openvpn_8021qhdr); + } + + if (ntohs(proto) != (ip_ver == 6 ? OPENVPN_ETH_P_IPV6 : OPENVPN_ETH_P_IPV4)) { return false; } - offset = sizeof(struct openvpn_ethhdr); } else { return false; } - ih = (const struct openvpn_iphdr *) (BPTR(buf) + offset); + ih = (const struct openvpn_iphdr *)(BPTR(buf) + offset); /* IP version is stored in the same bits for IPv4 or IPv6 header */ if (OPENVPN_IPH_GET_VER(ih->version_len) == ip_ver) @@ -98,6 +120,58 @@ is_ipv6(int tunnel_type, struct buffer *buf) return is_ipv_X( tunnel_type, buf, 6 ); } + +uint16_t +ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, + const uint8_t *src_addr, const uint8_t *dest_addr, const int proto) +{ + uint32_t sum = 0; + int addr_len = (af == AF_INET) ? 4 : 16; + + /* + * make 16 bit words out of every two adjacent 8 bit words and */ + /* calculate the sum of all 16 bit words + */ + for (int i = 0; i < len_payload; i += 2) + { + sum += (uint16_t)(((payload[i] << 8) & 0xFF00) + +((i + 1 < len_payload) ? (payload[i + 1] & 0xFF) : 0)); + + } + + /* + * add the pseudo header which contains the IP source and destination + * addresses + */ + for (int i = 0; i < addr_len; i += 2) + { + sum += (uint16_t)((src_addr[i] << 8) & 0xFF00) + (src_addr[i + 1] & 0xFF); + + } + for (int i = 0; i < addr_len; i += 2) + { + sum += (uint16_t)((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i + 1] & 0xFF); + } + + /* the length of the payload */ + sum += (uint16_t)len_payload; + + /* The next header or proto field*/ + sum += (uint16_t)proto; + + /* + * keep only the last 16 bits of the 32 bit calculated sum and add + * the carries + */ + while (sum >> 16) + { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + /* Take the one's complement of sum */ + return ((uint16_t) ~sum); +} + #ifdef PACKET_TRUNCATION_CHECK void diff --git a/src/openvpn/proto.h b/src/openvpn/proto.h index 985aa99..c251767 100644 --- a/src/openvpn/proto.h +++ b/src/openvpn/proto.h @@ -60,9 +60,31 @@ struct openvpn_ethhdr #define OPENVPN_ETH_P_IPV4 0x0800 /* IPv4 protocol */ #define OPENVPN_ETH_P_IPV6 0x86DD /* IPv6 protocol */ #define OPENVPN_ETH_P_ARP 0x0806 /* ARP protocol */ +#define OPENVPN_ETH_P_8021Q 0x8100 /* 802.1Q protocol */ uint16_t proto; /* packet type ID field */ }; +struct openvpn_8021qhdr +{ + uint8_t dest[OPENVPN_ETH_ALEN]; /* destination ethernet addr */ + uint8_t source[OPENVPN_ETH_ALEN]; /* source ethernet addr */ + + uint16_t tpid; /* 802.1Q Tag Protocol Identifier */ +#define OPENVPN_8021Q_MASK_PCP htons(0xE000) /* mask PCP out of pcp_cfi_vid */ +#define OPENVPN_8021Q_MASK_CFI htons(0x1000) /* mask CFI out of pcp_cfi_vid */ +#define OPENVPN_8021Q_MASK_VID htons(0x0FFF) /* mask VID out of pcp_cfi_vid */ + uint16_t pcp_cfi_vid; /* bit fields, see IEEE 802.1Q */ + uint16_t proto; /* contained packet type ID field */ +}; + +/* + * Size difference between a regular Ethernet II header and an Ethernet II + * header with additional IEEE 802.1Q tagging. + */ +#define SIZE_ETH_TO_8021Q_HDR (sizeof(struct openvpn_8021qhdr) \ + - sizeof(struct openvpn_ethhdr)) + + struct openvpn_arp { #define ARP_MAC_ADDR_TYPE 0x0001 uint16_t mac_addr_type; /* 0x0001 */ @@ -95,9 +117,10 @@ struct openvpn_iphdr { uint8_t ttl; -#define OPENVPN_IPPROTO_IGMP 2 /* IGMP protocol */ -#define OPENVPN_IPPROTO_TCP 6 /* TCP protocol */ -#define OPENVPN_IPPROTO_UDP 17 /* UDP protocol */ +#define OPENVPN_IPPROTO_IGMP 2 /* IGMP protocol */ +#define OPENVPN_IPPROTO_TCP 6 /* TCP protocol */ +#define OPENVPN_IPPROTO_UDP 17 /* UDP protocol */ +#define OPENVPN_IPPROTO_ICMPV6 58 /* ICMPV6 protocol */ uint8_t protocol; uint16_t check; @@ -120,6 +143,24 @@ struct openvpn_ipv6hdr { struct in6_addr daddr; }; +/* + * ICMPv6 header + */ +struct openvpn_icmp6hdr { +#define OPENVPN_ICMP6_DESTINATION_UNREACHABLE 1 +#define OPENVPN_ND_ROUTER_SOLICIT 133 +#define OPENVPN_ND_ROUTER_ADVERT 134 +#define OPENVPN_ND_NEIGHBOR_SOLICIT 135 +#define OPENVPN_ND_NEIGHBOR_ADVERT 136 +#define OPENVPN_ND_INVERSE_SOLICIT 141 +#define OPENVPN_ND_INVERSE_ADVERT 142 + uint8_t icmp6_type; +#define OPENVPN_ICMP6_DU_NOROUTE 0 +#define OPENVPN_ICMP6_DU_COMMUNICATION_PROHIBTED 1 + uint8_t icmp6_code; + uint16_t icmp6_cksum; + uint8_t icmp6_dataun[4]; +}; /* * UDP header @@ -265,6 +306,23 @@ bool is_ipv4(int tunnel_type, struct buffer *buf); bool is_ipv6(int tunnel_type, struct buffer *buf); +/** + * Calculates an IP or IPv6 checksum with a pseudo header as required by + * TCP, UDP and ICMPv6 + * + * @param af - Address family for which the checksum is calculated + * AF_INET or AF_INET6 + * @param payload - the TCP, ICMPv6 or UDP packet + * @param len_payload - length of payload + * @param src_addr - Source address of the packet + * @param dest_addr - Destination address of the packet + * @param proto next - header or IP protocol of the packet + * @return The calculated checksum in host order + */ +uint16_t +ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, + const uint8_t *src_addr, const uint8_t *dest_addr, const int proto); + #ifdef PACKET_TRUNCATION_CHECK void ipv4_packet_size_verify(const uint8_t *data, const int size, @@ -275,4 +333,7 @@ void ipv4_packet_size_verify(const uint8_t *data, #endif +#define OPENVPN_8021Q_MIN_VID 1 +#define OPENVPN_8021Q_MAX_VID 4094 + #endif /* ifndef PROTO_H */ diff --git a/src/openvpn/proxy.c b/src/openvpn/proxy.c index afcca86..9998623 100644 --- a/src/openvpn/proxy.c +++ b/src/openvpn/proxy.c @@ -318,7 +318,6 @@ static int get_proxy_authenticate(socket_descriptor_t sd, int timeout, char **data, - struct gc_arena *gc, volatile int *signal_received) { char buf[256]; @@ -341,14 +340,14 @@ get_proxy_authenticate(socket_descriptor_t sd, if (!strncmp(buf+20, "Basic ", 6)) { msg(D_PROXY, "PROXY AUTH BASIC: '%s'", buf); - *data = string_alloc(buf+26, gc); + *data = string_alloc(buf+26, NULL); ret = HTTP_AUTH_BASIC; } #if PROXY_DIGEST_AUTH else if (!strncmp(buf+20, "Digest ", 7)) { msg(D_PROXY, "PROXY AUTH DIGEST: '%s'", buf); - *data = string_alloc(buf+27, gc); + *data = string_alloc(buf+27, NULL); ret = HTTP_AUTH_DIGEST; } #endif @@ -885,10 +884,10 @@ establish_http_proxy_passthru(struct http_proxy_info *p, const char *algor = get_pa_var("algorithm", pa, &gc); const char *opaque = get_pa_var("opaque", pa, &gc); - if ( !realm || !nonce ) + if (!realm || !nonce) { msg(D_LINK_ERRORS, "HTTP proxy: digest auth failed, malformed response " - "from server: realm= or nonce= missing" ); + "from server: realm= or nonce= missing" ); goto error; } @@ -997,7 +996,6 @@ establish_http_proxy_passthru(struct http_proxy_info *p, const int method = get_proxy_authenticate(sd, get_server_poll_remaining_time(server_poll_timeout), &pa, - NULL, signal_received); if (method != HTTP_AUTH_NONE) { diff --git a/src/openvpn/ps.c b/src/openvpn/ps.c index 25ab374..2089e6b 100644 --- a/src/openvpn/ps.c +++ b/src/openvpn/ps.c @@ -985,7 +985,8 @@ is_openvpn_protocol(const struct buffer *buf) { return p[0] == 0 && p[1] >= 14 - && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT); + && (p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2 << P_OPCODE_SHIFT) + || p[2] == (P_CONTROL_HARD_RESET_CLIENT_V3 << P_OPCODE_SHIFT)); } else if (len >= 2) { diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 002be23..e0d2eea 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -33,6 +33,7 @@ #include "options.h" #include "ssl.h" #include "ssl_verify.h" +#include "ssl_ncp.h" #include "manage.h" #include "memdbg.h" @@ -69,19 +70,19 @@ receive_auth_failed(struct context *c, const struct buffer *buffer) { switch (auth_retry_get()) { - case AR_NONE: - c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ - break; + case AR_NONE: + c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ + break; - case AR_INTERACT: - ssl_purge_auth(false); + case AR_INTERACT: + ssl_purge_auth(false); - case AR_NOINTERACT: - c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ - break; + case AR_NOINTERACT: + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ + break; - default: - ASSERT(0); + default: + ASSERT(0); } c->sig->signal_text = "auth-failure"; } @@ -101,7 +102,7 @@ receive_auth_failed(struct context *c, const struct buffer *buffer) * Save the dynamic-challenge text even when management is defined */ { -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT struct buffer buf = *buffer; if (buf_string_match_head_str(&buf, "AUTH_FAILED,CRV1:") && BLEN(&buf)) { @@ -176,7 +177,60 @@ server_pushed_signal(struct context *c, const struct buffer *buffer, const bool } } -#if P2MP_SERVER +void +server_pushed_info(struct context *c, const struct buffer *buffer, + const int adv) +{ + const char *m = ""; + struct buffer buf = *buffer; + + if (buf_advance(&buf, adv) && buf_read_u8(&buf) == ',' && BLEN(&buf)) + { + m = BSTR(&buf); + } + +#ifdef ENABLE_MANAGEMENT + struct gc_arena gc; + if (management) + { + gc = gc_new(); + + /* + * We use >INFOMSG here instead of plain >INFO since INFO is used to + * for management greeting and we don't want to confuse the client + */ + struct buffer out = alloc_buf_gc(256, &gc); + buf_printf(&out, ">%s:%s", "INFOMSG", m); + management_notify_generic(management, BSTR(&out)); + + gc_free(&gc); + } + #endif + msg(D_PUSH, "Info command was pushed by server ('%s')", m); +} + +void +receive_cr_response(struct context *c, const struct buffer *buffer) +{ + struct buffer buf = *buffer; + const char *m = ""; + + if (buf_advance(&buf, 11) && buf_read_u8(&buf) == ',' && BLEN(&buf)) + { + m = BSTR(&buf); + } +#ifdef MANAGEMENT_DEF_AUTH + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + struct man_def_auth_context *mda = session->opt->mda_context; + struct env_set *es = session->opt->es; + int key_id = session->key[KS_PRIMARY].key_id; + + + management_notify_client_cr_response(key_id, mda, es, m); +#endif + msg(D_PUSH, "CR response was sent by client ('%s')", m); +} + /** * Add an option to the given push list by providing a format string. * @@ -233,6 +287,30 @@ send_auth_failed(struct context *c, const char *client_reason) gc_free(&gc); } +bool +send_auth_pending_messages(struct context *c, const char *extra) +{ + send_control_channel_string(c, "AUTH_PENDING", D_PUSH); + + static const char info_pre[] = "INFO_PRE,"; + + + size_t len = strlen(extra)+1 + sizeof(info_pre); + if (len > PUSH_BUNDLE_SIZE) + { + return false; + } + struct gc_arena gc = gc_new(); + + struct buffer buf = alloc_buf_gc(len, &gc); + buf_printf(&buf, info_pre); + buf_printf(&buf, "%s", extra); + send_control_channel_string(c, BSTR(&buf), D_PUSH); + + gc_free(&gc); + return true; +} + /* * Send restart message from server to client. */ @@ -243,8 +321,6 @@ send_restart(struct context *c, const char *kill_msg) send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH); } -#endif /* if P2MP_SERVER */ - /* * Push/Pull */ @@ -254,15 +330,12 @@ incoming_push_message(struct context *c, const struct buffer *buffer) { struct gc_arena gc = gc_new(); unsigned int option_types_found = 0; - int status; msg(D_PUSH, "PUSH: Received control message: '%s'", sanitize_control_message(BSTR(buffer), &gc)); - status = process_incoming_push_msg(c, - buffer, - c->options.pull, - pull_permission_mask(c), - &option_types_found); + int status = process_incoming_push_msg(c, buffer, c->options.pull, + pull_permission_mask(c), + &option_types_found); if (status == PUSH_MSG_ERROR) { @@ -282,29 +355,11 @@ incoming_push_message(struct context *c, const struct buffer *buffer) } } event_timeout_clear(&c->c2.push_request_interval); - } - else if (status == PUSH_MSG_REQUEST) - { - if (c->options.mode == MODE_SERVER) - { - struct frame *frame_fragment = NULL; -#ifdef ENABLE_FRAGMENT - if (c->options.ce.fragment) - { - frame_fragment = &c->c2.frame_fragment; - } -#endif - struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; - if (!tls_session_update_crypto_params(session, &c->options, - &c->c2.frame, frame_fragment)) - { - msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); - goto error; - } - } + event_timeout_clear(&c->c2.wait_for_connect); } goto cleanup; + error: register_signal(c, SIGUSR1, "process-push-msg-failed"); cleanup: @@ -328,10 +383,40 @@ send_push_request(struct context *c) } } -#if P2MP_SERVER +/** + * Prepare push option for auth-token + * @param tls_multi tls multi context of 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. + */ +void +prepare_auth_token_push_reply(struct tls_multi *tls_multi, struct gc_arena *gc, + struct push_list *push_list) +{ + /* + * If server uses --auth-gen-token and we have an auth token + * to send to the client + */ + if (tls_multi->auth_token) + { + push_option_fmt(gc, push_list, M_USAGE, + "auth-token %s", + tls_multi->auth_token); + if (!tls_multi->auth_token_initial) + { + /* + * Save the initial auth token for clients that ignore + * the updates to the token + */ + tls_multi->auth_token_initial = strdup(tls_multi->auth_token); + } + } +} /** - * Prepare push options, based on local options and available peer info. + * Prepare push options, based on local options * * @param context context structure storing data for VPN tunnel * @param gc gc arena for allocating push options @@ -339,13 +424,11 @@ send_push_request(struct context *c) * * @return true on success, false on failure. */ -static bool +bool prepare_push_reply(struct context *c, struct gc_arena *gc, struct push_list *push_list) { - 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; /* ipv6 */ @@ -360,7 +443,8 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, /* ipv4 */ if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local - && c->c2.push_ifconfig_remote_netmask) + && c->c2.push_ifconfig_remote_netmask + && !o->push_ifconfig_ipv4_blocked) { in_addr_t ifconfig_local = c->c2.push_ifconfig_local; if (c->c2.push_ifconfig_local_alias) @@ -373,58 +457,29 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, 0, gc)); } - /* Send peer-id if client supports it */ - optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; - if (optstr) + if (tls_multi->use_peer_id) { - 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); - tls_multi->use_peer_id = true; - } + push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", + tls_multi->peer_id); } - - /* Push cipher if client supports Negotiable Crypto Parameters */ - if (tls_peer_info_ncp_ver(peer_info) >= 2 && o->ncp_enabled) - { - /* if we have already created our key, we cannot *change* our own - * cipher -> so log the fact and push the "what we have now" cipher - * (so the client is always told what we expect it to use) - */ - 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, " - "re-sending previously negotiated cipher '%s'", - o->ciphername ); - } - else - { - /* 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); - } - else if (o->ncp_enabled) - { - tls_poor_mans_ncp(o, tls_multi->remote_ciphername); - } - - /* If server uses --auth-gen-token and we have an auth token + /* + * 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) + prepare_auth_token_push_reply(tls_multi, gc, push_list); + + /* + * Push the selected cipher, at this point the cipher has been + * already negotiated and been fixed. + * + * We avoid pushing the cipher to clients not supporting NCP + * to avoid error messages in their logs + */ + if (tls_peer_supports_ncp(c->c2.tls_multi->peer_info)) { - push_option_fmt(gc, push_list, M_USAGE, - "auth-token %s", tls_multi->auth_token); - tls_multi->auth_token_sent = true; + push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername); } + return true; } @@ -435,6 +490,7 @@ send_push_options(struct context *c, struct buffer *buf, { struct push_entry *e = push_list->head; + e = push_list->head; while (e) { if (e->enable) @@ -467,7 +523,26 @@ send_push_options(struct context *c, struct buffer *buf, return true; } -static bool +void +send_push_reply_auth_token(struct tls_multi *multi) +{ + struct gc_arena gc = gc_new(); + struct push_list push_list = { 0 }; + + prepare_auth_token_push_reply(multi, &gc, &push_list); + + /* prepare auth token should always add the auth-token option */ + struct push_entry *e = push_list.head; + ASSERT(e && e->enable); + + /* Construct a mimimal control channel push reply message */ + struct buffer buf = alloc_buf_gc(PUSH_BUNDLE_SIZE, &gc); + buf_printf(&buf, "%s, %s", push_reply_cmd, e->option); + send_control_channel_string_dowork(multi, BSTR(&buf), D_PUSH); + gc_free(&gc); +} + +bool send_push_reply(struct context *c, struct push_list *per_client_push_list) { struct gc_arena gc = gc_new(); @@ -586,7 +661,7 @@ clone_push_list(struct options *o) void push_options(struct options *o, char **p, int msglevel, struct gc_arena *gc) { - const char **argv = make_extended_arg_array(p, gc); + const char **argv = make_extended_arg_array(p, false, gc); char *opt = print_argv(argv, gc, 0); push_option(o, opt, msglevel); } @@ -620,6 +695,13 @@ push_remove_option(struct options *o, const char *p) { msg(D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p); + /* ifconfig is special, as not part of the push list */ + if (streq(p, "ifconfig")) + { + o->push_ifconfig_ipv4_blocked = true; + return; + } + /* ifconfig-ipv6 is special, as not part of the push list */ if (streq( p, "ifconfig-ipv6" )) { @@ -645,24 +727,19 @@ push_remove_option(struct options *o, const char *p) } } } -#endif /* if P2MP_SERVER */ -#if P2MP_SERVER int process_incoming_push_request(struct context *c) { int ret = PUSH_MSG_ERROR; -#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) { 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) + else if (c->c2.context_auth == CAS_SUCCEEDED) { time_t now; @@ -674,10 +751,9 @@ process_incoming_push_request(struct context *c) else { /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */ - struct push_list push_list; + struct push_list push_list = { 0 }; struct gc_arena gc = gc_new(); - CLEAR(push_list); if (prepare_push_reply(c, &gc, &push_list) && send_push_reply(c, &push_list)) { @@ -694,7 +770,6 @@ process_incoming_push_request(struct context *c) return ret; } -#endif /* if P2MP_SERVER */ static void push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt) @@ -716,6 +791,63 @@ push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt) } } +static int +process_incoming_push_reply(struct context *c, + unsigned int permission_mask, + unsigned int *option_types_found, + struct buffer *buf) +{ + int ret = PUSH_MSG_ERROR; + const uint8_t ch = buf_read_u8(buf); + if (ch == ',') + { + struct buffer buf_orig = (*buf); + if (!c->c2.pulled_options_digest_init_done) + { + c->c2.pulled_options_state = md_ctx_new(); + md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256")); + c->c2.pulled_options_digest_init_done = true; + } + if (!c->c2.did_pre_pull_restore) + { + pre_pull_restore(&c->options, &c->c2.gc); + c->c2.did_pre_pull_restore = true; + } + if (apply_push_options(&c->options, + buf, + permission_mask, + option_types_found, + c->c2.es)) + { + push_update_digest(c->c2.pulled_options_state, &buf_orig, + &c->options); + 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); + md_ctx_free(c->c2.pulled_options_state); + c->c2.pulled_options_state = NULL; + c->c2.pulled_options_digest_init_done = false; + ret = PUSH_MSG_REPLY; + break; + + case 2: + ret = PUSH_MSG_CONTINUATION; + break; + } + } + } + else if (ch == '\0') + { + ret = PUSH_MSG_REPLY; + } + /* show_settings (&c->options); */ + return ret; +} + int process_incoming_push_msg(struct context *c, const struct buffer *buffer, @@ -723,70 +855,25 @@ process_incoming_push_msg(struct context *c, 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); + c->c2.push_request_received = true; + return process_incoming_push_request(c); + } + else if (honor_received_options + && buf_string_compare_advance(&buf, push_reply_cmd)) + { + return process_incoming_push_reply(c, permission_mask, + option_types_found, &buf); } else -#endif - - if (honor_received_options && buf_string_compare_advance(&buf, "PUSH_REPLY")) { - const uint8_t ch = buf_read_u8(&buf); - if (ch == ',') - { - struct buffer buf_orig = buf; - if (!c->c2.pulled_options_digest_init_done) - { - c->c2.pulled_options_state = md_ctx_new(); - md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256")); - c->c2.pulled_options_digest_init_done = true; - } - if (!c->c2.did_pre_pull_restore) - { - pre_pull_restore(&c->options, &c->c2.gc); - c->c2.did_pre_pull_restore = true; - } - if (apply_push_options(&c->options, - &buf, - permission_mask, - option_types_found, - c->c2.es)) - { - push_update_digest(c->c2.pulled_options_state, &buf_orig, - &c->options); - 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); - md_ctx_free(c->c2.pulled_options_state); - c->c2.pulled_options_state = NULL; - c->c2.pulled_options_digest_init_done = false; - ret = PUSH_MSG_REPLY; - break; - - case 2: - ret = PUSH_MSG_CONTINUATION; - break; - } - } - } - else if (ch == '\0') - { - ret = PUSH_MSG_REPLY; - } - /* show_settings (&c->options); */ + return PUSH_MSG_ERROR; } - return ret; } -#if P2MP_SERVER /* * Remove iroutes from the push_list. @@ -850,6 +937,4 @@ remove_iroutes_from_push_route_list(struct options *o) } } -#endif /* if P2MP_SERVER */ - #endif /* if P2MP */ diff --git a/src/openvpn/push.h b/src/openvpn/push.h index 5f6181e..2faf19a 100644 --- a/src/openvpn/push.h +++ b/src/openvpn/push.h @@ -50,14 +50,19 @@ void receive_auth_failed(struct context *c, const struct buffer *buffer); void server_pushed_signal(struct context *c, const struct buffer *buffer, const bool restart, const int adv); +void server_pushed_info(struct context *c, const struct buffer *buffer, + const int adv); + +void receive_cr_response(struct context *c, const struct buffer *buffer); + void incoming_push_message(struct context *c, const struct buffer *buffer); -#if P2MP_SERVER 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_options(struct options *o, char **p, int msglevel, + struct gc_arena *gc); void push_reset(struct options *o); @@ -67,8 +72,22 @@ void remove_iroutes_from_push_route_list(struct options *o); void send_auth_failed(struct context *c, const char *client_reason); +/** + * Sends the auth pending control messages to a client. See + * doc/management-notes.txt under client-pending-auth for + * more details on message format + */ +bool send_auth_pending_messages(struct context *c, const char *extra); + void send_restart(struct context *c, const char *kill_msg); -#endif +/** + * Sends a push reply message only containin the auth-token to update + * the auth-token on the client + * + * @param multi - The tls_multi structure belonging to the instance to push to + */ +void send_push_reply_auth_token(struct tls_multi *multi); + #endif /* if P2MP */ #endif /* ifndef PUSH_H */ diff --git a/src/openvpn/pushlist.h b/src/openvpn/pushlist.h index 23b0ee5..967eda2 100644 --- a/src/openvpn/pushlist.h +++ b/src/openvpn/pushlist.h @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#if !defined(PUSHLIST_H) && P2MP && P2MP_SERVER +#if !defined(PUSHLIST_H) && P2MP #define PUSHLIST_H /* parameters to be pushed to peer */ @@ -37,5 +37,4 @@ struct push_list { struct push_entry *tail; }; - -#endif +#endif /* if !defined(PUSHLIST_H) && P2MP */ diff --git a/src/openvpn/reliable.c b/src/openvpn/reliable.c index 8f5e173..eae1e0c 100644 --- a/src/openvpn/reliable.c +++ b/src/openvpn/reliable.c @@ -34,8 +34,6 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - #include "buffer.h" #include "error.h" #include "common.h" @@ -354,7 +352,7 @@ reliable_empty(const struct reliable *rel) /* del acknowledged items from send buf */ void -reliable_send_purge(struct reliable *rel, struct reliable_ack *ack) +reliable_send_purge(struct reliable *rel, const struct reliable_ack *ack) { int i, j; for (i = 0; i < ack->len; ++i) @@ -464,7 +462,7 @@ reliable_wont_break_sequentiality(const struct reliable *rel, packet_id_type id) (packet_id_print_type)id, reliable_print_ids(rel, &gc)); } - dmsg(D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d\n", rel->size, rel->packet_id, id, ret); + dmsg(D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d", rel->size, rel->packet_id, id, ret); gc_free(&gc); return ret; @@ -567,30 +565,6 @@ reliable_can_send(const struct reliable *rel) return n_current > 0 && !rel->hold; } -#ifdef EXPONENTIAL_BACKOFF -/* return a unique point-in-time to trigger retry */ -static time_t -reliable_unique_retry(struct reliable *rel, time_t retry) -{ - int i; - while (true) - { - for (i = 0; i < rel->size; ++i) - { - struct reliable_entry *e = &rel->array[i]; - if (e->active && e->next_try == retry) - { - goto again; - } - } - break; -again: - ++retry; - } - return retry; -} -#endif /* ifdef EXPONENTIAL_BACKOFF */ - /* return next buffer to send to remote */ struct buffer * reliable_send(struct reliable *rel, int *opcode) @@ -614,7 +588,7 @@ reliable_send(struct reliable *rel, int *opcode) { #ifdef EXPONENTIAL_BACKOFF /* exponential backoff */ - best->next_try = reliable_unique_retry(rel, local_now + best->timeout); + best->next_try = local_now + best->timeout; best->timeout *= 2; #else /* constant timeout, no backoff */ @@ -788,24 +762,17 @@ reliable_debug_print(const struct reliable *rel, char *desc) printf("********* struct reliable %s\n", desc); printf(" initial_timeout=%d\n", (int)rel->initial_timeout); printf(" packet_id=" packet_id_format "\n", rel->packet_id); - printf(" now=" time_format "\n", now); + printf(" now=%" PRIi64 "\n", (int64_t)now); for (i = 0; i < rel->size; ++i) { const struct reliable_entry *e = &rel->array[i]; if (e->active) { printf(" %d: packet_id=" packet_id_format " len=%d", i, e->packet_id, e->buf.len); - printf(" next_try=" time_format, e->next_try); + printf(" next_try=%" PRIi64, (int64_t)e->next_try); printf("\n"); } } } #endif /* if 0 */ - -#else /* ifdef ENABLE_CRYPTO */ -static void -dummy(void) -{ -} -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/reliable.h b/src/openvpn/reliable.h index bc32ad9..688c65c 100644 --- a/src/openvpn/reliable.h +++ b/src/openvpn/reliable.h @@ -28,8 +28,6 @@ */ -#ifdef ENABLE_CRYPTO - #ifndef RELIABLE_H #define RELIABLE_H @@ -125,7 +123,7 @@ bool reliable_ack_read(struct reliable_ack *ack, * @param ack The acknowledgment structure containing received * acknowledgments. */ -void reliable_send_purge(struct reliable *rel, struct reliable_ack *ack); +void reliable_send_purge(struct reliable *rel, const struct reliable_ack *ack); /** @} name Functions for processing incoming acknowledgments */ @@ -476,4 +474,3 @@ void reliable_ack_debug_print(const struct reliable_ack *ack, char *desc); #endif /* RELIABLE_H */ -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/ring_buffer.h b/src/openvpn/ring_buffer.h new file mode 100644 index 0000000..4293f63 --- /dev/null +++ b/src/openvpn/ring_buffer.h @@ -0,0 +1,125 @@ +/* + * 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-2019 OpenVPN Inc <sales@openvpn.net> + * 2019 Lev Stipakov <lev@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef _WIN32 +#ifndef OPENVPN_RING_BUFFER_H +#define OPENVPN_RING_BUFFER_H + +#include <windows.h> +#include <winioctl.h> + +#include <stdint.h> +#include <stdbool.h> + +/* + * Values below are taken from Wireguard Windows client + * https://github.com/WireGuard/wireguard-go/blob/master/tun/wintun/ring_windows.go#L14 + */ +#define WINTUN_RING_CAPACITY 0x800000 +#define WINTUN_RING_TRAILING_BYTES 0x10000 +#define WINTUN_MAX_PACKET_SIZE 0xffff +#define WINTUN_PACKET_ALIGN 4 + +#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) + +/** + * Wintun ring buffer + * See https://github.com/WireGuard/wintun#ring-layout + */ +struct tun_ring +{ + volatile ULONG head; + volatile ULONG tail; + volatile LONG alertable; + UCHAR data[WINTUN_RING_CAPACITY + WINTUN_RING_TRAILING_BYTES]; +}; + +/** + * Struct for ring buffers registration + * See https://github.com/WireGuard/wintun#registering-rings + */ +struct tun_register_rings +{ + struct + { + ULONG ring_size; + struct tun_ring *ring; + HANDLE tail_moved; + } send, receive; +}; + +struct TUN_PACKET_HEADER +{ + uint32_t size; +}; + +struct TUN_PACKET +{ + uint32_t size; + UCHAR data[WINTUN_MAX_PACKET_SIZE]; +}; + +/** + * Registers ring buffers used to exchange data between + * userspace openvpn process and wintun kernel driver, + * see https://github.com/WireGuard/wintun#registering-rings + * + * @param device handle to opened wintun device + * @param send_ring pointer to send ring + * @param receive_ring pointer to receive ring + * @param send_tail_moved event set by wintun to signal openvpn + * that data is available for reading in send ring + * @param receive_tail_moved event set by openvpn to signal wintun + * that data has been written to receive ring + * @return true if registration is successful, false otherwise - use GetLastError() + */ +static bool +register_ring_buffers(HANDLE device, + struct tun_ring *send_ring, + struct tun_ring *receive_ring, + HANDLE send_tail_moved, + HANDLE receive_tail_moved) +{ + struct tun_register_rings rr; + BOOL res; + DWORD bytes_returned; + + ZeroMemory(&rr, sizeof(rr)); + + rr.send.ring = send_ring; + rr.send.ring_size = sizeof(struct tun_ring); + rr.send.tail_moved = send_tail_moved; + + rr.receive.ring = receive_ring; + rr.receive.ring_size = sizeof(struct tun_ring); + rr.receive.tail_moved = receive_tail_moved; + + res = DeviceIoControl(device, TUN_IOCTL_REGISTER_RINGS, &rr, sizeof(rr), + NULL, 0, &bytes_returned, NULL); + + return res != FALSE; +} + +#endif /* ifndef OPENVPN_RING_BUFFER_H */ +#endif /* ifdef _WIN32 */ diff --git a/src/openvpn/route.c b/src/openvpn/route.c index 4199da3..24563ed 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -36,11 +36,12 @@ #include "common.h" #include "error.h" #include "route.h" -#include "misc.h" +#include "run_command.h" #include "socket.h" #include "manage.h" #include "win32.h" #include "options.h" +#include "networking.h" #include "memdbg.h" @@ -62,7 +63,7 @@ static bool del_route_ipv6_service(const struct route_ipv6 *, const struct tunta #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); +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, openvpn_net_ctx_t *ctx); static void get_bypass_addresses(struct route_bypass *rb, const unsigned int flags); @@ -448,11 +449,6 @@ init_route_ipv6(struct route_ipv6 *r6, { r6->gateway = rl6->remote_endpoint_ipv6; } - else - { - msg(M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options"); - goto fail; - } /* metric */ @@ -613,7 +609,8 @@ init_route_list(struct route_list *rl, const char *remote_endpoint, int default_metric, in_addr_t remote_host, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); bool ret = true; @@ -634,7 +631,7 @@ init_route_list(struct route_list *rl, rl->spec.flags |= RTSA_DEFAULT_METRIC; } - get_default_gateway(&rl->rgi); + get_default_gateway(&rl->rgi, ctx); if (rl->rgi.flags & RGI_ADDR_DEFINED) { setenv_route_addr(es, "net_gateway", rl->rgi.gateway.addr, -1); @@ -768,7 +765,8 @@ init_route_ipv6_list(struct route_ipv6_list *rl6, const char *remote_endpoint, int default_metric, const struct in6_addr *remote_host_ipv6, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); bool ret = true; @@ -793,7 +791,7 @@ init_route_ipv6_list(struct route_ipv6_list *rl6, 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); + get_default_gateway_ipv6(&rl6->rgi6, remote_host_ipv6, ctx); if (rl6->rgi6.flags & RGI_ADDR_DEFINED) { setenv_str(es, "net_gateway_ipv6", print_in6_addr(rl6->rgi6.gateway.addr_ipv6, 0, &gc)); @@ -901,7 +899,8 @@ add_route3(in_addr_t network, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct route_ipv4 r; CLEAR(r); @@ -909,7 +908,7 @@ add_route3(in_addr_t network, r.network = network; r.netmask = netmask; r.gateway = gateway; - add_route(&r, tt, flags, rgi, es); + add_route(&r, tt, flags, rgi, es, ctx); } static void @@ -919,7 +918,8 @@ del_route3(in_addr_t network, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct route_ipv4 r; CLEAR(r); @@ -927,7 +927,7 @@ del_route3(in_addr_t network, r.network = network; r.netmask = netmask; r.gateway = gateway; - delete_route(&r, tt, flags, rgi, es); + delete_route(&r, tt, flags, rgi, es, ctx); } static void @@ -936,7 +936,8 @@ add_bypass_routes(struct route_bypass *rb, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { int i; for (i = 0; i < rb->n_bypass; ++i) @@ -949,7 +950,8 @@ add_bypass_routes(struct route_bypass *rb, tt, flags | ROUTE_REF_GW, rgi, - es); + es, + ctx); } } } @@ -960,7 +962,8 @@ del_bypass_routes(struct route_bypass *rb, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { int i; for (i = 0; i < rb->n_bypass; ++i) @@ -973,15 +976,18 @@ del_bypass_routes(struct route_bypass *rb, tt, flags | ROUTE_REF_GW, rgi, - es); + es, + ctx); } } } static void -redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { - const char err[] = "NOTE: unable to redirect default gateway --"; + const char err[] = "NOTE: unable to redirect IPv4 default gateway --"; if (rl && rl->flags & RG_ENABLE) { @@ -1035,7 +1041,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags | ROUTE_REF_GW, &rl->rgi, - es); + es, + ctx); rl->iflags |= RL_DID_LOCAL; } else @@ -1046,7 +1053,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un #endif /* ifndef TARGET_ANDROID */ /* route DHCP/DNS server traffic through original default gateway */ - add_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es); + add_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, + &rl->rgi, es, ctx); if (rl->flags & RG_REROUTE_GW) { @@ -1059,7 +1067,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); /* add new default route (2nd component) */ add_route3(0x80000000, @@ -1068,7 +1077,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); } else { @@ -1077,7 +1087,7 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un { /* delete default route */ del_route3(0, 0, rl->rgi.gateway.addr, tt, - flags | ROUTE_REF_GW, &rl->rgi, es); + flags | ROUTE_REF_GW, &rl->rgi, es, ctx); } /* add new default route */ @@ -1087,7 +1097,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); } } @@ -1098,7 +1109,10 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un } static void -undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +undo_redirect_default_route_to_vpn(struct route_list *rl, + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, + openvpn_net_ctx_t *ctx) { if (rl && rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY) { @@ -1111,12 +1125,14 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags | ROUTE_REF_GW, &rl->rgi, - es); + es, + ctx); rl->iflags &= ~RL_DID_LOCAL; } /* delete special DHCP/DNS bypass route */ - del_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es); + del_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, + &rl->rgi, es, ctx); if (rl->flags & RG_REROUTE_GW) { @@ -1129,7 +1145,8 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); /* delete default route (2nd component) */ del_route3(0x80000000, @@ -1138,7 +1155,8 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); } else { @@ -1149,12 +1167,13 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); /* restore original default route if there was any */ if (rl->rgi.flags & RGI_ADDR_DEFINED) { add_route3(0, 0, rl->rgi.gateway.addr, tt, - flags | ROUTE_REF_GW, &rl->rgi, es); + flags | ROUTE_REF_GW, &rl->rgi, es, ctx); } } } @@ -1164,13 +1183,23 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t } void -add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +add_routes(struct route_list *rl, struct route_ipv6_list *rl6, + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, openvpn_net_ctx_t *ctx) { - redirect_default_route_to_vpn(rl, tt, flags, es); + redirect_default_route_to_vpn(rl, tt, flags, es, ctx); if (rl && !(rl->iflags & RL_ROUTES_ADDED) ) { struct route_ipv4 *r; + if (rl->routes && !tt->did_ifconfig_setup) + { + msg(M_INFO, "WARNING: OpenVPN was configured to add an IPv4 " + "route. However, no IPv4 has been configured for %s, " + "therefore the route installation may fail or may not work " + "as expected.", tt->actual_name); + } + #ifdef ENABLE_MANAGEMENT if (management && rl->routes) { @@ -1189,9 +1218,9 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt check_subnet_conflict(r->network, r->netmask, "route"); if (flags & ROUTE_DELETE_FIRST) { - delete_route(r, tt, flags, &rl->rgi, es); + delete_route(r, tt, flags, &rl->rgi, es, ctx); } - add_route(r, tt, flags, &rl->rgi, es); + add_route(r, tt, flags, &rl->rgi, es, ctx); } rl->iflags |= RL_ROUTES_ADDED; } @@ -1202,18 +1231,18 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt if (!tt->did_ifconfig_ipv6_setup) { msg(M_INFO, "WARNING: OpenVPN was configured to add an IPv6 " - "route over %s. However, no IPv6 has been configured for " - "this interface, therefore the route installation may " - "fail or may not work as expected.", tt->actual_name); + "route. However, no IPv6 has been configured for %s, " + "therefore the route installation may fail or may not work " + "as expected.", tt->actual_name); } for (r = rl6->routes_ipv6; r; r = r->next) { if (flags & ROUTE_DELETE_FIRST) { - delete_route_ipv6(r, tt, flags, es); + delete_route_ipv6(r, tt, flags, es, ctx); } - add_route_ipv6(r, tt, flags, es); + add_route_ipv6(r, tt, flags, es, ctx); } rl6->iflags |= RL_ROUTES_ADDED; } @@ -1221,19 +1250,20 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt void delete_routes(struct route_list *rl, struct route_ipv6_list *rl6, - const struct tuntap *tt, unsigned int flags, const struct env_set *es) + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, openvpn_net_ctx_t *ctx) { if (rl && rl->iflags & RL_ROUTES_ADDED) { struct route_ipv4 *r; for (r = rl->routes; r; r = r->next) { - delete_route(r, tt, flags, &rl->rgi, es); + delete_route(r, tt, flags, &rl->rgi, es, ctx); } rl->iflags &= ~RL_ROUTES_ADDED; } - undo_redirect_default_route_to_vpn(rl, tt, flags, es); + undo_redirect_default_route_to_vpn(rl, tt, flags, es, ctx); if (rl) { @@ -1245,7 +1275,7 @@ delete_routes(struct route_list *rl, struct route_ipv6_list *rl6, struct route_ipv6 *r6; for (r6 = rl6->routes_ipv6; r6; r6 = r6->next) { - delete_route_ipv6(r6, tt, flags, es); + delete_route_ipv6(r6, tt, flags, es, ctx); } rl6->iflags &= ~RL_ROUTES_ADDED; } @@ -1322,7 +1352,7 @@ print_default_gateway(const int msglevel, #ifdef _WIN32 if (rgi->flags & RGI_IFACE_DEFINED) { - buf_printf(&out, " I=%u", (unsigned int)rgi->adapter_index); + buf_printf(&out, " I=%lu", rgi->adapter_index); } #else if (rgi->flags & RGI_IFACE_DEFINED) @@ -1353,7 +1383,7 @@ print_default_gateway(const int msglevel, #ifdef _WIN32 if (rgi6->flags & RGI_IFACE_DEFINED) { - buf_printf(&out, " I=%u", (unsigned int)rgi6->adapter_index); + buf_printf(&out, " I=%lu", rgi6->adapter_index); } #else if (rgi6->flags & RGI_IFACE_DEFINED) @@ -1525,15 +1555,18 @@ add_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, /* may be NULL */ - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); +#if !defined(TARGET_LINUX) const char *network; -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) const char *netmask; #endif const char *gateway; +#endif bool status = false; int is_local_route; @@ -1544,11 +1577,13 @@ add_route(struct route_ipv4 *r, gc_init(&gc); +#if !defined(TARGET_LINUX) network = print_in_addr_t(r->network, 0, &gc); -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) netmask = print_in_addr_t(r->netmask, 0, &gc); #endif gateway = print_in_addr_t(r->gateway, 0, &gc); +#endif is_local_route = local_route(r->network, r->netmask, r->gateway, rgi); if (is_local_route == LR_ERROR) @@ -1557,64 +1592,44 @@ add_route(struct route_ipv4 *r, } #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s route add %s/%d", - iproute_path, - network, - netmask_to_netbits2(r->netmask)); - - if (r->flags & RT_METRIC_DEFINED) - { - argv_printf_cat(&argv, "metric %d", r->metric); - } + const char *iface = NULL; + int metric = -1; if (is_on_link(is_local_route, flags, rgi)) { - argv_printf_cat(&argv, "dev %s", rgi->iface); - } - else - { - argv_printf_cat(&argv, "via %s", gateway); + iface = rgi->iface; } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s add -net %s netmask %s", - ROUTE_PATH, - network, - netmask); + if (r->flags & RT_METRIC_DEFINED) { - argv_printf_cat(&argv, "metric %d", r->metric); + metric = r->metric; } - if (is_on_link(is_local_route, flags, rgi)) - { - argv_printf_cat(&argv, "dev %s", rgi->iface); - } - else + + status = true; + if (net_route_v4_add(ctx, &r->network, netmask_to_netbits2(r->netmask), + &r->gateway, iface, 0, metric) < 0) { - argv_printf_cat(&argv, "gw %s", gateway); + msg(M_WARN, "ERROR: Linux route add command failed"); + status = false; } -#endif /*ENABLE_IPROUTE*/ - argv_msg(D_ROUTE, &argv); - status = openvpn_execve_check(&argv, es, 0, "ERROR: Linux route add command failed"); - #elif defined (TARGET_ANDROID) - struct buffer out = alloc_buf_gc(128, &gc); + char out[128]; if (rgi) { - buf_printf(&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); + openvpn_snprintf(out, sizeof(out), "%s %s %s dev %s", network, netmask, gateway, rgi->iface); } else { - buf_printf(&out, "%s %s %s", network, netmask, gateway); + openvpn_snprintf(out, sizeof(out), "%s %s %s", network, netmask, gateway); } - management_android_control(management, "ROUTE", buf_bptr(&out)); + management_android_control(management, "ROUTE", out); #elif defined (_WIN32) { DWORD ai = TUN_ADAPTER_INDEX_INVALID; - argv_printf(&argv, "%s%sc ADD %s MASK %s %s", + argv_printf(&argv, "%s%s ADD %s MASK %s %s", get_win_sys_path(), WIN_ROUTE_PATH_SUFFIX, network, @@ -1627,7 +1642,7 @@ add_route(struct route_ipv4 *r, if (is_on_link(is_local_route, flags, rgi)) { ai = rgi->adapter_index; - argv_printf_cat(&argv, "IF %u", (unsigned int)ai); + argv_printf_cat(&argv, "IF %lu", ai); } argv_msg(D_ROUTE, &argv); @@ -1815,8 +1830,10 @@ done: { r->flags &= ~RT_ADDED; } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route setup */ + net_ctx_reset(ctx); } @@ -1825,7 +1842,7 @@ 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) + * explicitly setup/clear the "connected" network routes on some OSes) */ int byte = 15; int bits_to_clear = 128 - r6->netbits; @@ -1844,7 +1861,9 @@ route_ipv6_clear_host_bits( struct route_ipv6 *r6 ) } void -add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); @@ -1853,7 +1872,9 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag const char *gateway; bool status = false; const char *device = tt->actual_name; - +#if defined(TARGET_LINUX) + int metric; +#endif bool gateway_needed = false; if (!(r6->flags & RT_DEFINED) ) @@ -1917,46 +1938,38 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag gateway_needed = true; } -#if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 route add %s/%d dev %s", - iproute_path, - network, - r6->netbits, - device); - if (gateway_needed) - { - argv_printf_cat(&argv, "via %s", gateway); - } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + if (gateway_needed && IN6_IS_ADDR_UNSPECIFIED(&r6->gateway)) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ROUTE6 WARNING: " PACKAGE_NAME " needs a gateway " + "parameter for a --route-ipv6 option and no default was set via " + "--ifconfig-ipv6 or --route-ipv6-gateway option. Not installing " + "IPv6 route to %s/%d.", network, r6->netbits); + status = false; + goto done; } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s -A inet6 add %s/%d dev %s", - ROUTE_PATH, - network, - r6->netbits, - device); - if (gateway_needed) +#if defined(TARGET_LINUX) + metric = -1; + if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0)) { - argv_printf_cat(&argv, "gw %s", gateway); + metric = r6->metric; } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + + status = true; + if (net_route_v6_add(ctx, &r6->network, r6->netbits, + gateway_needed ? &r6->gateway : NULL, device, 0, + metric) < 0) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ERROR: Linux IPv6 route can't be added"); + status = false; } -#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 (TARGET_ANDROID) - struct buffer out = alloc_buf_gc(64, &gc); + char out[64]; - buf_printf(&out, "%s/%d %s", network, r6->netbits, device); + openvpn_snprintf(out, sizeof(out), "%s/%d %s", network, r6->netbits, device); - management_android_control(management, "ROUTE6", buf_bptr(&out)); + management_android_control(management, "ROUTE6", out); #elif defined (_WIN32) @@ -1979,7 +1992,7 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag 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", + argv_printf(&argv, "%s%s interface ipv6 add route %s/%d %s", get_win_sys_path(), NETSH_PATH_SUFFIX, network, @@ -2114,6 +2127,7 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag 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 /* if defined(TARGET_LINUX) */ +done: if (status) { r6->flags |= RT_ADDED; @@ -2122,8 +2136,10 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag { r6->flags &= ~RT_ADDED; } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route setup */ + net_ctx_reset(ctx); } static void @@ -2131,17 +2147,22 @@ delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); +#if !defined(TARGET_LINUX) const char *network; -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) const char *netmask; #endif -#if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if !defined(TARGET_ANDROID) const char *gateway; #endif +#else /* if !defined(TARGET_LINUX) */ + int metric; +#endif int is_local_route; if ((r->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED)) @@ -2151,13 +2172,15 @@ delete_route(struct route_ipv4 *r, gc_init(&gc); +#if !defined(TARGET_LINUX) network = print_in_addr_t(r->network, 0, &gc); -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) netmask = print_in_addr_t(r->netmask, 0, &gc); #endif -#if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if !defined(TARGET_ANDROID) gateway = print_in_addr_t(r->gateway, 0, &gc); #endif +#endif is_local_route = local_route(r->network, r->netmask, r->gateway, rgi); if (is_local_route == LR_ERROR) @@ -2166,27 +2189,20 @@ delete_route(struct route_ipv4 *r, } #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s route del %s/%d", - iproute_path, - network, - netmask_to_netbits2(r->netmask)); -#else - argv_printf(&argv, "%s del -net %s netmask %s", - ROUTE_PATH, - network, - netmask); -#endif /*ENABLE_IPROUTE*/ + metric = -1; if (r->flags & RT_METRIC_DEFINED) { - argv_printf_cat(&argv, "metric %d", r->metric); + metric = r->metric; } - argv_msg(D_ROUTE, &argv); - openvpn_execve_check(&argv, es, 0, "ERROR: Linux route delete command failed"); + if (net_route_v4_del(ctx, &r->network, netmask_to_netbits2(r->netmask), + &r->gateway, NULL, 0, metric) < 0) + { + msg(M_WARN, "ERROR: Linux route delete command failed"); + } #elif defined (_WIN32) - argv_printf(&argv, "%s%sc DELETE %s MASK %s %s", + argv_printf(&argv, "%s%s DELETE %s MASK %s %s", get_win_sys_path(), WIN_ROUTE_PATH_SUFFIX, network, @@ -2314,17 +2330,25 @@ delete_route(struct route_ipv4 *r, done: r->flags &= ~RT_ADDED; - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route cleanup */ + net_ctx_reset(ctx); } void -delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); const char *network; +#if !defined(TARGET_LINUX) const char *gateway; +#else + int metric; +#endif const char *device = tt->actual_name; bool gateway_needed = false; @@ -2344,7 +2368,9 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned gc_init(&gc); network = print_in6_addr( r6->network, 0, &gc); +#if !defined(TARGET_LINUX) gateway = print_in6_addr( r6->gateway, 0, &gc); +#endif #if defined(TARGET_DARWIN) \ || defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) \ @@ -2375,35 +2401,19 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned gateway_needed = true; } - #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 route del %s/%d dev %s", - iproute_path, - network, - r6->netbits, - device); - if (gateway_needed) - { - argv_printf_cat(&argv, "via %s", gateway); - } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s -A inet6 del %s/%d dev %s", - ROUTE_PATH, - network, - r6->netbits, - device); - if (gateway_needed) + metric = -1; + if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0)) { - argv_printf_cat(&argv, "gw %s", gateway); + metric = r6->metric; } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + + if (net_route_v6_del(ctx, &r6->network, r6->netbits, + gateway_needed ? &r6->gateway : NULL, device, 0, + metric) < 0) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ERROR: Linux route v6 delete command failed"); } -#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) @@ -2426,7 +2436,7 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned 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", + argv_printf(&argv, "%s%s interface ipv6 delete route %s/%d %s", get_win_sys_path(), NETSH_PATH_SUFFIX, network, @@ -2548,8 +2558,10 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned 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 /* if defined(TARGET_LINUX) */ - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route cleanup */ + net_ctx_reset(ctx); } /* @@ -2642,7 +2654,11 @@ test_routes(const struct route_list *rl, const struct tuntap *tt) ret = true; adapter_up = true; - if (rl) + /* we do this test only if we have IPv4 routes to install, and if + * the tun/tap interface has seen IPv4 ifconfig - because if we + * have no IPv4, the check will always fail, failing tun init + */ + if (rl && tt->did_ifconfig_setup) { struct route_ipv4 *r; for (r = rl->routes, len = 0; r; r = r->next, ++len) @@ -2711,7 +2727,7 @@ get_default_gateway_row(const MIB_IPFORWARDTABLE *routes) } void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); @@ -2798,7 +2814,7 @@ windows_route_find_if_index(const struct route_ipv4 *r, const struct tuntap *tt) */ void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); MIB_IPFORWARD_ROW2 BestRoute; @@ -2817,7 +2833,7 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, DestinationAddress.Ipv6.sin6_addr = *dest; } - status = GetBestInterfaceEx( &DestinationAddress, &BestIfIndex ); + status = GetBestInterfaceEx( (struct sockaddr *)&DestinationAddress, &BestIfIndex ); if (status != NO_ERROR) { @@ -2987,16 +3003,12 @@ del_route_ipapi(const struct route_ipv4 *r, const struct tuntap *tt) 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)) + if (!send_msg_iservice(pipe, rt, size, &ack, "ROUTE")) { - msg(M_WARN, "ROUTE: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -3074,7 +3086,7 @@ do_route_ipv6_service(const bool add, const struct route_ipv6 *r, const struct t * (only do this for routes actually using the tun/tap device) */ if (tt->type == DEV_TYPE_TUN - && msg.iface.index == tt->adapter_index ) + && msg.iface.index == tt->adapter_index) { inet_pton(AF_INET6, "fe80::8", &msg.gateway.ipv6); } @@ -3160,90 +3172,68 @@ show_routes(int msglev) gc_free(&gc); } -#elif defined(TARGET_LINUX) || defined(TARGET_ANDROID) +#elif defined(TARGET_ANDROID) void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) +{ + /* 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. + */ + CLEAR(*rgi); + + rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w'; + rgi->flags = RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + strcpy(rgi->iface, "android-gw"); + + /* Skip scanning/fetching interface from loopback interface we do + * normally on Linux. + * It always fails and "ioctl(SIOCGIFCONF) failed" confuses users + */ + +} + +void +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) +{ + /* Same for ipv6 */ + + CLEAR(*rgi6); + + /* Use a fake link-local address */ + ASSERT(inet_pton(AF_INET6, "fe80::ad", &rgi6->addrs->addr_ipv6) == 1); + rgi6->addrs->netbits_ipv6 = 64; + rgi6->flags = RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + strcpy(rgi6->iface, "android-gw"); +} + +#elif defined(TARGET_LINUX) + +void +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); int sd = -1; - char best_name[16]; - best_name[0] = 0; + char best_name[IFNAMSIZ]; CLEAR(*rgi); + CLEAR(best_name); -#ifndef TARGET_ANDROID /* get default gateway IP addr */ + if (net_route_v4_best_gw(ctx, NULL, &rgi->gateway.addr, best_name) == 0) { - FILE *fp = fopen("/proc/net/route", "r"); - if (fp) + rgi->flags |= RGI_ADDR_DEFINED; + if (!rgi->gateway.addr && best_name[0]) { - char line[256]; - int count = 0; - unsigned int lowest_metric = UINT_MAX; - in_addr_t best_gw = 0; - bool found = false; - while (fgets(line, sizeof(line), fp) != NULL) - { - if (count) - { - unsigned int net_x = 0; - unsigned int mask_x = 0; - unsigned int gw_x = 0; - unsigned int metric = 0; - unsigned int flags = 0; - char name[16]; - name[0] = 0; - const int np = sscanf(line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x", - name, - &net_x, - &gw_x, - &flags, - &metric, - &mask_x); - if (np == 6 && (flags & IFF_UP)) - { - const in_addr_t net = ntohl(net_x); - const in_addr_t mask = ntohl(mask_x); - const in_addr_t gw = ntohl(gw_x); - - if (!net && !mask && metric < lowest_metric) - { - found = true; - best_gw = gw; - strcpy(best_name, name); - lowest_metric = metric; - } - } - } - ++count; - } - fclose(fp); - - if (found) - { - rgi->gateway.addr = best_gw; - rgi->flags |= RGI_ADDR_DEFINED; - if (!rgi->gateway.addr && best_name[0]) - { - rgi->flags |= RGI_ON_LINK; - } - } + rgi->flags |= RGI_ON_LINK; } } -#else /* ifndef TARGET_ANDROID */ - /* 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 /* ifndef TARGET_ANDROID */ /* scan adapter list */ if (rgi->flags & RGI_ADDR_DEFINED) @@ -3292,7 +3282,7 @@ get_default_gateway(struct route_gateway_info *rgi) if (rgi->flags & RGI_ON_LINK) { /* check that interface name of current interface - * matches interface name of best default route */ + * matches interface name of best default route */ if (strcmp(ifreq.ifr_name, best_name)) { continue; @@ -3369,152 +3359,29 @@ struct rtreq { void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { - int nls = -1; - struct rtreq rtreq; - struct rtattr *rta; - - char rtbuf[2000]; - ssize_t ssize; + int flags; 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; - } - - ssize = recv(nls, rtbuf, sizeof(rtbuf), MSG_TRUNC); - - if (ssize < 0) - { - msg(M_WARN|M_ERRNO, "GDG6: recv() failed" ); goto done; - } - - if (ssize > sizeof(rtbuf)) + if (net_route_v6_best_gw(ctx, dest, &rgi6->gateway.addr_ipv6, + rgi6->iface) == 0) { - msg(M_WARN, "get_default_gateway_ipv6: returned message too big for buffer (%d>%d)", (int)ssize, (int)sizeof(rtbuf) ); - goto done; - } - - struct nlmsghdr *nh; - - for (nh = (struct nlmsghdr *)rtbuf; - NLMSG_OK(nh, ssize); - nh = NLMSG_NEXT(nh, ssize)) - { - struct rtmsg *rtm; - int attrlen; - - if (nh->nlmsg_type == NLMSG_DONE) - { - break; - } - - if (nh->nlmsg_type == NLMSG_ERROR) - { - struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh); - - /* since linux-4.11 -ENETUNREACH is returned when no route can be - * found. Don't print any error message in this case */ - if (ne->error != -ENETUNREACH) - { - msg(M_WARN, "GDG6: NLMSG_ERROR: error %s\n", - strerror(-ne->error)); - } - break; - } - - if (nh->nlmsg_type != RTM_NEWROUTE) + if (!IN6_IS_ADDR_UNSPECIFIED(rgi6->gateway.addr_ipv6.s6_addr)) { - /* shouldn't happen */ - msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type ); - continue; + rgi6->flags |= RGI_ADDR_DEFINED; } - rtm = (struct rtmsg *)NLMSG_DATA(nh); - attrlen = RTM_PAYLOAD(nh); - - /* 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) + if (strlen(rgi6->iface) > 0) { - continue; - } /* we're not interested */ - - 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; - } + rgi6->flags |= RGI_IFACE_DEFINED; } } /* 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) + flags = rgi6->flags & (RGI_IFACE_DEFINED | RGI_ADDR_DEFINED); + if (flags == RGI_IFACE_DEFINED) { rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK); if (dest) @@ -3522,12 +3389,6 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, rgi6->gateway.addr_ipv6 = *dest; } } - -done: - if (nls >= 0) - { - close(nls); - } } #elif defined(TARGET_DARWIN) || defined(TARGET_SOLARIS) \ @@ -3575,7 +3436,7 @@ struct rtmsg { #else /* if defined(TARGET_SOLARIS) */ #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ - l = ROUNDUP( ((struct sockaddr *)&(u))->sa_len); memmove(cp, &(u), l); cp += l; \ + l = ((struct sockaddr *)&(u))->sa_len; memmove(cp, &(u), l); cp += ROUNDUP(l); \ } #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) @@ -3584,7 +3445,7 @@ struct rtmsg { #define max(a,b) ((a) > (b) ? (a) : (b)) void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct rtmsg m_rtmsg; @@ -3804,7 +3665,7 @@ done: void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { struct rtmsg m_rtmsg; @@ -3984,13 +3845,13 @@ done: * may be disabled by missing items. */ void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { CLEAR(*rgi); } void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system"); CLEAR(*rgi6); diff --git a/src/openvpn/route.h b/src/openvpn/route.h index 6942022..7dd9609 100644 --- a/src/openvpn/route.h +++ b/src/openvpn/route.h @@ -31,6 +31,7 @@ #include "basic.h" #include "tun.h" #include "misc.h" +#include "networking.h" #ifdef _WIN32 /* @@ -183,7 +184,11 @@ struct route_ipv6_gateway_info { #ifdef _WIN32 DWORD adapter_index; /* interface or ~0 if undefined */ #else - char iface[16]; /* interface name (null terminated), may be empty */ + /* non linux platform don't have this constant defined */ +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + char iface[IFNAMSIZ]; /* interface name (null terminated), may be empty */ #endif /* gateway interface hardware address */ @@ -256,15 +261,16 @@ void copy_route_ipv6_option_list(struct route_ipv6_option_list *dest, void route_ipv6_clear_host_bits( struct route_ipv6 *r6 ); -void add_route_ipv6(struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); +void add_route_ipv6(struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es, openvpn_net_ctx_t *ctx); -void delete_route_ipv6(const 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, openvpn_net_ctx_t *ctx); void add_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es); + const struct env_set *es, + openvpn_net_ctx_t *ctx); void add_route_to_option_list(struct route_option_list *l, const char *network, @@ -282,14 +288,16 @@ bool init_route_list(struct route_list *rl, const char *remote_endpoint, int default_metric, in_addr_t remote_host, - struct env_set *es); + struct env_set *es, + openvpn_net_ctx_t *ctx); 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); + struct env_set *es, + openvpn_net_ctx_t *ctx); void route_list_add_vpn_gateway(struct route_list *rl, struct env_set *es, @@ -299,26 +307,28 @@ void add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, - const struct env_set *es); + const struct env_set *es, + openvpn_net_ctx_t *ctx); void delete_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, - const struct env_set *es); + const struct env_set *es, + openvpn_net_ctx_t *ctx); void setenv_routes(struct env_set *es, const struct route_list *rl); 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 get_default_gateway(struct route_gateway_info *rgi, + openvpn_net_ctx_t *ctx); void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi, - const struct in6_addr *dest); + const struct in6_addr *dest, + openvpn_net_ctx_t *ctx); void print_default_gateway(const int msglevel, const struct route_gateway_info *rgi, diff --git a/src/openvpn/run_command.c b/src/openvpn/run_command.c new file mode 100644 index 0000000..4c4adf9 --- /dev/null +++ b/src/openvpn/run_command.c @@ -0,0 +1,288 @@ +/* + * 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-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "buffer.h" +#include "error.h" +#include "platform.h" +#include "win32.h" + +#include "memdbg.h" + +#include "run_command.h" + +/* contains an SSEC_x value defined in platform.h */ +static int script_security_level = SSEC_BUILT_IN; /* GLOBAL */ + +int +script_security(void) +{ + return script_security_level; +} + +void +script_security_set(int level) +{ + script_security_level = level; +} + +/* + * Generate an error message based on the status code returned by openvpn_execve(). + */ +static const char * +system_error_message(int stat, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc(256, gc); + + switch (stat) + { + case OPENVPN_EXECVE_NOT_ALLOWED: + buf_printf(&out, "disallowed by script-security setting"); + break; + +#ifdef _WIN32 + case OPENVPN_EXECVE_ERROR: + buf_printf(&out, "external program did not execute -- "); + /* fall through */ + + default: + buf_printf(&out, "returned error code %d", stat); + break; +#else /* ifdef _WIN32 */ + + case OPENVPN_EXECVE_ERROR: + buf_printf(&out, "external program fork failed"); + break; + + default: + if (!WIFEXITED(stat)) + { + buf_printf(&out, "external program did not exit normally"); + } + else + { + const int cmd_ret = WEXITSTATUS(stat); + if (!cmd_ret) + { + buf_printf(&out, "external program exited normally"); + } + else if (cmd_ret == OPENVPN_EXECVE_FAILURE) + { + buf_printf(&out, "could not execute external program"); + } + else + { + buf_printf(&out, "external program exited with error status: %d", cmd_ret); + } + } + break; +#endif /* ifdef _WIN32 */ + } + return (const char *)out.data; +} + +bool +openvpn_execve_allowed(const unsigned int flags) +{ + if (flags & S_SCRIPT) + { + return script_security() >= SSEC_SCRIPTS; + } + else + { + return script_security() >= SSEC_BUILT_IN; + } +} + + +#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 + * associated with formatting and parsing a command line. + * Returns the exit status of child, OPENVPN_EXECVE_NOT_ALLOWED if openvpn_execve_allowed() + * returns false, or OPENVPN_EXECVE_ERROR on other errors. + */ +int +openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags) +{ + struct gc_arena gc = gc_new(); + int ret = OPENVPN_EXECVE_ERROR; + static bool warn_shown = false; + + if (a && a->argv[0]) + { +#if defined(ENABLE_FEATURE_EXECVE) + if (openvpn_execve_allowed(flags)) + { + const char *cmd = a->argv[0]; + char *const *argv = a->argv; + char *const *envp = (char *const *)make_env_array(es, true, &gc); + pid_t pid; + + pid = fork(); + if (pid == (pid_t)0) /* child side */ + { + execve(cmd, argv, envp); + exit(OPENVPN_EXECVE_FAILURE); + } + else if (pid < (pid_t)0) /* fork failed */ + { + msg(M_ERR, "openvpn_execve: unable to fork"); + } + else /* parent side */ + { + if (waitpid(pid, &ret, 0) != pid) + { + ret = OPENVPN_EXECVE_ERROR; + } + } + } + else + { + ret = OPENVPN_EXECVE_NOT_ALLOWED; + if (!warn_shown && (script_security() < SSEC_SCRIPTS)) + { + msg(M_WARN, SCRIPT_SECURITY_WARNING); + warn_shown = true; + } + } +#else /* if defined(ENABLE_FEATURE_EXECVE) */ + msg(M_WARN, "openvpn_execve: execve function not available"); +#endif /* if defined(ENABLE_FEATURE_EXECVE) */ + } + else + { + msg(M_FATAL, "openvpn_execve: called with empty argv"); + } + + gc_free(&gc); + return ret; +} +#endif /* ifndef _WIN32 */ + +/* + * Wrapper around openvpn_execve + */ +bool +openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message) +{ + struct gc_arena gc = gc_new(); + const int stat = openvpn_execve(a, es, flags); + int ret = false; + + if (platform_system_ok(stat)) + { + ret = true; + } + else + { + if (error_message) + { + msg(((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s", + error_message, + system_error_message(stat, &gc)); + } + } + gc_free(&gc); + return ret; +} + +/* + * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but + * in a safer way that doesn't require the invocation of a shell or the risks + * associated with formatting and parsing a command line. + */ +int +openvpn_popen(const struct argv *a, const struct env_set *es) +{ + struct gc_arena gc = gc_new(); + int ret = -1; + static bool warn_shown = false; + + if (a && a->argv[0]) + { +#if defined(ENABLE_FEATURE_EXECVE) + if (script_security() >= SSEC_BUILT_IN) + { + const char *cmd = a->argv[0]; + char *const *argv = a->argv; + char *const *envp = (char *const *)make_env_array(es, true, &gc); + pid_t pid; + int pipe_stdout[2]; + + if (pipe(pipe_stdout) == 0) + { + pid = fork(); + if (pid == (pid_t)0) /* child side */ + { + close(pipe_stdout[0]); /* Close read end */ + dup2(pipe_stdout[1],1); + execve(cmd, argv, envp); + exit(OPENVPN_EXECVE_FAILURE); + } + 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 for %s", cmd); + ret = -1; + } + } + else if (!warn_shown && (script_security() < SSEC_SCRIPTS)) + { + msg(M_WARN, SCRIPT_SECURITY_WARNING); + warn_shown = true; + } +#else /* if defined(ENABLE_FEATURE_EXECVE) */ + msg(M_WARN, "openvpn_popen: execve function not available"); +#endif /* if defined(ENABLE_FEATURE_EXECVE) */ + } + else + { + msg(M_FATAL, "openvpn_popen: called with empty argv"); + } + + gc_free(&gc); + return ret; +} diff --git a/src/openvpn/run_command.h b/src/openvpn/run_command.h new file mode 100644 index 0000000..7ccb13c --- /dev/null +++ b/src/openvpn/run_command.h @@ -0,0 +1,67 @@ +/* + * 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-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RUN_COMMAND_H +#define RUN_COMMAND_H + +#include "basic.h" +#include "env_set.h" + +/* Script security */ +#define SSEC_NONE 0 /* strictly no calling of external programs */ +#define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/ +#define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */ +#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */ + +#define OPENVPN_EXECVE_ERROR -1 /* generic error while forking to run an external program */ +#define OPENVPN_EXECVE_NOT_ALLOWED -2 /* external program not run due to script security */ +#define OPENVPN_EXECVE_FAILURE 127 /* exit code passed back from child when execve fails */ + +int script_security(void); + +void script_security_set(int level); + +/* openvpn_execve flags */ +#define S_SCRIPT (1<<0) +#define S_FATAL (1<<1) + +/* wrapper around the execve() call */ +int openvpn_popen(const struct argv *a, const struct env_set *es); + +bool openvpn_execve_allowed(const unsigned int flags); + +bool openvpn_execve_check(const struct argv *a, const struct env_set *es, + const unsigned int flags, const char *error_message); + +static inline bool +openvpn_run_script(const struct argv *a, const struct env_set *es, + const unsigned int flags, const char *hook) +{ + char msg[256]; + + openvpn_snprintf(msg, sizeof(msg), + "WARNING: Failed running command (%s)", hook); + return openvpn_execve_check(a, es, flags | S_SCRIPT, msg); +} + +#endif /* ifndef RUN_COMMAND_H */ diff --git a/src/openvpn/schedule.c b/src/openvpn/schedule.c index 76cf7c3..13be323 100644 --- a/src/openvpn/schedule.c +++ b/src/openvpn/schedule.c @@ -29,8 +29,6 @@ #include "syshead.h" -#if P2MP_SERVER - #include "buffer.h" #include "misc.h" #include "crypto.h" @@ -723,4 +721,3 @@ schedule_test(void) } #endif /* ifdef SCHEDULE_TEST */ -#endif /* if P2MP_SERVER */ diff --git a/src/openvpn/schedule.h b/src/openvpn/schedule.h index 74d37fb..8c476fd 100644 --- a/src/openvpn/schedule.h +++ b/src/openvpn/schedule.h @@ -35,8 +35,6 @@ * a ping or scheduling a TLS renegotiation. */ -#if P2MP_SERVER - /* define to enable a special test mode */ /*#define SCHEDULE_TEST*/ @@ -136,5 +134,4 @@ schedule_get_earliest_wakeup(struct schedule *s, return ret; } -#endif /* if P2MP_SERVER */ #endif /* ifndef SCHEDULE_H */ diff --git a/src/openvpn/session_id.c b/src/openvpn/session_id.c index 2b50feb..d57609c 100644 --- a/src/openvpn/session_id.c +++ b/src/openvpn/session_id.c @@ -38,8 +38,6 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - #include "error.h" #include "common.h" #include "crypto.h" @@ -60,10 +58,3 @@ session_id_print(const struct session_id *sid, struct gc_arena *gc) { return format_hex(sid->id, SID_SIZE, 0, gc); } - -#else /* ifdef ENABLE_CRYPTO */ -static void -dummy(void) -{ -} -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/session_id.h b/src/openvpn/session_id.h index 5e950a6..c0a128d 100644 --- a/src/openvpn/session_id.h +++ b/src/openvpn/session_id.h @@ -29,8 +29,6 @@ * negotiated). */ -#ifdef ENABLE_CRYPTO - #ifndef SESSION_ID_H #define SESSION_ID_H @@ -82,4 +80,3 @@ 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 */ diff --git a/src/openvpn/shaper.c b/src/openvpn/shaper.c index 00eb2e9..6257984 100644 --- a/src/openvpn/shaper.c +++ b/src/openvpn/shaper.c @@ -76,8 +76,8 @@ shaper_soonest_event(struct timeval *tv, int delay) } } #ifdef SHAPER_DEBUG - dmsg(D_SHAPER_DEBUG, "SHAPER shaper_soonest_event sec=%d usec=%d ret=%d", - (int)tv->tv_sec, (int)tv->tv_usec, (int)ret); + dmsg(D_SHAPER_DEBUG, "SHAPER shaper_soonest_event sec=%" PRIi64 " usec=%ld ret=%d", + (int64_t)tv->tv_sec, (long)tv->tv_usec, (int)ret); #endif return ret; } diff --git a/src/openvpn/shaper.h b/src/openvpn/shaper.h index 0496c71..bcdb5e3 100644 --- a/src/openvpn/shaper.h +++ b/src/openvpn/shaper.h @@ -147,11 +147,11 @@ shaper_wrote_bytes(struct shaper *s, int nbytes) tv_add(&s->wakeup, &tv); #ifdef SHAPER_DEBUG - dmsg(D_SHAPER_DEBUG, "SHAPER shaper_wrote_bytes bytes=%d delay=%d sec=%d usec=%d", + dmsg(D_SHAPER_DEBUG, "SHAPER shaper_wrote_bytes bytes=%d delay=%ld sec=%" PRIi64 " usec=%ld", nbytes, - (int)tv.tv_usec, - (int)s->wakeup.tv_sec, - (int)s->wakeup.tv_usec); + (long)tv.tv_usec, + (int64_t)s->wakeup.tv_sec, + (long)s->wakeup.tv_usec); #endif } } diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c index d7f2abb..24a2878 100644 --- a/src/openvpn/sig.c +++ b/src/openvpn/sig.c @@ -317,8 +317,11 @@ print_status(const struct context *c, struct status_output *so) #ifdef _WIN32 if (tuntap_defined(c->c1.tuntap)) { - status_printf(so, "TAP-WIN32 driver status,\"%s\"", - tap_win_getinfo(c->c1.tuntap, &gc)); + const char *extended_msg = tap_win_getinfo(c->c1.tuntap, &gc); + if (extended_msg) + { + status_printf(so, "TAP-WIN32 driver status,\"%s\"", extended_msg); + } } #endif @@ -327,7 +330,6 @@ print_status(const struct context *c, struct status_output *so) gc_free(&gc); } -#ifdef ENABLE_OCC /* * Handle the triggering and time-wait of explicit * exit notification. @@ -364,7 +366,6 @@ process_explicit_exit_notification_timer_wakeup(struct context *c) } } } -#endif /* ifdef ENABLE_OCC */ /* * Process signals @@ -392,14 +393,12 @@ static bool process_sigterm(struct context *c) { bool ret = true; -#ifdef ENABLE_OCC if (c->options.ce.explicit_exit_notification && !c->c2.explicit_exit_notification_time_wait) { process_explicit_exit_notification_init(c); ret = false; } -#endif return ret; } @@ -412,7 +411,6 @@ 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) ) { @@ -431,7 +429,6 @@ ignore_restart_signals(struct context *c) ret = false; } } -#endif return ret; } diff --git a/src/openvpn/sig.h b/src/openvpn/sig.h index 887d833..59f30fd 100644 --- a/src/openvpn/sig.h +++ b/src/openvpn/sig.h @@ -81,11 +81,8 @@ bool process_signal(struct context *c); void register_signal(struct context *c, int sig, const char *text); -#ifdef ENABLE_OCC void process_explicit_exit_notification_timer_wakeup(struct context *c); -#endif - #ifdef _WIN32 static inline void diff --git a/src/openvpn/sitnl.h b/src/openvpn/sitnl.h new file mode 100644 index 0000000..937522f --- /dev/null +++ b/src/openvpn/sitnl.h @@ -0,0 +1,217 @@ +/* + * Simplified Interface To NetLink + * + * Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc> + * + * 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 SITNL_H_ +#define SITNL_H_ + +#ifdef TARGET_LINUX + +#include <stdbool.h> +#include <netinet/in.h> + +/** + * Bring interface up or down. + * + * @param iface the interface to modify + * @param up true if the interface has to be brought up, false otherwise + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_iface_up(const char *iface, bool up); + +/** + * Set the MTU for an interface + * + * @param iface the interface to modify + * @param mtru the new MTU + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_iface_mtu_set(const char *iface, uint32_t mtu); + +/** + * Add an IPv4 address to an interface + * + * @param iface the interface where the address has to be added + * @param addr the address to add + * @param prefixlen the prefix length of the network associated with the address + * @param broadcast the broadcast address to configure on the interface + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_addr_v4_add(const char *iface, const in_addr_t *addr, int prefixlen, + const in_addr_t *broadcast); + +/** + * Add an IPv6 address to an interface + * + * @param iface the interface where the address has to be added + * @param addr the address to add + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ + +int sitnl_addr_v6_add(const char *iface, const struct in6_addr *addr, + int prefixlen); + +/** + * Remove an IPv4 from an interface + * + * @param iface the interface to remove the address from + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_addr_v4_del(const char *iface, const in_addr_t *addr, int prefixlen); + +/** + * Remove an IPv6 from an interface + * + * @param iface the interface to remove the address from + * @param prefixlen the prefix length of the network associated with the address + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_addr_v6_del(const char *iface, const struct in6_addr *addr, + int prefixlen); + +/** + * Add a point-to-point IPv4 address to an interface + * + * @param iface the interface where the address has to be added + * @param local the address to add + * @param remote the associated p-t-p remote address + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_addr_ptp_v4_add(const char *iface, const in_addr_t *local, + const in_addr_t *remote); + +/** + * Remove a point-to-point IPv4 address from an interface + * + * @param iface the interface to remove the address from + * @param local the address to remove + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_addr_ptp_v4_del(const char *iface, const in_addr_t *local); + + +/** + * Add a route for an IPv4 address/network + * + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v4_add(const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric); + +/** + * Add a route for an IPv6 address/network + * + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v6_add(const struct in6_addr *dst, int prefixlen, + const struct in6_addr *gw, const char *iface, + uint32_t table, int metric); + +/** + * Delete a route for an IPv4 address/network + * + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v4_del(const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric); + +/** + * Delete a route for an IPv4 address/network + * + * @param dst the destination of the route + * @param prefixlen the length of the prefix of the destination + * @param gw the gateway for this route + * @param iface the interface for this route (can be NULL) + * @param table the table to add this route to (if 0, will be added to the + * main table) + * @param metric the metric associated with the route + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v6_del(const struct in6_addr *dst, int prefixlen, + const struct in6_addr *gw, const char *iface, + uint32_t table, int metric); + +/** + * Retrieve the gateway and outgoing interface for the specified IPv4 + * address/network + * + * @param dst The destination to lookup + * @param prefixlen The length of the prefix of the destination + * @param best_gw Location where the retrieved GW has to be stored + * @param best_iface Location where the retrieved interface has to be stored + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v4_best_gw(const in_addr_t *dst, int prefixlen, + in_addr_t *best_gw, char *best_iface); + +/** + * Retrieve the gateway and outgoing interface for the specified IPv6 + * address/network + * + * @param dst The destination to lookup + * @param prefixlen The length of the prefix of the destination + * @param best_gw Location where the retrieved GW has to be stored + * @param best_iface Location where the retrieved interface has to be stored + * + * @return 0 on success, a negative error code otherwise + */ +int sitnl_route_v6_best_gw(const struct in6_addr *dst, int prefixlen, + struct in6_addr *best_gw, char *best_iface); + +#endif /* TARGET_LINUX */ + +#endif /* SITNL_H_ */ diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 9131ec2..c486327 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -35,6 +35,7 @@ #include "gremlin.h" #include "plugin.h" #include "ps.h" +#include "run_command.h" #include "manage.h" #include "misc.h" #include "manage.h" @@ -99,10 +100,12 @@ get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname, bits = 0; max_bits = sizeof(in_addr_t) * 8; break; + case AF_INET6: bits = 64; max_bits = sizeof(struct in6_addr) * 8; break; + default: msg(M_WARN, "Unsupported AF family passed to getaddrinfo for %s (%d)", @@ -124,7 +127,7 @@ get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname, } /* check if this hostname has a /bits suffix */ - sep = strchr(var_host , '/'); + sep = strchr(var_host, '/'); if (sep) { bits = strtoul(sep + 1, &endp, 10); @@ -155,10 +158,12 @@ get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname, *ip4 = ntohl(*ip4); } break; + case AF_INET6: ip6 = network; *ip6 = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; break; + default: /* can't get here because 'af' was previously checked */ msg(M_WARN, @@ -987,7 +992,7 @@ link_socket_update_buffer_sizes(struct link_socket *ls, int rcvbuf, int sndbuf) } /* - * SOCKET INITALIZATION CODE. + * SOCKET INITIALIZATION CODE. * Create a TCP/UDP socket */ @@ -1133,6 +1138,18 @@ create_socket(struct link_socket *sock, struct addrinfo *addr) /* set socket to --mark packets with given value */ socket_set_mark(sock->sd, sock->mark); +#if defined(TARGET_LINUX) + if (sock->bind_dev) + { + msg (M_INFO, "Using bind-dev %s", sock->bind_dev); + if (setsockopt (sock->sd, SOL_SOCKET, SO_BINDTODEVICE, sock->bind_dev, strlen (sock->bind_dev) + 1) != 0) + { + msg(M_WARN|M_ERRNO, "WARN: setsockopt SO_BINDTODEVICE=%s failed", sock->bind_dev); + } + + } +#endif + bind_local(sock, addr->ai_family); } @@ -1607,6 +1624,22 @@ done: gc_free(&gc); } +/* + * Stream buffer handling prototypes -- stream_buf is a helper class + * to assist in the packetization of stream transport protocols + * such as TCP. + */ + +static void +stream_buf_init(struct stream_buf *sb, struct buffer *buf, + const unsigned int sockflags, const int proto); + +static void +stream_buf_close(struct stream_buf *sb); + +static bool +stream_buf_added(struct stream_buf *sb, int length_added); + /* For stream protocols, allocate a buffer to build up packet. * Called after frame has been finalized. */ @@ -1859,6 +1892,7 @@ link_socket_init_phase1(struct link_socket *sock, int rcvbuf, int sndbuf, int mark, + const char *bind_dev, struct event_timeout *server_poll_timeout, unsigned int sockflags) { @@ -1885,6 +1919,7 @@ link_socket_init_phase1(struct link_socket *sock, sock->sockflags = sockflags; sock->mark = mark; + sock->bind_dev = bind_dev; sock->info.proto = proto; sock->info.af = af; @@ -2415,8 +2450,7 @@ ipchange_fmt(const bool include_cmd, struct argv *argv, const struct link_socket } void -link_socket_connection_initiated(const struct buffer *buf, - struct link_socket_info *info, +link_socket_connection_initiated(struct link_socket_info *info, const struct link_socket_actual *act, const char *common_name, struct env_set *es) @@ -2450,7 +2484,7 @@ link_socket_connection_initiated(const struct buffer *buf, { msg(M_WARN, "WARNING: ipchange plugin call failed"); } - argv_reset(&argv); + argv_free(&argv); } /* Process --ipchange option */ @@ -2460,7 +2494,7 @@ link_socket_connection_initiated(const struct buffer *buf, setenv_str(es, "script_type", "ipchange"); ipchange_fmt(true, &argv, info, &gc); openvpn_run_script(&argv, es, 0, "--ipchange"); - argv_reset(&argv); + argv_free(&argv); } gc_free(&gc); @@ -2514,7 +2548,7 @@ link_socket_current_remote(const struct link_socket_info *info) * by now just ignore it * * For --remote entries with multiple addresses this - * only return the actual endpoint we have sucessfully connected to + * only return the actual endpoint we have successfully connected to */ if (lsa->actual.dest.addr.sa.sa_family != AF_INET) { @@ -2545,7 +2579,7 @@ link_socket_current_remote_ipv6(const struct link_socket_info *info) * 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 + * only return the actual endpoint we have successfully connected to */ if (lsa->actual.dest.addr.sa.sa_family != AF_INET6) { @@ -2616,7 +2650,7 @@ stream_buf_reset(struct stream_buf *sb) sb->len = -1; } -void +static void stream_buf_init(struct stream_buf *sb, struct buffer *buf, const unsigned int sockflags, @@ -2690,7 +2724,7 @@ stream_buf_read_setup_dowork(struct link_socket *sock) return !sock->stream_buf.residual_fully_formed; } -bool +static bool stream_buf_added(struct stream_buf *sb, int length_added) { @@ -2757,7 +2791,7 @@ stream_buf_added(struct stream_buf *sb, } } -void +static void stream_buf_close(struct stream_buf *sb) { free_buf(&sb->residual); @@ -3258,7 +3292,7 @@ addr_family_name(int af) * * IPv6 and IPv4 protocols are comptabile but OpenVPN * has always sent UDPv4, TCPv4 over the wire. Keep these - * strings for backward compatbility + * strings for backward compatibility */ const char * proto_remote(int proto, bool remote) @@ -3343,7 +3377,7 @@ link_socket_read_tcp(struct link_socket *sock, #if ENABLE_IP_PKTINFO -/* make the buffer large enough to handle ancilliary socket data for +/* make the buffer large enough to handle ancillary socket data for * both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292) */ #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) @@ -3858,7 +3892,7 @@ socket_finalize(SOCKET s, if (ret >= 0 && io->addr_defined) { /* TODO(jjo): streamline this mess */ - /* in this func we dont have relevant info about the PF_ of this + /* in this func we don't have relevant info about the PF_ of this * endpoint, as link_socket_actual will be zero for the 1st received packet * * Test for inets PF_ possible sizes diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index 80e8128..7aeae52 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -99,7 +99,7 @@ struct link_socket_actual #endif }; -/* IP addresses which are persistant across SIGUSR1s */ +/* IP addresses which are persistent across SIGUSR1s */ struct link_socket_addr { struct addrinfo *bind_local; @@ -138,7 +138,7 @@ struct stream_buf int len; /* -1 if not yet known */ bool error; /* if true, fatal TCP error has occurred, - * requiring that connection be restarted */ + * requiring that connection be restarted */ #if PORT_SHARE #define PS_DISABLED 0 #define PS_ENABLED 1 @@ -212,6 +212,7 @@ struct link_socket #define SF_GETADDRINFO_DGRAM (1<<4) unsigned int sockflags; int mark; + const char *bind_dev; /* for stream sockets */ struct stream_buf stream_buf; @@ -296,7 +297,8 @@ int openvpn_connect(socket_descriptor_t sd, /* * Initialize link_socket object. */ - +/* *INDENT-OFF* uncrustify misparses this function declarion because of + * embedded #if/#endif tell it to skip this section */ void link_socket_init_phase1(struct link_socket *sock, const char *local_host, @@ -325,8 +327,10 @@ link_socket_init_phase1(struct link_socket *sock, int rcvbuf, int sndbuf, int mark, + const char *bind_dev, struct event_timeout *server_poll_timeout, unsigned int sockflags); +/* Reenable uncrustify *INDENT-ON* */ void link_socket_init_phase2(struct link_socket *sock, const struct frame *frame, @@ -431,8 +435,7 @@ 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, +void link_socket_connection_initiated(struct link_socket_info *info, const struct link_socket_actual *addr, const char *common_name, struct env_set *es); @@ -980,52 +983,33 @@ link_socket_get_outgoing_addr(struct buffer *buf, } static inline void -link_socket_set_outgoing_addr(const struct buffer *buf, - struct link_socket_info *info, +link_socket_set_outgoing_addr(struct link_socket_info *info, const struct link_socket_actual *act, const char *common_name, struct env_set *es) { - if (!buf || buf->len > 0) + 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 + || (!lsa->remote_list || addrlist_match_proto(&act->dest, lsa->remote_list, info->proto)) + ) + ) { - 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 - || (!lsa->remote_list || addrlist_match_proto(&act->dest, lsa->remote_list, info->proto)) - ) - ) - { - link_socket_connection_initiated(buf, info, act, common_name, es); - } + link_socket_connection_initiated(info, act, common_name, es); } } -/* - * Stream buffer handling -- stream_buf is a helper class - * to assist in the packetization of stream transport protocols - * such as TCP. - */ - -void stream_buf_init(struct stream_buf *sb, - struct buffer *buf, - const unsigned int sockflags, - const int proto); - -void stream_buf_close(struct stream_buf *sb); - -bool stream_buf_added(struct stream_buf *sb, int length_added); +bool stream_buf_read_setup_dowork(struct link_socket *sock); static inline bool stream_buf_read_setup(struct link_socket *sock) { - bool stream_buf_read_setup_dowork(struct link_socket *sock); - if (link_socket_connection_oriented(sock)) { return stream_buf_read_setup_dowork(sock); @@ -1130,16 +1114,17 @@ link_socket_write_win32(struct link_socket *sock, #else /* ifdef _WIN32 */ +size_t link_socket_write_udp_posix_sendmsg(struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to); + + 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 - size_t link_socket_write_udp_posix_sendmsg(struct link_socket *sock, - struct buffer *buf, - struct link_socket_actual *to); - if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO) && addr_defined_ipi(to)) { diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index cf66899..f16114c 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -43,8 +43,6 @@ #include "syshead.h" #include "win32.h" -#if defined(ENABLE_CRYPTO) - #include "error.h" #include "common.h" #include "socket.h" @@ -61,23 +59,11 @@ #include "ssl.h" #include "ssl_verify.h" #include "ssl_backend.h" +#include "ssl_ncp.h" +#include "auth_token.h" #include "memdbg.h" -#ifndef ENABLE_OCC -static const char ssl_default_options_string[] = "V0 UNDEF"; -#endif - -static inline const char * -local_options_string(const struct tls_session *session) -{ -#ifdef ENABLE_OCC - return session->opt->local_options; -#else - return ssl_default_options_string; -#endif -} - #ifdef MEASURE_TLS_HANDSHAKE_STATS static int tls_handshake_success; /* GLOBAL */ @@ -296,7 +282,7 @@ tls_get_cipher_name_pair(const char *cipher_name, size_t len) static void tls_limit_reneg_bytes(const cipher_kt_t *cipher, int *reneg_bytes) { - if (cipher && (cipher_kt_block_size(cipher) < 128/8)) + if (cipher && cipher_kt_insecure(cipher)) { if (*reneg_bytes == -1) /* Not user-specified */ { @@ -402,7 +388,7 @@ static bool auth_user_pass_enabled; /* GLOBAL */ static struct user_pass auth_user_pass; /* GLOBAL */ static struct user_pass auth_token; /* GLOBAL */ -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT static char *auth_challenge; /* GLOBAL */ #endif @@ -412,10 +398,7 @@ auth_user_pass_setup(const char *auth_file, const struct static_challenge_info * auth_user_pass_enabled = true; if (!auth_user_pass.defined && !auth_token.defined) { -#if AUTO_USERID - get_user_pass_auto_userid(&auth_user_pass, auth_file); -#else -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT if (auth_challenge) /* dynamic challenge/response */ { get_user_pass_cr(&auth_user_pass, @@ -438,9 +421,8 @@ auth_user_pass_setup(const char *auth_file, const struct static_challenge_info * sci->challenge_text); } else -#endif /* ifdef ENABLE_CLIENT_CR */ +#endif /* ifdef ENABLE_MANAGEMENT */ get_user_pass(&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT); -#endif /* if AUTO_USERID */ } } @@ -469,7 +451,7 @@ ssl_set_auth_token(const char *token) * Cleans an auth token and checks if it was active */ bool -ssl_clean_auth_token (void) +ssl_clean_auth_token(void) { bool wasdefined = auth_token.defined; purge_user_pass(&auth_token, true); @@ -490,12 +472,12 @@ ssl_purge_auth(const bool auth_user_pass_only) purge_user_pass(&passbuf, true); } purge_user_pass(&auth_user_pass, true); -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT ssl_purge_auth_challenge(); #endif } -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT void ssl_purge_auth_challenge(void) @@ -561,7 +543,7 @@ tls_version_parse(const char *vstr, const char *extra) */ static void tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, - const char *crl_file_inline) + bool crl_file_inline) { /* if something goes wrong with stat(), we'll store 0 as mtime */ platform_stat_t crl_stat = {0}; @@ -633,6 +615,12 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx) tls_ctx_restrict_ciphers(new_ctx, options->cipher_list); tls_ctx_restrict_ciphers_tls13(new_ctx, options->cipher_list_tls13); + /* Set the allow groups/curves for TLS if we want to override them */ + if (options->tls_groups) + { + tls_ctx_set_tls_groups(new_ctx, options->tls_groups); + } + if (!tls_ctx_set_options(new_ctx, options->ssl_flags)) { goto err; @@ -663,42 +651,38 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx) tls_ctx_load_cryptoapi(new_ctx, options->cryptoapi_cert); } #endif -#ifdef MANAGMENT_EXTERNAL_KEY - else if ((options->management_flags & MF_EXTERNAL_KEY) - && (options->cert_file || options->management_flags & MF_EXTERNAL_CERT)) +#ifdef ENABLE_MANAGEMENT + else if (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); - } + char *cert = management_query_cert(management, + options->management_certificate); + tls_ctx_load_cert_file(new_ctx, cert, true); + free(cert); } #endif - else + else if (options->cert_file) + { + tls_ctx_load_cert_file(new_ctx, options->cert_file, options->cert_file_inline); + } + + if (options->priv_key_file) { - /* Load Certificate */ - if (options->cert_file) + if (0 != tls_ctx_load_priv_file(new_ctx, options->priv_key_file, + options->priv_key_file_inline)) { - tls_ctx_load_cert_file(new_ctx, options->cert_file, options->cert_file_inline); + goto err; } - - /* Load Private Key */ - if (options->priv_key_file) + } +#ifdef ENABLE_MANAGEMENT + else if (options->management_flags & MF_EXTERNAL_KEY) + { + if (tls_ctx_use_management_external_key(new_ctx)) { - if (0 != tls_ctx_load_priv_file(new_ctx, options->priv_key_file, options->priv_key_file_inline)) - { - goto err; - } + msg(M_WARN, "Cannot initialize mamagement-external-key"); + goto err; } } +#endif if (options->ca_file || options->ca_path) { @@ -771,9 +755,6 @@ state_name(int state) case S_ACTIVE: return "S_ACTIVE"; - case S_NORMAL_OP: - return "S_NORMAL_OP"; - case S_ERROR: return "S_ERROR"; @@ -799,6 +780,9 @@ packet_opcode_name(int op) case P_CONTROL_HARD_RESET_SERVER_V2: return "P_CONTROL_HARD_RESET_SERVER_V2"; + case P_CONTROL_HARD_RESET_CLIENT_V3: + return "P_CONTROL_HARD_RESET_CLIENT_V3"; + case P_CONTROL_SOFT_RESET_V1: return "P_CONTROL_SOFT_RESET_V1"; @@ -844,10 +828,9 @@ session_index_name(int index) static const char * print_key_id(struct tls_multi *multi, struct gc_arena *gc) { - int i; struct buffer out = alloc_buf_gc(256, gc); - for (i = 0; i < KEY_SCAN_SIZE; ++i) + for (int i = 0; i < KEY_SCAN_SIZE; ++i) { struct key_state *ks = multi->key_scan[i]; buf_printf(&out, " [key#%d state=%s id=%d sid=%s]", i, @@ -859,22 +842,12 @@ print_key_id(struct tls_multi *multi, struct gc_arena *gc) } bool -is_hard_reset(int op, int key_method) +is_hard_reset_method2(int op) { - if (!key_method || key_method == 1) + if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2 + || op == P_CONTROL_HARD_RESET_CLIENT_V3) { - if (op == P_CONTROL_HARD_RESET_CLIENT_V1 || op == P_CONTROL_HARD_RESET_SERVER_V1) - { - return true; - } - } - - if (!key_method || key_method >= 2) - { - if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2) - { - return true; - } + return true; } return false; @@ -1094,16 +1067,14 @@ tls_session_init(struct tls_multi *multi, struct tls_session *session) } /* Are we a TLS server or client? */ - ASSERT(session->opt->key_method >= 1); - if (session->opt->key_method == 1) + if (session->opt->server) { - session->initial_opcode = session->opt->server ? - P_CONTROL_HARD_RESET_SERVER_V1 : P_CONTROL_HARD_RESET_CLIENT_V1; + session->initial_opcode = P_CONTROL_HARD_RESET_SERVER_V2; } - else /* session->opt->key_method >= 2 */ + else { - session->initial_opcode = session->opt->server ? - P_CONTROL_HARD_RESET_SERVER_V2 : P_CONTROL_HARD_RESET_CLIENT_V2; + session->initial_opcode = session->opt->tls_crypt_v2 ? + P_CONTROL_HARD_RESET_CLIENT_V3 : P_CONTROL_HARD_RESET_CLIENT_V2; } /* Initialize control channel authentication parameters */ @@ -1143,16 +1114,9 @@ tls_session_init(struct tls_multi *multi, struct tls_session *session) static void tls_session_free(struct tls_session *session, bool clear) { - int i; - - 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); + tls_wrap_free(&session->tls_wrap); - for (i = 0; i < KS_SIZE; ++i) + for (size_t i = 0; i < KS_SIZE; ++i) { key_state_free(&session->key[i], false); } @@ -1234,11 +1198,10 @@ lame_duck_must_die(const struct tls_session *session, interval_t *wakeup) const struct key_state *lame = &session->key[KS_LAME_DUCK]; if (lame->state >= S_INITIAL) { - const time_t local_now = now; ASSERT(lame->must_die); /* a lame duck key must always have an expiration */ - if (local_now < lame->must_die) + if (now < lame->must_die) { - compute_earliest_wakeup(wakeup, lame->must_die - local_now); + compute_earliest_wakeup(wakeup, lame->must_die - now); return false; } else @@ -1337,11 +1300,9 @@ tls_multi_init_set_options(struct tls_multi *multi, const char *local, const char *remote) { -#ifdef ENABLE_OCC /* initialize options string */ multi->opt.local_options = local; multi->opt.remote_options = remote; -#endif } /* @@ -1350,17 +1311,11 @@ tls_multi_init_set_options(struct tls_multi *multi, void tls_multi_free(struct tls_multi *multi, bool clear) { - int i; - ASSERT(multi); -#ifdef MANAGEMENT_DEF_AUTH - man_def_auth_set_client_reason(multi, NULL); + auth_set_client_reason(multi, NULL); -#endif -#if P2MP_SERVER free(multi->peer_info); -#endif if (multi->locked_cn) { @@ -1374,15 +1329,11 @@ tls_multi_free(struct tls_multi *multi, bool clear) cert_hash_free(multi->locked_cert_hash_set); - if (multi->auth_token) - { - secure_memzero(multi->auth_token, AUTH_TOKEN_SIZE); - free(multi->auth_token); - } + wipe_auth_token(multi); free(multi->remote_ciphername); - for (i = 0; i < TM_SIZE; ++i) + for (int i = 0; i < TM_SIZE; ++i) { tls_session_free(&multi->session[i], false); } @@ -1410,11 +1361,10 @@ tls_multi_free(struct tls_multi *multi, bool clear) static bool swap_hmac(struct buffer *buf, const struct crypto_options *co, bool incoming) { - const struct key_ctx *ctx; - ASSERT(co); - ctx = (incoming ? &co->key_ctx_bi.decrypt : &co->key_ctx_bi.encrypt); + const struct key_ctx *ctx = (incoming ? &co->key_ctx_bi.decrypt : + &co->key_ctx_bi.encrypt); ASSERT(ctx->hmac); { @@ -1478,6 +1428,8 @@ write_control_auth(struct tls_session *session, ASSERT(reliable_ack_write (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack)); + msg(D_TLS_DEBUG, "%s(): %s", __func__, packet_opcode_name(opcode)); + if (session->tls_wrap.mode == TLS_WRAP_AUTH || session->tls_wrap.mode == TLS_WRAP_NONE) { @@ -1495,17 +1447,26 @@ write_control_auth(struct tls_session *session, ASSERT(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)); - if (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; - } - else + if (!tls_crypt_wrap(buf, &session->tls_wrap.work, &session->tls_wrap.opt)) { buf->len = 0; return; } + + if (opcode == P_CONTROL_HARD_RESET_CLIENT_V3) + { + if (!buf_copy(&session->tls_wrap.work, + session->tls_wrap.tls_crypt_v2_wkc)) + { + msg(D_TLS_ERRORS, "Could not append tls-crypt-v2 client key"); + buf->len = 0; + return; + } + } + + /* 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; } @@ -1516,11 +1477,22 @@ write_control_auth(struct tls_session *session, static bool read_control_auth(struct buffer *buf, struct tls_wrap_ctx *ctx, - const struct link_socket_actual *from) + const struct link_socket_actual *from, + const struct tls_options *opt) { struct gc_arena gc = gc_new(); bool ret = false; + const uint8_t opcode = *(BPTR(buf)) >> P_OPCODE_SHIFT; + if (opcode == P_CONTROL_HARD_RESET_CLIENT_V3 + && !tls_crypt_v2_extract_client_key(buf, ctx, opt)) + { + msg(D_TLS_ERRORS, + "TLS Error: can not extract tls-crypt-v2 client key from %s", + print_link_socket_actual(from, &gc)); + goto cleanup; + } + if (ctx->mode == TLS_WRAP_AUTH) { struct buffer null = clear_buf(); @@ -1560,6 +1532,18 @@ read_control_auth(struct buffer *buf, ASSERT(buf_copy(buf, &tmp)); buf_clear(&tmp); } + else if (ctx->tls_crypt_v2_server_key.cipher) + { + /* If tls-crypt-v2 is enabled, require *some* wrapping */ + msg(D_TLS_ERRORS, "TLS Error: could not determine wrapping from %s", + print_link_socket_actual(from, &gc)); + /* TODO Do we want to support using tls-crypt-v2 and no control channel + * wrapping at all simultaneously? That would allow server admins to + * upgrade clients one-by-one without running a second instance, but we + * should not enable it by default because it breaks DoS-protection. + * So, add something like --tls-crypt-v2-allow-insecure-fallback ? */ + goto cleanup; + } if (ctx->mode == TLS_WRAP_NONE || ctx->mode == TLS_WRAP_AUTH) { @@ -1632,25 +1616,21 @@ tls1_P_hash(const md_kt_t *md_kt, int olen) { struct gc_arena gc = gc_new(); - int chunk; - hmac_ctx_t *ctx; - hmac_ctx_t *ctx_tmp; uint8_t A1[MAX_HMAC_KEY_LENGTH]; - unsigned int A1_len; #ifdef ENABLE_DEBUG const int olen_orig = olen; const uint8_t *out_orig = out; #endif - ctx = hmac_ctx_new(); - ctx_tmp = hmac_ctx_new(); + hmac_ctx_t *ctx = hmac_ctx_new(); + hmac_ctx_t *ctx_tmp = hmac_ctx_new(); dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc)); dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc)); - chunk = md_kt_size(md_kt); - A1_len = md_kt_size(md_kt); + int chunk = md_kt_size(md_kt); + unsigned int A1_len = md_kt_size(md_kt); hmac_ctx_init(ctx, sec, sec_len, md_kt); hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt); @@ -1720,21 +1700,18 @@ tls1_PRF(const uint8_t *label, struct gc_arena gc = gc_new(); const md_kt_t *md5 = md_kt_get("MD5"); const md_kt_t *sha1 = md_kt_get("SHA1"); - int len,i; - const uint8_t *S1,*S2; - uint8_t *out2; - out2 = (uint8_t *) gc_malloc(olen, false, &gc); + uint8_t *out2 = (uint8_t *) gc_malloc(olen, false, &gc); - len = slen/2; - S1 = sec; - S2 = &(sec[len]); + int len = slen/2; + const uint8_t *S1 = sec; + const uint8_t *S2 = &(sec[len]); len += (slen&1); /* add for odd, make longer */ tls1_P_hash(md5,S1,len,label,label_len,out1,olen); tls1_P_hash(sha1,S2,len,label,label_len,out2,olen); - for (i = 0; i<olen; i++) + for (int i = 0; i<olen; i++) { out1[i] ^= out2[i]; } @@ -1891,40 +1868,6 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) } } -bool -tls_item_in_cipher_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; -} - -void -tls_poor_mans_ncp(struct options *o, const char *remote_ciphername) -{ - if (o->ncp_enabled && remote_ciphername - && 0 != strcmp(o->ciphername, remote_ciphername)) - { - if (tls_item_in_cipher_list(remote_ciphername, o->ncp_ciphers)) - { - o->ciphername = string_alloc(remote_ciphername, &o->gc); - msg(D_TLS_DEBUG_LOW, "Using peer cipher '%s'", o->ciphername); - } - } -} - /** * Generate data channel keys for the supplied TLS session. * @@ -1941,7 +1884,11 @@ tls_session_generate_data_channel_keys(struct tls_session *session) const struct session_id *server_sid = !session->opt->server ? &ks->session_id_remote : &session->session_id; - ASSERT(ks->authenticated); + if (ks->authenticated == KS_AUTH_FALSE) + { + msg(D_TLS_ERRORS, "TLS Error: key_state not authenticated"); + goto cleanup; + } ks->crypto_options.flags = session->opt->crypto_flags; if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, @@ -1971,13 +1918,14 @@ tls_session_update_crypto_params(struct tls_session *session, return true; } - if (!session->opt->server - && 0 != strcmp(options->ciphername, session->opt->config_ciphername) + bool cipher_allowed_as_fallback = options->enable_ncp_fallback + && streq(options->ciphername, session->opt->config_ciphername); + + if (!session->opt->server && !cipher_allowed_as_fallback && !tls_item_in_cipher_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); + msg(D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s", + options->ciphername, options->ncp_ciphers); /* undo cipher push, abort connection setup */ options->ciphername = session->opt->config_ciphername; return false; @@ -1993,6 +1941,13 @@ tls_session_update_crypto_params(struct tls_session *session, options->keysize = 0; } } + else + { + /* Very hacky workaround and quick fix for frame calculation + * different when adjusting frame size when the original and new cipher + * are identical to avoid a regression with client without NCP */ + return tls_session_generate_data_channel_keys(session); + } init_key_type(&session->opt->key_type, options->ciphername, options->authname, options->keysize, true, true); @@ -2007,7 +1962,7 @@ tls_session_update_crypto_params(struct tls_session *session, /* Update frame parameters: undo worst-case overhead, add actual overhead */ frame_remove_from_extra_frame(frame, crypto_max_overhead()); crypto_adjust_frame_parameters(frame, &session->opt->key_type, - options->use_iv, options->replay, packet_id_long_form); + 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); @@ -2024,7 +1979,7 @@ tls_session_update_crypto_params(struct tls_session *session, { frame_remove_from_extra_frame(frame_fragment, crypto_max_overhead()); crypto_adjust_frame_parameters(frame_fragment, &session->opt->key_type, - options->use_iv, options->replay, packet_id_long_form); + options->replay, packet_id_long_form); frame_set_mtu_dynamic(frame_fragment, options->ce.fragment, SET_MTU_UPPER_BOUND); frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms"); } @@ -2220,63 +2175,15 @@ read_string_alloc(struct buffer *buf) return str; } -/* - * Handle the reading and writing of key data to and from - * the TLS control channel (cleartext). - */ - -static bool -key_method_1_write(struct buffer *buf, struct tls_session *session) -{ - struct key key; - struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - - ASSERT(session->opt->key_method == 1); - ASSERT(buf_init(buf, 0)); - - generate_key_random(&key, &session->opt->key_type); - if (!check_key(&key, &session->opt->key_type)) - { - msg(D_TLS_ERRORS, "TLS Error: Bad encrypting key generated"); - return false; - } - - if (!write_key(&key, &session->opt->key_type, buf)) - { - msg(D_TLS_ERRORS, "TLS Error: write_key failed"); - return false; - } - - init_key_ctx(&ks->crypto_options.key_ctx_bi.encrypt, &key, - &session->opt->key_type, OPENVPN_OP_ENCRYPT, - "Data Channel Encrypt"); - secure_memzero(&key, sizeof(key)); - - /* send local options string */ - { - const char *local_options = local_options_string(session); - const int optlen = strlen(local_options) + 1; - if (!buf_write(buf, local_options, optlen)) - { - msg(D_TLS_ERRORS, "TLS Error: KM1 write options failed"); - return false; - } - } - - return true; -} - static bool push_peer_info(struct buffer *buf, struct tls_session *session) { struct gc_arena gc = gc_new(); bool ret = false; -#ifdef ENABLE_PUSH_PEER_INFO if (session->opt->push_peer_info_detail > 0) { struct env_set *es = session->opt->es; - struct env_item *e; struct buffer out = alloc_buf_gc(512*3, &gc); /* push version */ @@ -2302,13 +2209,30 @@ push_peer_info(struct buffer *buf, struct tls_session *session) #endif /* support for P_DATA_V2 */ - buf_printf(&out, "IV_PROTO=2\n"); + int iv_proto = IV_PROTO_DATA_V2; + + /* support for receiving push_reply before sending + * push request, also signal that the client wants + * to get push-reply messages without without requiring a round + * trip for a push request message*/ + if(session->opt->pull) + { + iv_proto |= IV_PROTO_REQUEST_PUSH; + } + + buf_printf(&out, "IV_PROTO=%d\n", iv_proto); - /* support for Negotiable Crypto Paramters */ + /* support for Negotiable Crypto Parameters */ if (session->opt->ncp_enabled && (session->opt->mode == MODE_SERVER || session->opt->pull)) { - buf_printf(&out, "IV_NCP=2\n"); + if (tls_item_in_cipher_list("AES-128-GCM", session->opt->config_ncp_ciphers) + && tls_item_in_cipher_list("AES-256-GCM", session->opt->config_ncp_ciphers)) + { + + buf_printf(&out, "IV_NCP=2\n"); + } + buf_printf(&out, "IV_CIPHERS=%s\n", session->opt->config_ncp_ciphers); } /* push compression status */ @@ -2320,7 +2244,7 @@ push_peer_info(struct buffer *buf, struct tls_session *session) { /* push mac addr */ struct route_gateway_info rgi; - get_default_gateway(&rgi); + get_default_gateway(&rgi, session->opt->net_ctx); if (rgi.flags & RGI_HWADDR_DEFINED) { buf_printf(&out, "IV_HWADDR=%s\n", format_hex_ex(rgi.hwaddr, 6, 0, 1, ":", &gc)); @@ -2332,14 +2256,16 @@ push_peer_info(struct buffer *buf, struct tls_session *session) } /* push env vars that begin with UV_, IV_PLAT_VER and IV_GUI_VER */ - for (e = es->list; e != NULL; e = e->next) + for (struct env_item *e = es->list; e != NULL; e = e->next) { if (e->string) { 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)) + || (strncmp(e->string,"IV_GUI_VER=",sizeof("IV_GUI_VER=")-1)==0) + || (strncmp(e->string,"IV_SSO=",sizeof("IV_SSO=")-1)==0) + ) && buf_safe(&out, strlen(e->string)+1)) { buf_printf(&out, "%s\n", e->string); @@ -2353,7 +2279,6 @@ push_peer_info(struct buffer *buf, struct tls_session *session) } } else -#endif /* ifdef ENABLE_PUSH_PEER_INFO */ { if (!write_empty_string(buf)) /* no peer info */ { @@ -2367,12 +2292,15 @@ error: return ret; } +/** + * Handle the writing of key data, peer-info, username/password, OCC + * to the TLS control channel (cleartext). + */ static bool key_method_2_write(struct buffer *buf, struct tls_session *session) { struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - ASSERT(session->opt->key_method == 2); ASSERT(buf_init(buf, 0)); /* write a uint32 0 */ @@ -2382,7 +2310,7 @@ key_method_2_write(struct buffer *buf, struct tls_session *session) } /* write key_method + flags */ - if (!buf_write_u8(buf, (session->opt->key_method & KEY_METHOD_MASK))) + if (!buf_write_u8(buf, KEY_METHOD_2)) { goto error; } @@ -2395,7 +2323,7 @@ key_method_2_write(struct buffer *buf, struct tls_session *session) /* write options string */ { - if (!write_string(buf, local_options_string(session), TLS_OPTIONS_LEN)) + if (!write_string(buf, session->opt->local_options, TLS_OPTIONS_LEN)) { goto error; } @@ -2404,7 +2332,7 @@ key_method_2_write(struct buffer *buf, struct tls_session *session) /* write username/password if specified */ if (auth_user_pass_enabled) { -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT auth_user_pass_setup(session->opt->auth_user_pass_file, session->opt->sci); #else auth_user_pass_setup(session->opt->auth_user_pass_file, NULL); @@ -2416,7 +2344,9 @@ key_method_2_write(struct buffer *buf, struct tls_session *session) * username/password */ if (auth_token.defined) + { up = &auth_token; + } if (!write_string(buf, up->username, -1)) { @@ -2462,10 +2392,9 @@ key_method_2_write(struct buffer *buf, struct tls_session *session) * generation is postponed until after the pull/push, so we can process pushed * cipher directives. */ - if (session->opt->server && !(session->opt->ncp_enabled - && session->opt->mode == MODE_SERVER && ks->key_id <= 0)) + if (session->opt->server && !(session->opt->mode == MODE_SERVER && ks->key_id <= 0)) { - if (ks->authenticated) + if (ks->authenticated > KS_AUTH_FALSE) { if (!tls_session_generate_data_channel_keys(session)) { @@ -2483,73 +2412,15 @@ error: return false; } -static bool -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 */ - - ASSERT(session->opt->key_method == 1); - - if (!session->verified) - { - msg(D_TLS_ERRORS, - "TLS Error: Certificate verification failed (key-method 1)"); - goto error; - } - - status = read_key(&key, &session->opt->key_type, buf); - if (status != 1) - { - msg(D_TLS_ERRORS, - "TLS Error: Error reading data channel key from plaintext buffer"); - goto error; - } - - if (!check_key(&key, &session->opt->key_type)) - { - msg(D_TLS_ERRORS, "TLS Error: Bad decrypting key received from peer"); - goto error; - } - - if (buf->len < 1) - { - msg(D_TLS_ERRORS, "TLS Error: Missing options string"); - goto error; - } - -#ifdef ENABLE_OCC - /* compare received remote options string - * with our locally computed options string */ - if (!session->opt->disable_occ - && !options_cmp_equal_safe((char *) BPTR(buf), session->opt->remote_options, buf->len)) - { - options_warning_safe((char *) BPTR(buf), session->opt->remote_options, buf->len); - } -#endif - - buf_clear(buf); - - init_key_ctx(&ks->crypto_options.key_ctx_bi.decrypt, &key, - &session->opt->key_type, OPENVPN_OP_DECRYPT, - "Data Channel Decrypt"); - secure_memzero(&key, sizeof(key)); - ks->authenticated = true; - return true; - -error: - buf_clear(buf); - secure_memzero(&key, sizeof(key)); - return false; -} - +/** + * Handle reading key data, peer-info, username/password, OCC + * from the TLS control channel (cleartext). + */ 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 */ - int key_method_flags; bool username_status, password_status; struct gc_arena gc = gc_new(); @@ -2559,8 +2430,6 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio /* allocate temporary objects */ ALLOC_ARRAY_CLEAR_GC(options, char, TLS_OPTIONS_LEN, &gc); - ASSERT(session->opt->key_method == 2); - /* discard leading uint32 */ if (!buf_advance(buf, 4)) { @@ -2570,7 +2439,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio } /* get key method */ - key_method_flags = buf_read_u8(buf); + int key_method_flags = buf_read_u8(buf); if ((key_method_flags & KEY_METHOD_MASK) != 2) { msg(D_TLS_ERRORS, @@ -2593,7 +2462,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio goto error; } - ks->authenticated = false; + ks->authenticated = KS_AUTH_FALSE; /* always extract username + password fields from buf, even if not * authenticating for it, because otherwise we can't get at the @@ -2603,7 +2472,6 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio 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); @@ -2616,19 +2484,6 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio multi->remote_ciphername = options_string_extract_option(options, "cipher", NULL); - if (tls_peer_info_ncp_ver(multi->peer_info) < 2) - { - /* Peer does not support NCP, but leave NCP enabled if the local and - * remote cipher do not match to attempt 'poor-man's NCP'. - */ - if (multi->remote_ciphername == NULL - || 0 == strcmp(multi->remote_ciphername, multi->opt.config_ciphername)) - { - session->opt->ncp_enabled = false; - } - } -#endif /* if P2MP_SERVER */ - if (tls_session_user_pass_enabled(session)) { /* Perform username/password authentication */ @@ -2653,19 +2508,18 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio "TLS Error: Certificate verification failed (key-method 2)"); goto error; } - ks->authenticated = true; + ks->authenticated = KS_AUTH_TRUE; } /* clear username and password from memory */ secure_memzero(up, sizeof(*up)); /* Perform final authentication checks */ - if (ks->authenticated) + if (ks->authenticated > KS_AUTH_FALSE) { verify_final_auth_checks(multi, session); } -#ifdef ENABLE_OCC /* check options consistency */ if (!session->opt->disable_occ && !options_cmp_equal(options, session->opt->remote_options)) @@ -2674,10 +2528,9 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio if (session->opt->ssl_flags & SSLF_OPT_VERIFY) { msg(D_TLS_ERRORS, "Option inconsistency warnings triggering disconnect due to --opt-verify"); - ks->authenticated = false; + ks->authenticated = KS_AUTH_FALSE; } } -#endif buf_clear(buf); @@ -2685,13 +2538,14 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final * veto opportunity over authentication decision. */ - if (ks->authenticated && plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) + if ((ks->authenticated > KS_AUTH_FALSE) + && 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; + ks->authenticated = KS_AUTH_FALSE; } setenv_del(session->opt->es, "exported_keying_material"); @@ -2715,6 +2569,7 @@ key_method_2_read(struct buffer *buf, struct tls_multi *multi, struct tls_sessio return true; error: + ks->authenticated = KS_AUTH_FALSE; secure_memzero(ks->key_src, sizeof(*ks->key_src)); if (up) { @@ -2777,9 +2632,9 @@ tls_process(struct tls_multi *multi, && ks->n_packets >= session->opt->renegotiate_packets) || (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", - (int)(ks->established + session->opt->renegotiate_seconds - now), + msg(D_TLS_DEBUG_LOW, "TLS: soft reset sec=%d/%d bytes=" counter_format + "/%d pkts=" counter_format "/%d", + (int) (now - ks->established), session->opt->renegotiate_seconds, ks->n_bytes, session->opt->renegotiate_bytes, ks->n_packets, session->opt->renegotiate_packets); key_state_soft_reset(session); @@ -2847,21 +2702,12 @@ tls_process(struct tls_multi *multi, } /* Are we timed out on receive? */ - if (now >= ks->must_negotiate) + if (now >= ks->must_negotiate && 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; } /* Wait for Initial Handshake ACK */ @@ -2901,10 +2747,12 @@ tls_process(struct tls_multi *multi, } state_change = true; ks->state = S_ACTIVE; + /* Cancel negotiation timeout */ + ks->must_negotiate = 0; 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); + link_socket_set_outgoing_addr(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); @@ -2992,23 +2840,9 @@ tls_process(struct tls_multi *multi, 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 (!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 + if (!key_method_2_write(buf, session)) { - ASSERT(0); + goto error; } state_change = true; @@ -3022,23 +2856,9 @@ tls_process(struct tls_multi *multi, && ((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 + if (!key_method_2_read(buf, multi, session)) { - ASSERT(0); + goto error; } state_change = true; @@ -3162,10 +2982,8 @@ tls_multi_process(struct tls_multi *multi, interval_t *wakeup) { struct gc_arena gc = gc_new(); - int i; int active = TLSMP_INACTIVE; bool error = false; - int tas; perf_push(PERF_TLS_MULTI_PROCESS); @@ -3176,7 +2994,7 @@ tls_multi_process(struct tls_multi *multi, * and which has a defined remote IP addr. */ - for (i = 0; i < TM_SIZE; ++i) + for (int i = 0; i < TM_SIZE; ++i) { struct tls_session *session = &multi->session[i]; struct key_state *ks = &session->key[KS_PRIMARY]; @@ -3251,7 +3069,7 @@ tls_multi_process(struct tls_multi *multi, update_time(); - tas = tls_authentication_status(multi, TLS_MULTI_AUTH_STATUS_INTERVAL); + int tas = tls_authentication_status(multi, TLS_MULTI_AUTH_STATUS_INTERVAL); /* * If lame duck session expires, kill it. @@ -3284,7 +3102,7 @@ tls_multi_process(struct tls_multi *multi, */ if (error) { - for (i = 0; i < (int) SIZE(multi->key_scan); ++i) + for (int i = 0; i < (int) SIZE(multi->key_scan); ++i) { if (multi->key_scan[i]->state >= S_ACTIVE) { @@ -3301,7 +3119,7 @@ nohard: const int throw_level = GREMLIN_CONNECTION_FLOOD_LEVEL(multi->opt.gremlin); if (throw_level) { - for (i = 0; i < (int) SIZE(multi->key_scan); ++i) + for (int i = 0; i < (int) SIZE(multi->key_scan); ++i) { if (multi->key_scan[i]->state >= throw_level) { @@ -3324,6 +3142,95 @@ nohard: * to implement a multiplexed TLS channel over the TCP/UDP port. */ +static inline void +handle_data_channel_packet(struct tls_multi *multi, + const struct link_socket_actual *from, + struct buffer *buf, + struct crypto_options **opt, + bool floated, + const uint8_t **ad_start) +{ + struct gc_arena gc = gc_new(); + + uint8_t c = *BPTR(buf); + int op = c >> P_OPCODE_SHIFT; + int key_id = c & P_KEY_ID_MASK; + + /* data channel packet */ + for (int i = 0; i < KEY_SCAN_SIZE; ++i) + { + struct key_state *ks = multi->key_scan[i]; + + /* + * This is the basic test of TLS state compatibility between a local OpenVPN + * instance and its remote peer. + * + * If the test fails, it tells us that we are getting a packet from a source + * which claims reference to a prior negotiated TLS session, but the local + * OpenVPN instance has no memory of such a negotiation. + * + * It almost always occurs on UDP sessions when the passive side of the + * connection is restarted without the active side restarting as well (the + * passive side is the server which only listens for the connections, the + * active side is the client which initiates connections). + */ + if (DECRYPT_KEY_ENABLED(multi, ks) + && key_id == ks->key_id + && (ks->authenticated == KS_AUTH_TRUE) + && (floated || link_socket_actual_match(from, &ks->remote_addr))) + { + if (!ks->crypto_options.key_ctx_bi.initialized) + { + msg(D_MULTI_DROPPED, + "Key %s [%d] not initialized (yet), dropping packet.", + print_link_socket_actual(from, &gc), key_id); + goto done; + } + + /* 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) + { + msg(D_TLS_ERRORS, "Protocol error: received P_DATA_V2 from %s but length is < 4", + print_link_socket_actual(from, &gc)); + ++multi->n_soft_errors; + goto done; + } + ASSERT(buf_advance(buf, 3)); + } + + ++ks->n_packets; + ks->n_bytes += buf->len; + dmsg(D_TLS_KEYSELECT, + "TLS: tls_pre_decrypt, key_id=%d, IP=%s", + key_id, print_link_socket_actual(from, &gc)); + gc_free(&gc); + return; + } + } + + msg(D_TLS_ERRORS, + "TLS Error: local/remote TLS keys are out of sync: %s [%d]", + print_link_socket_actual(from, &gc), key_id); + +done: + tls_clear_error(); + buf->len = 0; + *opt = NULL; + gc_free(&gc); +} + /* * * When we are in TLS mode, this is the first routine which sees @@ -3357,448 +3264,374 @@ tls_pre_decrypt(struct tls_multi *multi, bool floated, const uint8_t **ad_start) { + + if (buf->len <= 0) + { + buf->len = 0; + *opt = NULL; + return false; + } + struct gc_arena gc = gc_new(); bool ret = false; - if (buf->len > 0) + /* get opcode */ + uint8_t pkt_firstbyte = *BPTR(buf); + int op = pkt_firstbyte >> P_OPCODE_SHIFT; + + if ((op == P_DATA_V1) || (op == P_DATA_V2)) { - int i; - int op; - int key_id; + handle_data_channel_packet(multi, from, buf, opt, floated, ad_start); + return false; + } - /* get opcode and key ID */ + /* get key_id */ + int key_id = pkt_firstbyte & P_KEY_ID_MASK; + + /* control channel packet */ + bool do_burst = false; + bool new_link = false; + struct session_id sid; /* remote session ID */ + + /* verify legal opcode */ + if (op < P_FIRST_OPCODE || op > P_LAST_OPCODE) + { + if (op == P_CONTROL_HARD_RESET_CLIENT_V1 + || op == P_CONTROL_HARD_RESET_SERVER_V1) { - uint8_t c = *BPTR(buf); - op = c >> P_OPCODE_SHIFT; - key_id = c & P_KEY_ID_MASK; + msg(D_TLS_ERRORS, "Peer tried unsupported key-method 1"); } + msg(D_TLS_ERRORS, + "TLS Error: unknown opcode received from %s op=%d", + print_link_socket_actual(from, &gc), op); + goto error; + } - if ((op == P_DATA_V1) || (op == P_DATA_V2)) + /* hard reset ? */ + if (is_hard_reset_method2(op)) + { + /* verify client -> server or server -> client connection */ + if (((op == P_CONTROL_HARD_RESET_CLIENT_V2 + || op == P_CONTROL_HARD_RESET_CLIENT_V3) && !multi->opt.server) + || ((op == P_CONTROL_HARD_RESET_SERVER_V2) && multi->opt.server)) { - /* data channel packet */ - for (i = 0; i < KEY_SCAN_SIZE; ++i) - { - struct key_state *ks = multi->key_scan[i]; - - /* - * This is the basic test of TLS state compatibility between a local OpenVPN - * instance and its remote peer. - * - * If the test fails, it tells us that we are getting a packet from a source - * which claims reference to a prior negotiated TLS session, but the local - * OpenVPN instance has no memory of such a negotiation. - * - * It almost always occurs on UDP sessions when the passive side of the - * connection is restarted without the active side restarting as well (the - * passive side is the server which only listens for the connections, the - * active side is the client which initiates connections). - */ - if (DECRYPT_KEY_ENABLED(multi, ks) - && key_id == ks->key_id - && ks->authenticated -#ifdef ENABLE_DEF_AUTH - && !ks->auth_deferred -#endif - && (floated || link_socket_actual_match(from, &ks->remote_addr))) - { - if (!ks->crypto_options.key_ctx_bi.initialized) - { - msg(D_MULTI_DROPPED, - "Key %s [%d] not initialized (yet), dropping packet.", - print_link_socket_actual(from, &gc), key_id); - goto error_lite; - } - - /* 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) - { - msg(D_TLS_ERRORS, "Protocol error: received P_DATA_V2 from %s but length is < 4", - print_link_socket_actual(from, &gc)); - goto error; - } - ASSERT(buf_advance(buf, 3)); - } + msg(D_TLS_ERRORS, + "TLS Error: client->client or server->server connection attempted from %s", + print_link_socket_actual(from, &gc)); + goto error; + } + } - ++ks->n_packets; - ks->n_bytes += buf->len; - dmsg(D_TLS_KEYSELECT, - "TLS: tls_pre_decrypt, key_id=%d, IP=%s", - key_id, print_link_socket_actual(from, &gc)); - gc_free(&gc); - return ret; - } - } + /* + * Authenticate Packet + */ + dmsg(D_TLS_DEBUG, "TLS: control channel, op=%s, IP=%s", + packet_opcode_name(op), print_link_socket_actual(from, &gc)); + /* get remote session-id */ + { + struct buffer tmp = *buf; + buf_advance(&tmp, 1); + if (!session_id_read(&sid, &tmp) || !session_id_defined(&sid)) + { msg(D_TLS_ERRORS, - "TLS Error: local/remote TLS keys are out of sync: %s [%d]", - print_link_socket_actual(from, &gc), key_id); - goto error_lite; + "TLS Error: session-id not found in packet from %s", + print_link_socket_actual(from, &gc)); + goto error; } - else /* control channel packet */ - { - bool do_burst = false; - bool new_link = false; - struct session_id sid; /* remote session ID */ + } + + int i; + /* use session ID to match up packet with appropriate tls_session object */ + for (i = 0; i < TM_SIZE; ++i) + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; - /* verify legal opcode */ - if (op < P_FIRST_OPCODE || op > P_LAST_OPCODE) + dmsg(D_TLS_DEBUG, + "TLS: initial packet test, i=%d state=%s, mysid=%s, rec-sid=%s, rec-ip=%s, stored-sid=%s, stored-ip=%s", + i, + state_name(ks->state), + session_id_print(&session->session_id, &gc), + session_id_print(&sid, &gc), + print_link_socket_actual(from, &gc), + session_id_print(&ks->session_id_remote, &gc), + print_link_socket_actual(&ks->remote_addr, &gc)); + + if (session_id_equal(&ks->session_id_remote, &sid)) + /* found a match */ + { + if (i == TM_LAME_DUCK) { msg(D_TLS_ERRORS, - "TLS Error: unknown opcode received from %s op=%d", - print_link_socket_actual(from, &gc), op); + "TLS ERROR: received control packet with stale session-id=%s", + session_id_print(&sid, &gc)); goto error; } + dmsg(D_TLS_DEBUG, + "TLS: found match, session[%d], sid=%s", + i, session_id_print(&sid, &gc)); + break; + } + } - /* hard reset ? */ - if (is_hard_reset(op, 0)) - { - /* verify client -> server or server -> client connection */ - if (((op == P_CONTROL_HARD_RESET_CLIENT_V1 - || op == P_CONTROL_HARD_RESET_CLIENT_V2) && !multi->opt.server) - || ((op == P_CONTROL_HARD_RESET_SERVER_V1 - || op == P_CONTROL_HARD_RESET_SERVER_V2) && multi->opt.server)) - { - msg(D_TLS_ERRORS, - "TLS Error: client->client or server->server connection attempted from %s", - print_link_socket_actual(from, &gc)); - goto error; - } - } - - /* - * Authenticate Packet - */ - dmsg(D_TLS_DEBUG, "TLS: control channel, op=%s, IP=%s", - packet_opcode_name(op), print_link_socket_actual(from, &gc)); + /* + * Hard reset and session id does not match any session in + * multi->session: Possible initial packet + */ + if (i == TM_SIZE && is_hard_reset_method2(op)) + { + struct tls_session *session = &multi->session[TM_ACTIVE]; + struct key_state *ks = &session->key[KS_PRIMARY]; - /* get remote session-id */ + /* + * If we have no session currently in progress, the initial packet will + * open a new session in TM_ACTIVE rather than TM_UNTRUSTED. + */ + if (!session_id_defined(&ks->session_id_remote)) + { + if (multi->opt.single_session && multi->n_sessions) { - struct buffer tmp = *buf; - buf_advance(&tmp, 1); - if (!session_id_read(&sid, &tmp) || !session_id_defined(&sid)) - { - msg(D_TLS_ERRORS, - "TLS Error: session-id not found in packet from %s", - print_link_socket_actual(from, &gc)); - goto error; - } + msg(D_TLS_ERRORS, + "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [1]", + print_link_socket_actual(from, &gc)); + goto error; } - /* use session ID to match up packet with appropriate tls_session object */ - for (i = 0; i < TM_SIZE; ++i) +#ifdef ENABLE_MANAGEMENT + if (management) { - struct tls_session *session = &multi->session[i]; - struct key_state *ks = &session->key[KS_PRIMARY]; - - dmsg(D_TLS_DEBUG, - "TLS: initial packet test, i=%d state=%s, mysid=%s, rec-sid=%s, rec-ip=%s, stored-sid=%s, stored-ip=%s", - i, - state_name(ks->state), - session_id_print(&session->session_id, &gc), - session_id_print(&sid, &gc), - print_link_socket_actual(from, &gc), - session_id_print(&ks->session_id_remote, &gc), - print_link_socket_actual(&ks->remote_addr, &gc)); - - if (session_id_equal(&ks->session_id_remote, &sid)) - /* found a match */ - { - if (i == TM_LAME_DUCK) - { - msg(D_TLS_ERRORS, - "TLS ERROR: received control packet with stale session-id=%s", - session_id_print(&sid, &gc)); - goto error; - } - dmsg(D_TLS_DEBUG, - "TLS: found match, session[%d], sid=%s", - i, session_id_print(&sid, &gc)); - break; - } + management_set_state(management, + OPENVPN_STATE_AUTH, + NULL, + NULL, + NULL, + NULL, + NULL); } +#endif - /* - * Initial packet received. - */ - - if (i == TM_SIZE && is_hard_reset(op, 0)) - { - struct tls_session *session = &multi->session[TM_ACTIVE]; - struct key_state *ks = &session->key[KS_PRIMARY]; - - if (!is_hard_reset(op, multi->opt.key_method)) - { - msg(D_TLS_ERRORS, "TLS ERROR: initial packet local/remote key_method mismatch, local key_method=%d, op=%s", - multi->opt.key_method, - packet_opcode_name(op)); - goto error; - } + msg(D_TLS_DEBUG_LOW, + "TLS: Initial packet from %s, sid=%s", + print_link_socket_actual(from, &gc), + session_id_print(&sid, &gc)); - /* - * If we have no session currently in progress, the initial packet will - * open a new session in TM_ACTIVE rather than TM_UNTRUSTED. - */ - if (!session_id_defined(&ks->session_id_remote)) - { - if (multi->opt.single_session && multi->n_sessions) - { - msg(D_TLS_ERRORS, - "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [1]", - print_link_socket_actual(from, &gc)); - goto error; - } + do_burst = true; + new_link = true; + i = TM_ACTIVE; + session->untrusted_addr = *from; + } + } -#ifdef ENABLE_MANAGEMENT - if (management) - { - management_set_state(management, - OPENVPN_STATE_AUTH, - NULL, - NULL, - NULL, - NULL, - NULL); - } -#endif + /* + * If we detected new session in the last if block, variable i has + * changed to TM_ACTIVE, so check the condition again. + */ + if (i == TM_SIZE && is_hard_reset_method2(op)) + { + /* + * No match with existing sessions, + * probably a new session. + */ + struct tls_session *session = &multi->session[TM_UNTRUSTED]; - msg(D_TLS_DEBUG_LOW, - "TLS: Initial packet from %s, sid=%s", - print_link_socket_actual(from, &gc), - session_id_print(&sid, &gc)); + /* + * If --single-session, don't allow any hard-reset connection request + * unless it the first packet of the session. + */ + if (multi->opt.single_session) + { + msg(D_TLS_ERRORS, + "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [2]", + print_link_socket_actual(from, &gc)); + goto error; + } - do_burst = true; - new_link = true; - i = TM_ACTIVE; - session->untrusted_addr = *from; - } - } + if (!read_control_auth(buf, &session->tls_wrap, from, + session->opt)) + { + goto error; + } - if (i == TM_SIZE && is_hard_reset(op, 0)) - { - /* - * No match with existing sessions, - * probably a new session. - */ - struct tls_session *session = &multi->session[TM_UNTRUSTED]; - - /* - * If --single-session, don't allow any hard-reset connection request - * unless it the the first packet of the session. - */ - if (multi->opt.single_session) - { - msg(D_TLS_ERRORS, - "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [2]", - print_link_socket_actual(from, &gc)); - goto error; - } + /* + * New session-initiating control packet is authenticated at this point, + * assuming that the --tls-auth command line option was used. + * + * Without --tls-auth, we leave authentication entirely up to TLS. + */ + msg(D_TLS_DEBUG_LOW, + "TLS: new session incoming connection from %s", + print_link_socket_actual(from, &gc)); - if (!is_hard_reset(op, multi->opt.key_method)) - { - msg(D_TLS_ERRORS, "TLS ERROR: new session local/remote key_method mismatch, local key_method=%d, op=%s", - multi->opt.key_method, - packet_opcode_name(op)); - goto error; - } + new_link = true; + i = TM_UNTRUSTED; + session->untrusted_addr = *from; + } + else + { + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; - if (!read_control_auth(buf, &session->tls_wrap, from)) - { - goto error; - } + /* + * Packet must belong to an existing session. + */ + if (i != TM_ACTIVE && i != TM_UNTRUSTED) + { + msg(D_TLS_ERRORS, + "TLS Error: Unroutable control packet received from %s (si=%d op=%s)", + print_link_socket_actual(from, &gc), + i, + packet_opcode_name(op)); + goto error; + } - /* - * New session-initiating control packet is authenticated at this point, - * assuming that the --tls-auth command line option was used. - * - * Without --tls-auth, we leave authentication entirely up to TLS. - */ - msg(D_TLS_DEBUG_LOW, - "TLS: new session incoming connection from %s", - print_link_socket_actual(from, &gc)); + /* + * Verify remote IP address + */ + if (!new_link && !link_socket_actual_match(&ks->remote_addr, from)) + { + msg(D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s", + print_link_socket_actual(from, &gc)); + goto error; + } - new_link = true; - i = TM_UNTRUSTED; - session->untrusted_addr = *from; - } - else + /* + * Remote is requesting a key renegotiation + */ + if (op == P_CONTROL_SOFT_RESET_V1 + && DECRYPT_KEY_ENABLED(multi, ks)) + { + if (!read_control_auth(buf, &session->tls_wrap, from, + session->opt)) { - struct tls_session *session = &multi->session[i]; - struct key_state *ks = &session->key[KS_PRIMARY]; + goto error; + } - /* - * Packet must belong to an existing session. - */ - if (i != TM_ACTIVE && i != TM_UNTRUSTED) - { - msg(D_TLS_ERRORS, - "TLS Error: Unroutable control packet received from %s (si=%d op=%s)", - print_link_socket_actual(from, &gc), - i, - packet_opcode_name(op)); - goto error; - } + key_state_soft_reset(session); - /* - * Verify remote IP address - */ - if (!new_link && !link_socket_actual_match(&ks->remote_addr, from)) - { - msg(D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s", - print_link_socket_actual(from, &gc)); - goto error; - } + dmsg(D_TLS_DEBUG, + "TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s", + i, session_id_print(&sid, &gc)); + } + else + { + /* + * Remote responding to our key renegotiation request? + */ + if (op == P_CONTROL_SOFT_RESET_V1) + { + do_burst = true; + } - /* - * Remote is requesting a key renegotiation - */ - if (op == P_CONTROL_SOFT_RESET_V1 - && DECRYPT_KEY_ENABLED(multi, ks)) - { - if (!read_control_auth(buf, &session->tls_wrap, from)) - { - goto error; - } + if (!read_control_auth(buf, &session->tls_wrap, from, + session->opt)) + { + goto error; + } - key_state_soft_reset(session); + dmsg(D_TLS_DEBUG, + "TLS: received control channel packet s#=%d sid=%s", + i, session_id_print(&sid, &gc)); + } + } - dmsg(D_TLS_DEBUG, - "TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s", - i, session_id_print(&sid, &gc)); - } - else - { - /* - * Remote responding to our key renegotiation request? - */ - if (op == P_CONTROL_SOFT_RESET_V1) - { - do_burst = true; - } + /* + * We have an authenticated control channel packet (if --tls-auth was set). + * Now pass to our reliability layer which deals with + * packet acknowledgements, retransmits, sequencing, etc. + */ + struct tls_session *session = &multi->session[i]; + struct key_state *ks = &session->key[KS_PRIMARY]; - if (!read_control_auth(buf, &session->tls_wrap, from)) - { - goto error; - } + /* Make sure we were initialized and that we're not in an error state */ + ASSERT(ks->state != S_UNDEF); + ASSERT(ks->state != S_ERROR); + ASSERT(session_id_defined(&session->session_id)); - dmsg(D_TLS_DEBUG, - "TLS: received control channel packet s#=%d sid=%s", - i, session_id_print(&sid, &gc)); - } - } + /* Let our caller know we processed a control channel packet */ + ret = true; - /* - * We have an authenticated control channel packet (if --tls-auth was set). - * Now pass to our reliability layer which deals with - * packet acknowledgements, retransmits, sequencing, etc. - */ - { - struct tls_session *session = &multi->session[i]; - struct key_state *ks = &session->key[KS_PRIMARY]; + /* + * Set our remote address and remote session_id + */ + if (new_link) + { + ks->session_id_remote = sid; + ks->remote_addr = *from; + ++multi->n_sessions; + } + else if (!link_socket_actual_match(&ks->remote_addr, from)) + { + msg(D_TLS_ERRORS, + "TLS Error: Existing session control channel packet from unknown IP address: %s", + print_link_socket_actual(from, &gc)); + goto error; + } - /* Make sure we were initialized and that we're not in an error state */ - ASSERT(ks->state != S_UNDEF); - ASSERT(ks->state != S_ERROR); - ASSERT(session_id_defined(&session->session_id)); + /* + * Should we do a retransmit of all unacknowledged packets in + * the send buffer? This improves the start-up efficiency of the + * initial key negotiation after the 2nd peer comes online. + */ + if (do_burst && !session->burst) + { + reliable_schedule_now(ks->send_reliable); + session->burst = true; + } - /* Let our caller know we processed a control channel packet */ - ret = true; + /* Check key_id */ + if (ks->key_id != key_id) + { + msg(D_TLS_ERRORS, + "TLS ERROR: local/remote key IDs out of sync (%d/%d) ID: %s", + ks->key_id, key_id, print_key_id(multi, &gc)); + goto error; + } - /* - * Set our remote address and remote session_id - */ - if (new_link) - { - ks->session_id_remote = sid; - ks->remote_addr = *from; - ++multi->n_sessions; - } - else if (!link_socket_actual_match(&ks->remote_addr, from)) - { - msg(D_TLS_ERRORS, - "TLS Error: Existing session control channel packet from unknown IP address: %s", - print_link_socket_actual(from, &gc)); - goto error; - } + /* + * Process incoming ACKs for packets we can now + * delete from reliable send buffer + */ + { + /* buffers all packet IDs to delete from send_reliable */ + struct reliable_ack send_ack; - /* - * Should we do a retransmit of all unacknowledged packets in - * the send buffer? This improves the start-up efficiency of the - * initial key negotiation after the 2nd peer comes online. - */ - if (do_burst && !session->burst) - { - reliable_schedule_now(ks->send_reliable); - session->burst = true; - } + send_ack.len = 0; + if (!reliable_ack_read(&send_ack, buf, &session->session_id)) + { + msg(D_TLS_ERRORS, + "TLS Error: reading acknowledgement record from packet"); + goto error; + } + reliable_send_purge(ks->send_reliable, &send_ack); + } - /* Check key_id */ - if (ks->key_id != key_id) - { - msg(D_TLS_ERRORS, - "TLS ERROR: local/remote key IDs out of sync (%d/%d) ID: %s", - ks->key_id, key_id, print_key_id(multi, &gc)); - goto error; - } + if (op != P_ACK_V1 && reliable_can_get(ks->rec_reliable)) + { + packet_id_type id; - /* - * Process incoming ACKs for packets we can now - * delete from reliable send buffer - */ + /* Extract the packet ID from the packet */ + if (reliable_ack_read_packet_id(buf, &id)) + { + /* Avoid deadlock by rejecting packet that would de-sequentialize receive buffer */ + if (reliable_wont_break_sequentiality(ks->rec_reliable, id)) + { + if (reliable_not_replay(ks->rec_reliable, id)) { - /* buffers all packet IDs to delete from send_reliable */ - struct reliable_ack send_ack; - - send_ack.len = 0; - if (!reliable_ack_read(&send_ack, buf, &session->session_id)) + /* Save incoming ciphertext packet to reliable buffer */ + struct buffer *in = reliable_get_buf(ks->rec_reliable); + ASSERT(in); + if (!buf_copy(in, buf)) { - msg(D_TLS_ERRORS, - "TLS Error: reading acknowledgement record from packet"); + msg(D_MULTI_DROPPED, + "Incoming control channel packet too big, dropping."); goto error; } - reliable_send_purge(ks->send_reliable, &send_ack); + reliable_mark_active_incoming(ks->rec_reliable, in, id, op); } - if (op != P_ACK_V1 && reliable_can_get(ks->rec_reliable)) - { - packet_id_type id; - - /* Extract the packet ID from the packet */ - if (reliable_ack_read_packet_id(buf, &id)) - { - /* Avoid deadlock by rejecting packet that would de-sequentialize receive buffer */ - if (reliable_wont_break_sequentiality(ks->rec_reliable, id)) - { - if (reliable_not_replay(ks->rec_reliable, id)) - { - /* Save incoming ciphertext packet to reliable buffer */ - struct buffer *in = reliable_get_buf(ks->rec_reliable); - ASSERT(in); - if(!buf_copy(in, buf)) - { - msg(D_MULTI_DROPPED, - "Incoming control channel packet too big, dropping."); - goto error; - } - reliable_mark_active_incoming(ks->rec_reliable, in, id, op); - } - - /* Process outgoing acknowledgment for packet just received, even if it's a replay */ - reliable_ack_acknowledge_packet_id(ks->rec_ack, id); - } - } - } + /* Process outgoing acknowledgment for packet just received, even if it's a replay */ + reliable_ack_acknowledge_packet_id(ks->rec_ack, id); } } } @@ -3811,7 +3644,6 @@ done: error: ++multi->n_soft_errors; -error_lite: tls_clear_error(); goto done; } @@ -3833,94 +3665,91 @@ tls_pre_decrypt_lite(const struct tls_auth_standalone *tas, const struct buffer *buf) { - struct gc_arena gc = gc_new(); - bool ret = false; - - if (buf->len > 0) + if (buf->len <= 0) { - int op; - int key_id; + return false; + } + struct gc_arena gc = gc_new(); - /* get opcode and key ID */ - { - uint8_t c = *BPTR(buf); - op = c >> P_OPCODE_SHIFT; - key_id = c & P_KEY_ID_MASK; - } + /* get opcode and key ID */ + uint8_t pkt_firstbyte = *BPTR(buf); + int op = pkt_firstbyte >> P_OPCODE_SHIFT; + int key_id = pkt_firstbyte & P_KEY_ID_MASK; - /* this packet is from an as-yet untrusted source, so - * scrutinize carefully */ + /* this packet is from an as-yet untrusted source, so + * scrutinize carefully */ - if (op != P_CONTROL_HARD_RESET_CLIENT_V2) - { - /* - * This can occur due to bogus data or DoS packets. - */ - dmsg(D_TLS_STATE_ERRORS, - "TLS State Error: No TLS state for client %s, opcode=%d", - print_link_socket_actual(from, &gc), - op); - goto error; - } + if (op != P_CONTROL_HARD_RESET_CLIENT_V2 + && op != P_CONTROL_HARD_RESET_CLIENT_V3) + { + /* + * This can occur due to bogus data or DoS packets. + */ + dmsg(D_TLS_STATE_ERRORS, + "TLS State Error: No TLS state for client %s, opcode=%d", + print_link_socket_actual(from, &gc), + op); + goto error; + } - if (key_id != 0) - { - dmsg(D_TLS_STATE_ERRORS, - "TLS State Error: Unknown key ID (%d) received from %s -- 0 was expected", - key_id, - print_link_socket_actual(from, &gc)); - goto error; - } + if (key_id != 0) + { + dmsg(D_TLS_STATE_ERRORS, + "TLS State Error: Unknown key ID (%d) received from %s -- 0 was expected", + key_id, + print_link_socket_actual(from, &gc)); + goto error; + } - if (buf->len > EXPANDED_SIZE_DYNAMIC(&tas->frame)) - { - dmsg(D_TLS_STATE_ERRORS, - "TLS State Error: Large packet (size %d) received from %s -- a packet no larger than %d bytes was expected", - buf->len, - print_link_socket_actual(from, &gc), - EXPANDED_SIZE_DYNAMIC(&tas->frame)); - goto error; - } + if (buf->len > EXPANDED_SIZE_DYNAMIC(&tas->frame)) + { + dmsg(D_TLS_STATE_ERRORS, + "TLS State Error: Large packet (size %d) received from %s -- a packet no larger than %d bytes was expected", + buf->len, + print_link_socket_actual(from, &gc), + EXPANDED_SIZE_DYNAMIC(&tas->frame)); + goto error; + } - { - struct buffer newbuf = clone_buf(buf); - struct tls_wrap_ctx tls_wrap_tmp = tas->tls_wrap; - bool status; - - /* HMAC test, if --tls-auth was specified */ - status = read_control_auth(&newbuf, &tls_wrap_tmp, from); - free_buf(&newbuf); - if (!status) - { - goto error; - } - /* - * At this point, if --tls-auth is being used, we know that - * the packet has passed the HMAC test, but we don't know if - * it is a replay yet. We will attempt to defeat replays - * by not advancing to the S_START state until we - * receive an ACK from our first reply to the client - * that includes an HMAC of our randomly generated 64 bit - * session ID. - * - * On the other hand if --tls-auth is not being used, we - * will proceed to begin the TLS authentication - * handshake with only cursory integrity checks having - * been performed, since we will be leaving the task - * of authentication solely up to TLS. - */ + struct buffer newbuf = clone_buf(buf); + struct tls_wrap_ctx tls_wrap_tmp = tas->tls_wrap; - ret = true; - } + /* HMAC test, if --tls-auth was specified */ + bool status = read_control_auth(&newbuf, &tls_wrap_tmp, from, NULL); + free_buf(&newbuf); + free_buf(&tls_wrap_tmp.tls_crypt_v2_metadata); + if (tls_wrap_tmp.cleanup_key_ctx) + { + free_key_ctx_bi(&tls_wrap_tmp.opt.key_ctx_bi); } + if (!status) + { + goto error; + } + + /* + * At this point, if --tls-auth is being used, we know that + * the packet has passed the HMAC test, but we don't know if + * it is a replay yet. We will attempt to defeat replays + * by not advancing to the S_START state until we + * receive an ACK from our first reply to the client + * that includes an HMAC of our randomly generated 64 bit + * session ID. + * + * On the other hand if --tls-auth is not being used, we + * will proceed to begin the TLS authentication + * handshake with only cursory integrity checks having + * been performed, since we will be leaving the task + * of authentication solely up to TLS. + */ gc_free(&gc); - return ret; + return true; error: tls_clear_error(); gc_free(&gc); - return ret; + return false; } /* Choose the key with which to encrypt a data packet */ @@ -3929,51 +3758,51 @@ tls_pre_encrypt(struct tls_multi *multi, struct buffer *buf, struct crypto_options **opt) { multi->save_ks = NULL; - if (buf->len > 0) + if (buf->len <= 0) + { + buf->len = 0; + *opt = NULL; + return; + } + + struct key_state *ks_select = NULL; + for (int i = 0; i < KEY_SCAN_SIZE; ++i) { - int i; - struct key_state *ks_select = NULL; - for (i = 0; i < KEY_SCAN_SIZE; ++i) + struct key_state *ks = multi->key_scan[i]; + if (ks->state >= S_ACTIVE + && (ks->authenticated == KS_AUTH_TRUE) + && ks->crypto_options.key_ctx_bi.initialized + ) { - 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 - ) + if (!ks_select) { - if (!ks_select) - { - ks_select = ks; - } - if (now >= ks->auth_deferred_expire) - { - ks_select = ks; - break; - } + ks_select = ks; + } + if (now >= ks->auth_deferred_expire) + { + ks_select = ks; + break; } } + } - if (ks_select) - { - *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; - } - else - { - struct gc_arena gc = gc_new(); - dmsg(D_TLS_KEYSELECT, "TLS Warning: no data channel send key available: %s", - print_key_id(multi, &gc)); - gc_free(&gc); - } + if (ks_select) + { + *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; } + else + { + struct gc_arena gc = gc_new(); + dmsg(D_TLS_KEYSELECT, "TLS Warning: no data channel send key available: %s", + print_key_id(multi, &gc)); + gc_free(&gc); - buf->len = 0; - *opt = NULL; + *opt = NULL; + buf->len = 0; + } } void @@ -4097,13 +3926,11 @@ 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) + for (int i = 0; i < TM_SIZE; ++i) { struct tls_session *session = &multi->session[i]; - for (j = 0; j < KS_SIZE; ++j) + for (int j = 0; j < KS_SIZE; ++j) { struct key_state *ks = &session->key[j]; @@ -4123,45 +3950,6 @@ tls_update_remote_addr(struct tls_multi *multi, const struct link_socket_actual 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; -} - void show_available_tls_ciphers(const char *cipher_list, const char *cipher_list_tls13, @@ -4169,21 +3957,20 @@ show_available_tls_ciphers(const char *cipher_list, { printf("Available TLS Ciphers, listed in order of preference:\n"); -#if (ENABLE_CRYPTO_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x1010100fL) - printf("\nFor TLS 1.3 and newer (--tls-ciphersuites):\n\n"); - show_available_tls_ciphers_list(cipher_list_tls13, tls_cert_profile, true); -#else - (void) cipher_list_tls13; /* Avoid unused warning */ -#endif + if (tls_version_max() >= TLS_VER_1_3) + { + printf("\nFor TLS 1.3 and newer (--tls-ciphersuites):\n\n"); + show_available_tls_ciphers_list(cipher_list_tls13, tls_cert_profile, true); + } printf("\nFor TLS 1.2 and older (--tls-cipher):\n\n"); show_available_tls_ciphers_list(cipher_list, tls_cert_profile, false); printf("\n" - "Be aware that that whether a cipher suite in this list can actually work\n" - "depends on the specific setup of both peers. See the man page entries of\n" - "--tls-cipher and --show-tls for more details.\n\n" - ); + "Be aware that that whether a cipher suite in this list can actually work\n" + "depends on the specific setup of both peers. See the man page entries of\n" + "--tls-cipher and --show-tls for more details.\n\n" + ); } /* @@ -4314,10 +4101,3 @@ delayed_auth_pass_purge(void) auth_user_pass.wait_for_push = false; purge_user_pass(&auth_user_pass, false); } - -#else /* if defined(ENABLE_CRYPTO) */ -static void -dummy(void) -{ -} -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h index 3266f38..005628f 100644 --- a/src/openvpn/ssl.h +++ b/src/openvpn/ssl.h @@ -29,8 +29,6 @@ #ifndef OPENVPN_SSL_H #define OPENVPN_SSL_H -#if defined(ENABLE_CRYPTO) - #include "basic.h" #include "common.h" #include "crypto.h" @@ -65,9 +63,14 @@ #define P_CONTROL_HARD_RESET_CLIENT_V2 7 /* initial key from client, forget previous state */ #define P_CONTROL_HARD_RESET_SERVER_V2 8 /* initial key from server, forget previous state */ -/* define the range of legal opcodes */ -#define P_FIRST_OPCODE 1 -#define P_LAST_OPCODE 9 +/* indicates key_method >= 2 and client-specific tls-crypt key */ +#define P_CONTROL_HARD_RESET_CLIENT_V3 10 /* initial key from client, forget previous state */ + +/* define the range of legal opcodes + * Since we do no longer support key-method 1 we consider + * the v1 op codes invalid */ +#define P_FIRST_OPCODE 3 +#define P_LAST_OPCODE 10 /* * Set the max number of acknowledgments that can "hitch a ride" on an outgoing @@ -88,13 +91,6 @@ #define TLS_MULTI_HORIZON 2 /* call tls_multi_process frequently for n seconds after * every packet sent/received action */ -/* - * The SSL/TLS worker thread will wait at most this many seconds for the - * interprocess communication pipe to the main thread to be ready to accept - * writes. - */ -#define TLS_MULTI_THREAD_SEND_TIMEOUT 5 - /* Interval that tls_multi_process should call tls_authentication_status */ #define TLS_MULTI_AUTH_STATUS_INTERVAL 10 @@ -105,14 +101,26 @@ /* Maximum length of OCC options string passed as part of auth handshake */ #define TLS_OPTIONS_LEN 512 +/* Definitions of the bits in the IV_PROTO bitfield + * + * In older OpenVPN versions this used in a comparison + * IV_PROTO >= 2 to determine if DATA_V2 is supported. + * Therefore any client announcing any of the flags must + * also announce IV_PROTO_DATA_V2. We also treat bit 0 + * as reserved for this reason */ + +/** Support P_DATA_V2 */ +#define IV_PROTO_DATA_V2 (1<<1) + +/** Assume client will send a push request and server does not need + * to wait for a push-request to send a push-reply */ +#define IV_PROTO_REQUEST_PUSH (1<<2) + + /* Default field in X509 to be username */ #define X509_USERNAME_FIELD_DEFAULT "CN" -/* - * Range of key exchange methods - */ -#define KEY_METHOD_MIN 1 -#define KEY_METHOD_MAX 2 +#define KEY_METHOD_2 2 /* key method taken from lower 4 bits */ #define KEY_METHOD_MASK 0x0F @@ -430,7 +438,9 @@ void ssl_purge_auth(const bool auth_user_pass_only); void ssl_set_auth_token(const char *token); -#ifdef ENABLE_CLIENT_CR +bool ssl_clean_auth_token(void); + +#ifdef ENABLE_MANAGEMENT /* * ssl_get_auth_challenge will parse the server-pushed auth-failed * reason string and return a dynamically allocated @@ -438,8 +448,6 @@ void ssl_set_auth_token(const char *token); */ void ssl_purge_auth_challenge(void); -bool ssl_clean_auth_token(void); - void ssl_put_auth_challenge(const char *cr_str); #endif @@ -489,15 +497,6 @@ bool tls_session_update_crypto_params(struct tls_session *session, struct frame *frame, struct frame *frame_fragment); -/** - * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. - * Allows non-NCP peers to upgrade their cipher individually. - * - * Make sure to call tls_session_update_crypto_params() after calling this - * function. - */ -void tls_poor_mans_ncp(struct options *o, const char *remote_ciphername); - #ifdef MANAGEMENT_DEF_AUTH static inline char * tls_get_peer_info(const struct tls_multi *multi) @@ -506,31 +505,27 @@ 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. +/* + * inline functions */ -bool tls_check_ncp_cipher_list(const char *list); -/** - * Return true iff item is present in the colon-separated zero-terminated - * cipher list. - */ -bool tls_item_in_cipher_list(const char *item, const char *list); +/** Free the elements of a tls_wrap_ctx structure */ +static inline void +tls_wrap_free(struct tls_wrap_ctx *tls_wrap) +{ + if (packet_id_initialized(&tls_wrap->opt.packet_id)) + { + packet_id_free(&tls_wrap->opt.packet_id); + } + if (tls_wrap->cleanup_key_ctx) + { + free_key_ctx_bi(&tls_wrap->opt.key_ctx_bi); + } -/* - * inline functions - */ + free_buf(&tls_wrap->tls_crypt_v2_metadata); + free_buf(&tls_wrap->work); +} static inline bool tls_initial_packet_received(const struct tls_multi *multi) @@ -597,12 +592,11 @@ void show_tls_performance_stats(void); void extract_x509_field_test(void); /** - * Given a key_method, return true if opcode represents the required form of - * hard_reset. + * Given a key_method, return true if opcode represents the one of the + * hard_reset op codes for key-method 2 * - * If key_method == 0, return true if any form of hard reset is used. */ -bool is_hard_reset(int op, int key_method); +bool is_hard_reset_method2(int op); void delayed_auth_pass_purge(void); @@ -619,6 +613,5 @@ void show_available_tls_ciphers(const char *cipher_list, const char *cipher_list_tls13, const char *tls_cert_profile); -#endif /* ENABLE_CRYPTO */ #endif /* ifndef OPENVPN_SSL_H */ diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index c614efa..7f52ab1 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -125,8 +125,6 @@ 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. * @@ -201,6 +199,16 @@ void tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *cipher void tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile); /** + * Set the (elliptic curve) group allowed for signatures and + * key exchange. + * + * @param ctx TLS context to restrict, must be valid. + * @param groups List of groups that will be allowed, in priority, + * separated by : + */ +void tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups); + +/** * Check our certificate notBefore and notAfter fields, and warn if the cert is * either not yet valid or has expired. Note that this is a non-fatal error, * since we compare against the system time, which might be incorrect. @@ -215,11 +223,12 @@ void tls_ctx_check_cert_time(const struct tls_root_ctx *ctx); * * @param ctx TLS context to use * @param dh_file The file name to load the parameters from, or - * "[[INLINE]]" in the case of inline files. - * @param dh_file_inline A string containing the parameters + * a string containing the parameters in the case + * of inline files. + * @param dh_file_inline True if dh_file is an inline file. */ void tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file, - const char *dh_file_inline); + bool dh_file_inline); /** * Load Elliptic Curve Parameters, and load them into the library-specific @@ -237,15 +246,15 @@ void tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name * * @param ctx TLS context to use * @param pkcs12_file The file name to load the information from, or - * "[[INLINE]]" in the case of inline files. - * @param pkcs12_file_inline A string containing the information + * a string containing the information in the case + * of inline files. + * @param pkcs12_file_inline True if pkcs12_file is an inline file. * * @return 1 if an error occurred, 0 if parsing was * successful. */ int tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, - const char *pkcs12_file_inline, bool load_ca_file - ); + bool pkcs12_file_inline, bool load_ca_file); /** * Use Windows cryptoapi for key and cert, and add to library-specific TLS @@ -265,46 +274,41 @@ void tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert * * @param ctx TLS context to use * @param cert_file The file name to load the certificate from, or - * "[[INLINE]]" in the case of inline files. - * @param cert_file_inline A string containing the certificate + * a string containing the certificate in the case + * of inline files. + * @param cert_file_inline True if cert_file is an inline file. */ void tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, - const char *cert_file_inline); + bool cert_file_inline); /** * Load private key file into the given TLS context. * * @param ctx TLS context to use * @param priv_key_file The file name to load the private key from, or - * "[[INLINE]]" in the case of inline files. - * @param priv_key_file_inline A string containing the private key + * a string containing the private key in the case + * of inline files. + * @param priv_key_file_inline True if priv_key_file is an inline file * * @return 1 if an error occurred, 0 if parsing was * successful. */ int tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, - const char *priv_key_file_inline - ); + bool priv_key_file_inline); -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT /** * Tell the management interface to load the given certificate and the external * private key matching the given certificate. * * @param ctx TLS context to use - * @param cert_file The file name to load the certificate from, or - * "[[INLINE]]" in the case of inline files. - * @param cert_file_inline A string containing the certificate * - * @return 1 if an error occurred, 0 if parsing was - * successful. + * @return 1 if an error occurred, 0 if successful. */ -int tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline); - -#endif +int tls_ctx_use_management_external_key(struct tls_root_ctx *ctx); +#endif /* ENABLE_MANAGEMENT */ /** * Load certificate authority certificates from the given file or path. @@ -313,13 +317,13 @@ int tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, * * @param ctx TLS context to use * @param ca_file The file name to load the CAs from, or - * "[[INLINE]]" in the case of inline files. - * @param ca_file_inline A string containing the CAs + * a string containing the CAs in the case of + * inline files. + * @param ca_file_inline True if ca_file is an inline file * @param ca_path The path to load the CAs from */ void tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, - const char *ca_file_inline, const char *ca_path, bool tls_server - ); + bool ca_file_inline, const char *ca_path, bool tls_server); /** * Load extra certificate authority certificates from the given file or path. @@ -329,12 +333,14 @@ void tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, * * @param ctx TLS context to use * @param extra_certs_file The file name to load the certs from, or - * "[[INLINE]]" in the case of inline files. - * @param extra_certs_file_inline A string containing the certs + * a string containing the certs in the + * case of inline files. + * @param extra_certs_file_inline True if extra_certs_file is an inline + * file. */ -void tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, - const char *extra_certs_file_inline - ); +void tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, + const char *extra_certs_file, + bool extra_certs_file_inline); #ifdef ENABLE_CRYPTO_MBEDTLS /** @@ -377,11 +383,11 @@ void key_state_ssl_free(struct key_state_ssl *ks_ssl); * * @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 + * an array containing the inline CRL. + * @param crl_inline True if crl_file is an inline CRL. */ void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, - const char *crl_file, const char *crl_inline); + const char *crl_file, bool crl_inline); /** * Keying Material Exporters [RFC 5705] allows additional keying material to be @@ -557,5 +563,4 @@ 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 ac25ffa..96897e4 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -64,8 +64,7 @@ * material. * -# \c S_GOT_KEY, have received remote part of \c key_source2 random * material. - * -# \c S_ACTIVE, normal operation during remaining handshake window. - * -# \c S_NORMAL_OP, normal operation. + * -# \c S_ACTIVE, normal operation * * Servers follow the same order, except for \c S_SENT_KEY and \c * S_GOT_KEY being reversed, because the server first receives the @@ -94,9 +93,9 @@ * immediately after negotiation has * completed while still within the * handshake window. */ -/* ready to exchange data channel packets */ -#define S_NORMAL_OP 7 /**< Normal operational \c key_state - * state. */ +/* Note that earlier versions also had a S_OP_NORMAL state that was + * virtually identical with S_ACTIVE and the code still assumes everything + * >= S_ACTIVE to be fully operational */ /** @} name Control channel negotiation states */ /** @} addtogroup control_processor */ @@ -127,6 +126,25 @@ struct key_source2 { struct key_source server; /**< Random provided by server. */ }; + +/** + * This reflects the (server side) authentication state after the TLS + * session has been established and key_method_2_read is called. If async auth + * is enabled the state will first move to KS_AUTH_DEFERRED before eventually + * being set to KS_AUTH_TRUE or KS_AUTH_FALSE + * Only KS_AUTH_TRUE is fully authenticated + */ +enum ks_auth_state { + KS_AUTH_FALSE, /**< Key state is not authenticated */ + KS_AUTH_DEFERRED, /**< Key state authentication is being deferred, + * by async auth */ + KS_AUTH_TRUE /**< Key state is authenticated. TLS and user/pass + * succeeded. This includes AUTH_PENDING/OOB + * authentication as those hold the + * connection artificially in KS_AUTH_DEFERRED + */ +}; + /** * Security parameter state of one TLS and data channel %key session. * @ingroup control_processor @@ -185,12 +203,9 @@ struct key_state /* * If bad username/password, TLS connection will come up but 'authenticated' will be false. */ - bool authenticated; + enum ks_auth_state authenticated; time_t auth_deferred_expire; -#ifdef ENABLE_DEF_AUTH - /* If auth_deferred is true, authentication is being deferred */ - bool auth_deferred; #ifdef MANAGEMENT_DEF_AUTH unsigned int mda_key_id; unsigned int mda_status; @@ -200,7 +215,6 @@ struct key_state time_t acf_last_mod; char *auth_control_file; #endif -#endif }; /** Control channel wrapping (--tls-auth/--tls-crypt) context */ @@ -213,6 +227,12 @@ struct tls_wrap_ctx } mode; /**< Control channel wrapping mode */ struct crypto_options opt; /**< Crypto state */ struct buffer work; /**< Work buffer (only for --tls-crypt) */ + struct key_ctx tls_crypt_v2_server_key; /**< Decrypts client keys */ + const struct buffer *tls_crypt_v2_wkc; /**< Wrapped client key, + * sent to server */ + struct buffer tls_crypt_v2_metadata; /**< Received from client */ + bool cleanup_key_ctx; /**< opt.key_ctx_bi is owned by + * this context */ }; /* @@ -233,25 +253,18 @@ struct tls_options /* if true, don't xmit until first packet from peer is received */ bool xmit_hold; -#ifdef ENABLE_OCC /* local and remote options strings * that must match between client and server */ const char *local_options; const char *remote_options; -#endif /* from command line */ - int key_method; bool replay; bool single_session; -#ifdef ENABLE_OCC bool disable_occ; -#endif int mode; bool pull; -#ifdef ENABLE_PUSH_PEER_INFO int push_peer_info_detail; -#endif int transition_window; int handshake_window; interval_t packet_timeout; @@ -265,7 +278,7 @@ struct tls_options int verify_x509_type; const char *verify_x509_name; const char *crl_file; - const char *crl_file_inline; + bool crl_file_inline; int ns_cert_type; unsigned remote_cert_ku[MAX_PARMS]; const char *remote_cert_eku; @@ -285,13 +298,15 @@ struct tls_options bool tcp_mode; const char *config_ciphername; - const char *config_authname; + const char *config_ncp_ciphers; bool ncp_enabled; + bool tls_crypt_v2; + const char *tls_crypt_v2_verify_script; + /** TLS handshake wrapping state */ struct tls_wrap_ctx tls_wrap; - /* frame parameters for TLS control channel */ struct frame frame; /* used for username/password authentication */ @@ -299,15 +314,21 @@ 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. */ + + bool auth_token_generate; /**< Generate auth-tokens on successful + * user/pass auth,seet via + * options->auth_token_generate. */ + bool auth_token_call_auth; /**< always call normal authentication */ unsigned int auth_token_lifetime; + struct key_ctx auth_token_key; + /* use the client-config-dir as a positive authenticator */ const char *client_config_dir_exclusive; /* instance-wide environment variable set */ struct env_set *es; + openvpn_net_ctx_t *net_ctx; const struct plugin_list *plugins; /* compression parms */ @@ -334,7 +355,7 @@ struct tls_options const struct x509_track *x509_track; -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT const struct static_challenge_info *sci; #endif @@ -361,10 +382,6 @@ 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. * @ingroup control_processor @@ -461,7 +478,6 @@ struct tls_session */ #define KEY_SCAN_SIZE 3 - /** * Security parameter state for a single VPN tunnel. * @ingroup control_processor @@ -517,22 +533,42 @@ struct tls_multi struct cert_hash_set *locked_cert_hash_set; #ifdef ENABLE_DEF_AUTH + /* Time of last call to tls_authentication_status */ + time_t tas_last; +#endif + /* * An error message to send to client on AUTH_FAILED */ 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; -#endif + char *auth_token; /**< If server sends a generated auth-token, + * this is the token to use for future + * user/pass authentications in this session. + */ + char *auth_token_initial; + /**< The first auth-token we sent to a client, for clients that do + * not update their auth-token (older OpenVPN3 core versions) + */ +#define AUTH_TOKEN_HMAC_OK (1<<0) + /**< Auth-token sent from client has valid hmac */ +#define AUTH_TOKEN_EXPIRED (1<<1) + /**< Auth-token sent from client has expired */ +#define AUTH_TOKEN_VALID_EMPTYUSER (1<<2) + /**< + * Auth-token is only valid for an empty username + * and not the username actually supplied from the client + * + * OpenVPN 3 clients sometimes wipes or replaces the username with a + * username hint from their config. + */ + int auth_token_state_flags; + /**< The state of the auth-token sent from the client last time */ /* For P_DATA_V2 */ uint32_t peer_id; @@ -540,13 +576,6 @@ struct tls_multi char *remote_ciphername; /**< cipher specified in peer's config file */ - 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 index 4746261..9c87478 100644 --- a/src/openvpn/ssl_mbedtls.c +++ b/src/openvpn/ssl_mbedtls.c @@ -35,7 +35,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) +#if defined(ENABLE_CRYPTO_MBEDTLS) #include "errlevel.h" #include "ssl_backend.h" @@ -43,6 +43,7 @@ #include "buffer.h" #include "misc.h" #include "manage.h" +#include "pkcs11_backend.h" #include "ssl_common.h" #include <mbedtls/havege.h> @@ -64,12 +65,12 @@ static const mbedtls_x509_crt_profile openvpn_x509_crt_profile_legacy = { /* Hashes from SHA-1 and above */ - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), 0xFFFFFFF, /* Any PK alg */ 0xFFFFFFF, /* Any curve */ 1024, /* RSA-1024 and larger */ @@ -78,10 +79,10 @@ static const mbedtls_x509_crt_profile openvpn_x509_crt_profile_legacy = static const mbedtls_x509_crt_profile openvpn_x509_crt_profile_preferred = { /* SHA-2 and above */ - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) | - MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) + |MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), 0xFFFFFFF, /* Any PK alg */ 0xFFFFFFF, /* Any curve */ 2048, /* RSA-2048 and larger */ @@ -167,17 +168,7 @@ tls_ctx_free(struct tls_root_ctx *ctx) } #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); - } + pkcs11h_certificate_freeCertificate(ctx->pkcs11_cert); #endif if (ctx->allowed_ciphers) @@ -185,6 +176,11 @@ tls_ctx_free(struct tls_root_ctx *ctx) free(ctx->allowed_ciphers); } + if (ctx->groups) + { + free(ctx->groups); + } + CLEAR(*ctx); ctx->initialised = false; @@ -199,12 +195,63 @@ tls_ctx_initialised(struct tls_root_ctx *ctx) return ctx->initialised; } +#ifdef HAVE_EXPORT_KEYING_MATERIAL +int +mbedtls_ssl_export_keys_cb(void *p_expkey, const unsigned char *ms, + const unsigned char *kb, size_t maclen, + size_t keylen, size_t ivlen, + const unsigned char client_random[32], + const unsigned char server_random[32], + mbedtls_tls_prf_types tls_prf_type) +{ + struct tls_session *session = p_expkey; + struct key_state_ssl *ks_ssl = &session->key[KS_PRIMARY].ks_ssl; + unsigned char client_server_random[64]; + + ks_ssl->exported_key_material = gc_malloc(session->opt->ekm_size, + true, NULL); + + memcpy(client_server_random, client_random, 32); + memcpy(client_server_random + 32, server_random, 32); + + const size_t ms_len = sizeof(ks_ssl->ctx->session->master); + int ret = mbedtls_ssl_tls_prf(tls_prf_type, ms, ms_len, + session->opt->ekm_label, client_server_random, + sizeof(client_server_random), ks_ssl->exported_key_material, + session->opt->ekm_size); + + if (!mbed_ok(ret)) + { + secure_memzero(ks_ssl->exported_key_material, session->opt->ekm_size); + } + + secure_memzero(client_server_random, sizeof(client_server_random)); + + return ret; +} +#endif /* HAVE_EXPORT_KEYING_MATERIAL */ + void key_state_export_keying_material(struct key_state_ssl *ssl, struct tls_session *session) { + if (ssl->exported_key_material) + { + unsigned int size = session->opt->ekm_size; + struct gc_arena gc = gc_new(); + unsigned int len = (size * 2) + 2; + + const char *key = format_hex_ex(ssl->exported_key_material, + 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); + gc_free(&gc); + } } + bool tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) { @@ -241,40 +288,29 @@ tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers) } msg(M_WARN, "mbed TLS does not support setting tls-ciphersuites. " - "Ignoring TLS 1.3 cipher list: %s", ciphers); + "Ignoring TLS 1.3 cipher list: %s", ciphers); } 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++; - } - } + int cipher_count = get_num_elements(ciphers, ':'); /* Allocate an array for them */ ALLOC_ARRAY_CLEAR(ctx->allowed_ciphers, int, cipher_count+1) /* Parse allowed ciphers, getting IDs */ - i = 0; + int i = 0; tmp_ciphers_orig = tmp_ciphers = string_alloc(ciphers, NULL); token = strtok(tmp_ciphers, ":"); @@ -308,10 +344,45 @@ tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile) } else { - msg (M_FATAL, "ERROR: Invalid cert profile: %s", profile); + msg(M_FATAL, "ERROR: Invalid cert profile: %s", profile); + } +} + +void +tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups) +{ + ASSERT(ctx); + struct gc_arena gc = gc_new(); + + /* Get number of groups and allocate an array in ctx */ + int groups_count = get_num_elements(groups, ':'); + ALLOC_ARRAY_CLEAR(ctx->groups, mbedtls_ecp_group_id, groups_count + 1) + + /* Parse allowed ciphers, getting IDs */ + int i = 0; + char *tmp_groups = string_alloc(groups, &gc); + + const char *token; + while ((token = strsep(&tmp_groups, ":"))) + { + const mbedtls_ecp_curve_info *ci = + mbedtls_ecp_curve_info_from_name(token); + if (!ci) + { + msg(M_WARN, "Warning unknown curve/group specified: %s", token); + } + else + { + ctx->groups[i] = ci->grp_id; + i++; + } } + ctx->groups[i] = MBEDTLS_ECP_DP_NONE; + + gc_free(&gc); } + void tls_ctx_check_cert_time(const struct tls_root_ctx *ctx) { @@ -334,13 +405,13 @@ 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_inline - ) + bool dh_inline) { - if (!strcmp(dh_file, INLINE_FILE_TAG) && dh_inline) + if (dh_inline) { if (!mbed_ok(mbedtls_dhm_parse_dhm(ctx->dhm_ctx, - (const unsigned char *) dh_inline, strlen(dh_inline)+1))) + (const unsigned char *) dh_file, + strlen(dh_file) + 1))) { msg(M_FATAL, "Cannot read inline DH parameters"); } @@ -370,9 +441,7 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name int tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, - const char *pkcs12_file_inline, - bool load_ca_file - ) + bool pkcs12_file_inline, bool load_ca_file) { msg(M_FATAL, "PKCS #12 files not yet supported for mbed TLS."); return 0; @@ -388,8 +457,7 @@ tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) void tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, - const char *cert_inline - ) + bool cert_inline) { ASSERT(NULL != ctx); @@ -398,10 +466,11 @@ tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, ALLOC_OBJ_CLEAR(ctx->crt_chain, mbedtls_x509_crt); } - if (!strcmp(cert_file, INLINE_FILE_TAG) && cert_inline) + if (cert_inline) { if (!mbed_ok(mbedtls_x509_crt_parse(ctx->crt_chain, - (const unsigned char *) cert_inline, strlen(cert_inline)+1))) + (const unsigned char *)cert_file, + strlen(cert_file) + 1))) { msg(M_FATAL, "Cannot load inline certificate file"); } @@ -417,8 +486,7 @@ tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, int tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, - const char *priv_key_inline - ) + bool priv_key_inline) { int status; ASSERT(NULL != ctx); @@ -428,19 +496,20 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, ALLOC_OBJ_CLEAR(ctx->priv_key, mbedtls_pk_context); } - if (!strcmp(priv_key_file, INLINE_FILE_TAG) && priv_key_inline) + if (priv_key_inline) { status = mbedtls_pk_parse_key(ctx->priv_key, - (const unsigned char *) priv_key_inline, strlen(priv_key_inline)+1, - NULL, 0); + (const unsigned char *) priv_key_file, + strlen(priv_key_file) + 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, + (const unsigned char *) priv_key_file, + strlen(priv_key_file) + 1, + (unsigned char *) passbuf, strlen(passbuf)); } } @@ -462,7 +531,8 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, management_auth_failure(management, UP_TYPE_PRIVATE_KEY, NULL); } #endif - msg(M_WARN, "Cannot load private key file %s", priv_key_file); + msg(M_WARN, "Cannot load private key file %s", + print_key_filename(priv_key_file, priv_key_inline)); return 1; } @@ -475,13 +545,6 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, 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. @@ -508,11 +571,9 @@ external_pkcs1_sign( void *ctx_voidptr, 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; + uint8_t *to_sign = NULL; + size_t asn_len = 0, oid_size = 0; const char *oid = NULL; if (NULL == ctx) @@ -548,12 +609,14 @@ external_pkcs1_sign( void *ctx_voidptr, asn_len = 10 + oid_size; } - sig_len = ctx->signature_length; - if ( (SIZE_MAX - hashlen) < asn_len || (hashlen + asn_len) > sig_len) + if ((SIZE_MAX - hashlen) < asn_len + || ctx->signature_length < (asn_len + hashlen)) { return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; } + ALLOC_ARRAY_CLEAR(to_sign, uint8_t, asn_len + hashlen); + uint8_t *p = to_sign; if (md_alg != MBEDTLS_MD_NONE) { /* @@ -578,34 +641,16 @@ external_pkcs1_sign( void *ctx_voidptr, *p++ = MBEDTLS_ASN1_OCTET_STRING; *p++ = hashlen; - /* Determine added ASN length */ - asn_len = p - sig; + /* Double-check ASN length */ + ASSERT(asn_len == p - to_sign); } /* Copy the hash to be signed */ - memcpy( p, hash, hashlen ); + 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) + /* Call external signature function */ + if (!ctx->sign(ctx->sign_ctx, to_sign, asn_len + hashlen, sig, + ctx->signature_length)) { rv = MBEDTLS_ERR_RSA_PRIVATE_FAILED; goto done; @@ -614,14 +659,7 @@ external_pkcs1_sign( void *ctx_voidptr, rv = 0; done: - if (in_b64) - { - free(in_b64); - } - if (out_b64) - { - free(out_b64); - } + free(to_sign); return rv; } @@ -634,23 +672,30 @@ external_key_len(void *vctx) } int -tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline) +tls_ctx_use_external_signing_func(struct tls_root_ctx *ctx, + external_sign_func sign_func, void *sign_ctx) { ASSERT(NULL != ctx); - tls_ctx_load_cert_file(ctx, cert_file, cert_file_inline); - if (ctx->crt_chain == NULL) { + msg(M_WARN, "ERROR: external key requires a certificate."); + return 1; + } + + if (mbedtls_pk_get_type(&ctx->crt_chain->pk) != MBEDTLS_PK_RSA) + { + msg(M_WARN, "ERROR: external key with mbed TLS requires a " + "certificate with an RSA key."); return 1; } - ALLOC_OBJ_CLEAR(ctx->external_key, struct external_context); - ctx->external_key->signature_length = mbedtls_pk_get_len(&ctx->crt_chain->pk); + ctx->external_key.signature_length = mbedtls_pk_get_len(&ctx->crt_chain->pk); + ctx->external_key.sign = sign_func; + ctx->external_key.sign_ctx = sign_ctx; ALLOC_OBJ_CLEAR(ctx->priv_key, mbedtls_pk_context); - if (!mbed_ok(mbedtls_pk_setup_rsa_alt(ctx->priv_key, ctx->external_key, + if (!mbed_ok(mbedtls_pk_setup_rsa_alt(ctx->priv_key, &ctx->external_key, NULL, external_pkcs1_sign, external_key_len))) { return 1; @@ -658,22 +703,67 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, return 0; } -#endif /* ifdef MANAGMENT_EXTERNAL_KEY */ + +#ifdef ENABLE_MANAGEMENT +/** Query the management interface for a signature, see external_sign_func. */ +static bool +management_sign_func(void *sign_ctx, const void *src, size_t src_len, + void *dst, size_t dst_len) +{ + bool ret = false; + char *src_b64 = NULL; + char *dst_b64 = NULL; + + if (!management || (openvpn_base64_encode(src, src_len, &src_b64) <= 0)) + { + goto cleanup; + } + + /* + * We only support RSA external keys and PKCS1 signatures at the moment + * in mbed TLS, so the signature parameter is hardcoded to this encoding + */ + if (!(dst_b64 = management_query_pk_sig(management, src_b64, + "RSA_PKCS1_PADDING"))) + { + goto cleanup; + } + + if (openvpn_base64_decode(dst_b64, dst, dst_len) != dst_len) + { + goto cleanup; + } + + ret = true; +cleanup: + free(src_b64); + free(dst_b64); + + return ret; +} + +int +tls_ctx_use_management_external_key(struct tls_root_ctx *ctx) +{ + return tls_ctx_use_external_signing_func(ctx, management_sign_func, NULL); +} + +#endif /* ifdef ENABLE_MANAGEMENT */ 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 - ) + bool 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 (ca_file && ca_inline) { if (!mbed_ok(mbedtls_x509_crt_parse(ctx->ca_chain, - (const unsigned char *) ca_inline, strlen(ca_inline)+1))) + (const unsigned char *) ca_file, + strlen(ca_file) + 1))) { msg(M_FATAL, "Cannot load inline CA certificates"); } @@ -690,8 +780,7 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, void tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, - const char *extra_certs_inline - ) + bool extra_certs_inline) { ASSERT(NULL != ctx); @@ -700,11 +789,11 @@ tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, ALLOC_OBJ_CLEAR(ctx->crt_chain, mbedtls_x509_crt); } - if (!strcmp(extra_certs_file, INLINE_FILE_TAG) && extra_certs_inline) + if (extra_certs_inline) { if (!mbed_ok(mbedtls_x509_crt_parse(ctx->crt_chain, - (const unsigned char *) extra_certs_inline, - strlen(extra_certs_inline)+1))) + (const unsigned char *) extra_certs_file, + strlen(extra_certs_file) + 1))) { msg(M_FATAL, "Cannot load inline extra-certs file"); } @@ -932,7 +1021,7 @@ tls_version_to_major_minor(int tls_ver, int *major, int *minor) void backend_tls_ctx_reload_crl(struct tls_root_ctx *ctx, const char *crl_file, - const char *crl_inline) + bool crl_inline) { ASSERT(crl_file); @@ -942,10 +1031,11 @@ backend_tls_ctx_reload_crl(struct tls_root_ctx *ctx, const char *crl_file, } mbedtls_x509_crl_free(ctx->crl); - if (!strcmp(crl_file, INLINE_FILE_TAG) && crl_inline) + if (crl_inline) { if (!mbed_ok(mbedtls_x509_crl_parse(ctx->crl, - (const unsigned char *)crl_inline, strlen(crl_inline)+1))) + (const unsigned char *)crl_file, + strlen(crl_file) + 1))) { msg(M_WARN, "CRL: cannot parse inline CRL"); goto err; @@ -967,7 +1057,8 @@ err: 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) + const struct tls_root_ctx *ssl_ctx, bool is_server, + struct tls_session *session) { ASSERT(NULL != ssl_ctx); ASSERT(ks_ssl); @@ -992,6 +1083,11 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl, mbedtls_ssl_conf_ciphersuites(ks_ssl->ssl_config, ssl_ctx->allowed_ciphers); } + if (ssl_ctx->groups) + { + mbedtls_ssl_conf_curves(ks_ssl->ssl_config, ssl_ctx->groups); + } + /* 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 @@ -1012,13 +1108,11 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl, 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); } @@ -1059,6 +1153,15 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl, } } +#ifdef HAVE_EXPORT_KEYING_MATERIAL + /* Initialize keying material exporter */ + if (session->opt->ekm_size) + { + mbedtls_ssl_conf_export_keys_ext_cb(ks_ssl->ssl_config, + mbedtls_ssl_export_keys_cb, session); + } +#endif + /* Initialise SSL context */ ALLOC_OBJ_CLEAR(ks_ssl->ctx, mbedtls_ssl_context); mbedtls_ssl_init(ks_ssl->ctx); @@ -1075,6 +1178,8 @@ key_state_ssl_free(struct key_state_ssl *ks_ssl) { if (ks_ssl) { + free(ks_ssl->exported_key_material); + if (ks_ssl->ctx) { mbedtls_ssl_free(ks_ssl->ctx); @@ -1421,4 +1526,4 @@ get_ssl_library_version(void) return mbedtls_version; } -#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ +#endif /* defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/ssl_mbedtls.h b/src/openvpn/ssl_mbedtls.h index f99aba1..0525134 100644 --- a/src/openvpn/ssl_mbedtls.h +++ b/src/openvpn/ssl_mbedtls.h @@ -33,9 +33,10 @@ #include <mbedtls/ssl.h> #include <mbedtls/x509_crt.h> +#include <mbedtls/version.h> #if defined(ENABLE_PKCS11) -#include <mbedtls/pkcs11.h> +#include <pkcs11-helper-1.0/pkcs11h-certificate.h> #endif typedef struct _buffer_entry buffer_entry; @@ -58,6 +59,30 @@ typedef struct { } bio_ctx; /** + * External signing function prototype. A function pointer to a function + * implementing this prototype is provided to + * tls_ctx_use_external_signing_func(). + * + * @param sign_ctx The context for the signing function. + * @param src The data to be signed, + * @param src_len The length of src, in bytes. + * @param dst The destination buffer for the signature. + * @param dst_len The length of the destination buffer. + * + * @return true if signing succeeded, false otherwise. + */ +typedef bool (*external_sign_func)( + void *sign_ctx, const void *src, size_t src_size, + void *dst, size_t dst_size); + +/** Context used by external_pkcs1_sign() */ +struct external_context { + size_t signature_length; + external_sign_func sign; + void *sign_ctx; +}; + +/** * Structure that wraps the TLS context. Contents differ depending on the * SSL library used. * @@ -75,13 +100,12 @@ struct tls_root_ctx { mbedtls_x509_crl *crl; /**< Certificate Revocation List */ time_t crl_last_mtime; /**< CRL last modification time */ off_t crl_last_size; /**< size of last loaded CRL */ -#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 */ +#ifdef ENABLE_PKCS11 + pkcs11h_certificate_t pkcs11_cert; /**< PKCS11 certificate */ #endif + struct external_context external_key; /**< External key context */ int *allowed_ciphers; /**< List of allowed ciphers for this connection */ + mbedtls_ecp_group_id *groups; /**< List of allowed groups for this connection */ mbedtls_x509_crt_profile cert_profile; /**< Allowed certificate types */ }; @@ -89,7 +113,24 @@ struct key_state_ssl { mbedtls_ssl_config *ssl_config; /**< mbedTLS global ssl config */ mbedtls_ssl_context *ctx; /**< mbedTLS connection context */ bio_ctx *bio_ctx; + + /** Keying material exporter cache (RFC 5705). */ + uint8_t *exported_key_material; + }; +/** + * Call the supplied signing function to create a TLS signature during the + * TLS handshake. + * + * @param ctx TLS context to use. + * @param sign_func Signing function to call. + * @param sign_ctx Context for the sign function. + * + * @return 0 if successful, 1 if an error occurred. + */ +int tls_ctx_use_external_signing_func(struct tls_root_ctx *ctx, + external_sign_func sign_func, + void *sign_ctx); #endif /* SSL_MBEDTLS_H_ */ diff --git a/src/openvpn/ssl_ncp.c b/src/openvpn/ssl_ncp.c new file mode 100644 index 0000000..f522b8f --- /dev/null +++ b/src/openvpn/ssl_ncp.c @@ -0,0 +1,330 @@ +/* + * 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-2018 OpenVPN Inc <sales@openvpn.net> + * Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> + * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file Control Channel SSL/Data dynamic negotion Module + * This file is split from ssl.c to be able to unit test it. + */ + +/* + * The routines in this file deal with dynamically negotiating + * the data channel HMAC and cipher keys through a TLS session. + * + * 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) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "win32.h" + +#include "error.h" +#include "common.h" + +#include "ssl_ncp.h" +#include "openvpn.h" + +/** + * Return the Negotiable Crypto Parameters version advertised in the peer info + * string, or 0 if none specified. + */ +static 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; +} + +/** + * Returns whether the client supports NCP either by + * announcing IV_NCP>=2 or the IV_CIPHERS list + */ +bool +tls_peer_supports_ncp(const char *peer_info) +{ + if (!peer_info) + { + return false; + } + else if (tls_peer_info_ncp_ver(peer_info) >= 2 + || strstr(peer_info, "IV_CIPHERS=")) + { + return true; + } + else + { + return false; + } +} + +char * +mutate_ncp_cipher_list(const char *list, struct gc_arena *gc) +{ + bool error_found = false; + + struct buffer new_list = alloc_buf(MAX_NCP_CIPHERS_LENGTH); + + char *const tmp_ciphers = string_alloc(list, NULL); + const char *token = strtok(tmp_ciphers, ":"); + while (token) + { + /* + * Going through a roundtrip by using cipher_kt_get/cipher_kt_name + * (and translate_cipher_name_from_openvpn/ + * translate_cipher_name_to_openvpn) also normalises the cipher name, + * e.g. replacing AeS-128-gCm with AES-128-GCM + */ + const cipher_kt_t *ktc = cipher_kt_get(token); + if (!ktc) + { + msg(M_WARN, "Unsupported cipher in --data-ciphers: %s", token); + error_found = true; + } + else + { + const char *ovpn_cipher_name = cipher_kt_name(ktc); + + if (buf_len(&new_list)> 0) + { + /* The next if condition ensure there is always space for + * a : + */ + buf_puts(&new_list, ":"); + } + + /* Ensure buffer has capacity for cipher name + : + \0 */ + if (!(buf_forward_capacity(&new_list) > + strlen(ovpn_cipher_name) + 2)) + { + msg(M_WARN, "Length of --data-ciphers is over the " + "limit of 127 chars"); + error_found = true; + } + else + { + buf_puts(&new_list, ovpn_cipher_name); + } + } + token = strtok(NULL, ":"); + } + + + + char *ret = NULL; + if (!error_found && buf_len(&new_list) > 0) + { + buf_null_terminate(&new_list); + ret = string_alloc(buf_str(&new_list), gc); + } + free(tmp_ciphers); + free_buf(&new_list); + + return ret; +} + +bool +tls_item_in_cipher_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; +} + +const char * +tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc) +{ + /* Check if the peer sends the IV_CIPHERS list */ + const char *ncp_ciphers_start; + if (peer_info && (ncp_ciphers_start = strstr(peer_info, "IV_CIPHERS="))) + { + ncp_ciphers_start += strlen("IV_CIPHERS="); + const char *ncp_ciphers_end = strstr(ncp_ciphers_start, "\n"); + if (!ncp_ciphers_end) + { + /* IV_CIPHERS is at end of the peer_info list and no '\n' + * follows */ + ncp_ciphers_end = ncp_ciphers_start + strlen(ncp_ciphers_start); + } + + char *ncp_ciphers_peer = string_alloc(ncp_ciphers_start, gc); + /* NULL terminate the copy at the right position */ + ncp_ciphers_peer[ncp_ciphers_end - ncp_ciphers_start] = '\0'; + return ncp_ciphers_peer; + + } + else if (tls_peer_info_ncp_ver(peer_info)>=2) + { + /* If the peer announces IV_NCP=2 then it supports the AES GCM + * ciphers */ + return "AES-256-GCM:AES-128-GCM"; + } + else + { + return ""; + } +} + +char * +ncp_get_best_cipher(const char *server_list, const char *peer_info, + const char *remote_cipher, struct gc_arena *gc) +{ + /* + * The gc of the parameter is tied to the VPN session, create a + * short lived gc arena that is only valid for the duration of + * this function + */ + + struct gc_arena gc_tmp = gc_new(); + + const char *peer_ncp_list = tls_peer_ncp_list(peer_info, &gc_tmp); + + /* non-NCP client without OCC? "assume nothing" */ + /* For client doing the newer version of NCP (that send IV_CIPHER) + * we cannot assume that they will accept remote_cipher */ + if (remote_cipher == NULL || + (peer_info && strstr(peer_info, "IV_CIPHERS="))) + { + remote_cipher = ""; + } + + char *tmp_ciphers = string_alloc(server_list, &gc_tmp); + + const char *token; + while ((token = strsep(&tmp_ciphers, ":"))) + { + if (tls_item_in_cipher_list(token, peer_ncp_list) + || streq(token, remote_cipher)) + { + break; + } + } + + char *ret = NULL; + if (token != NULL) + { + ret = string_alloc(token, gc); + } + + gc_free(&gc_tmp); + return ret; +} + +/** + * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. + * Allows non-NCP peers to upgrade their cipher individually. + * + * Returns true if we switched to the peer's cipher + * + * Make sure to call tls_session_update_crypto_params() after calling this + * function. + */ +static bool +tls_poor_mans_ncp(struct options *o, const char *remote_ciphername) +{ + if (remote_ciphername + && 0 != strcmp(o->ciphername, remote_ciphername)) + { + if (tls_item_in_cipher_list(remote_ciphername, o->ncp_ciphers)) + { + o->ciphername = string_alloc(remote_ciphername, &o->gc); + msg(D_TLS_DEBUG_LOW, "Using peer cipher '%s'", o->ciphername); + return true; + } + } + return false; +} + +bool +check_pull_client_ncp(struct context *c, const int found) +{ + if (found & OPT_P_NCP) + { + msg(D_PUSH, "OPTIONS IMPORT: data channel crypto options modified"); + return true; + } + + if (!c->options.ncp_enabled) + { + return true; + } + /* If the server did not push a --cipher, we will switch to the + * remote cipher if it is in our ncp-ciphers list */ + bool useremotecipher = tls_poor_mans_ncp(&c->options, + c->c2.tls_multi->remote_ciphername); + + + /* We could not figure out the peer's cipher but we have fallback + * enabled */ + if (!useremotecipher && c->options.enable_ncp_fallback) + { + return true; + } + + /* We failed negotiation, give appropiate error message */ + if (c->c2.tls_multi->remote_ciphername) + { + msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to negotiate " + "cipher with server. Add the server's " + "cipher ('%s') to --data-ciphers (currently '%s') if " + "you want to connect to this server.", + c->c2.tls_multi->remote_ciphername, + c->options.ncp_ciphers); + return false; + + } + else + { + msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to negotiate " + "cipher with server. Configure " + "--data-ciphers-fallback if you want to connect " + "to this server."); + return false; + } +}
\ No newline at end of file diff --git a/src/openvpn/ssl_ncp.h b/src/openvpn/ssl_ncp.h new file mode 100644 index 0000000..39158a5 --- /dev/null +++ b/src/openvpn/ssl_ncp.h @@ -0,0 +1,118 @@ +/* + * 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-2018 OpenVPN Inc <sales@openvpn.net> + * Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> + * + * 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; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * @file Control Channel SSL/Data dynamic negotion Module + * This file is split from ssl.h to be able to unit test it. + */ + +#ifndef OPENVPN_SSL_NCP_H +#define OPENVPN_SSL_NCP_H + +#include "buffer.h" +#include "options.h" + +/** + * Returns whether the client supports NCP either by + * announcing IV_NCP>=2 or the IV_CIPHERS list + */ +bool +tls_peer_supports_ncp(const char *peer_info); + +/* forward declaration to break include dependency loop */ +struct context; + +/** + * Checks whether the cipher negotiation is in an acceptable state + * and we continue to connect or should abort. + * + * @return Wether the client NCP process suceeded or failed + */ +bool +check_pull_client_ncp(struct context *c, int found); + +/** + * Iterates through the ciphers in server_list and return the first + * cipher that is also supported by the peer according to the IV_NCP + * and IV_CIPHER values in peer_info. + * + * We also accept a cipher that is the remote cipher of the client for + * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. + * Allows non-NCP peers to upgrade their cipher individually. + * + * Make sure to call tls_session_update_crypto_params() after calling this + * function. + * + * @param gc gc arena that is ONLY used to allocate the returned string + * + * @returns NULL if no common cipher is available, otherwise the best common + * cipher + */ +char * +ncp_get_best_cipher(const char *server_list, const char *peer_info, + const char *remote_cipher, struct gc_arena *gc); + + +/** + * Returns the support cipher list from the peer according to the IV_NCP + * and IV_CIPHER values in peer_info. + * + * @returns Either a string containing the ncp list that is either static + * or allocated via gc. If no information is available an empty string + * ("") is returned. + */ +const char * +tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc); + +/** + * Check whether the ciphers in the supplied list are supported. + * + * @param list Colon-separated list of ciphers + * @parms gc gc_arena to allocate the returned string + * + * @returns colon separated string of normalised (via + * translate_cipher_name_from_openvpn) and + * zero terminated string iff all ciphers + * in list are supported and the total length + * is short than MAX_NCP_CIPHERS_LENGTH. NULL + * otherwise. + */ +char * +mutate_ncp_cipher_list(const char *list, struct gc_arena *gc); + +/** + * Return true iff item is present in the colon-separated zero-terminated + * cipher list. + */ +bool tls_item_in_cipher_list(const char *item, const char *list); + +/** + * The maximum length of a ncp-cipher string that is accepted. + * + * Since this list needs to be pushed as IV_CIPHERS, we are conservative + * about its length. + */ +#define MAX_NCP_CIPHERS_LENGTH 127 + +#endif /* ifndef OPENVPN_SSL_NCP_H */ diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 19509b7..5ba7440 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -34,7 +34,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO_OPENSSL) #include "errlevel.h" #include "buffer.h" @@ -52,10 +52,15 @@ #include "ssl_verify_openssl.h" +#include <openssl/bn.h> +#include <openssl/crypto.h> +#include <openssl/dh.h> +#include <openssl/dsa.h> #include <openssl/err.h> #include <openssl/pkcs12.h> +#include <openssl/rsa.h> #include <openssl/x509.h> -#include <openssl/crypto.h> +#include <openssl/ssl.h> #ifndef OPENSSL_NO_EC #include <openssl/ec.h> #endif @@ -110,6 +115,11 @@ tls_ctx_server_new(struct tls_root_ctx *ctx) { crypto_msg(M_FATAL, "SSL_CTX_new SSLv23_server_method"); } + if (ERR_peek_error() != 0) + { + crypto_msg(M_WARN, "Warning: TLS server context initialisation " + "has warnings."); + } } void @@ -123,6 +133,11 @@ tls_ctx_client_new(struct tls_root_ctx *ctx) { crypto_msg(M_FATAL, "SSL_CTX_new SSLv23_client_method"); } + if (ERR_peek_error() != 0) + { + crypto_msg(M_WARN, "Warning: TLS client context initialisation " + "has warnings."); + } } void @@ -149,13 +164,14 @@ key_state_export_keying_material(struct key_state_ssl *ssl, { 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)) + session->opt->ekm_label, + session->opt->ekm_label_size, + NULL, 0, 0)) { unsigned int len = (size * 2) + 2; @@ -171,7 +187,6 @@ key_state_export_keying_material(struct key_state_ssl *ssl, setenv_del(session->opt->es, "exported_keying_material"); } gc_free(&gc); -#endif /* if (OPENSSL_VERSION_NUMBER >= 0x10001000) */ } } @@ -209,13 +224,34 @@ info_callback(INFO_CALLBACK_SSL_CONST SSL *s, int where, int ret) int tls_version_max(void) { -#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) +#if defined(TLS1_3_VERSION) + /* If this is defined we can safely assume TLS 1.3 support */ return TLS_VER_1_3; +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L + /* + * If TLS_VER_1_3 is not defined, we were compiled against a version that + * did not support TLS 1.3. + * + * However, the library we are *linked* against might be OpenSSL 1.1.1 + * and therefore supports TLS 1.3. This needs to be checked at runtime + * since we can be compiled against 1.1.0 and then the library can be + * upgraded to 1.1.1. + * We only need to check this for OpenSSL versions that can be + * upgraded to 1.1.1 without recompile (>= 1.1.0) + */ + if (OpenSSL_version_num() >= 0x1010100fL) + { + return TLS_VER_1_3; + } + else + { + return TLS_VER_1_2; + } #elif defined(TLS1_2_VERSION) || defined(SSL_OP_NO_TLSv1_2) return TLS_VER_1_2; #elif defined(TLS1_1_VERSION) || defined(SSL_OP_NO_TLSv1_1) return TLS_VER_1_1; -#else +#else /* if defined(TLS1_3_VERSION) */ return TLS_VER_1_0; #endif } @@ -236,12 +272,25 @@ openssl_tls_version(int ver) { return TLS1_2_VERSION; } -#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) else if (ver == TLS_VER_1_3) { + /* + * Supporting the library upgraded to TLS1.3 without recompile + * is enough to support here with a simple constant that the same + * as in the TLS 1.3, so spec it is very unlikely that OpenSSL + * will change this constant + */ +#ifndef TLS1_3_VERSION + /* + * We do not want to define TLS_VER_1_3 if not defined + * since other parts of the code use the existance of this macro + * as proxy for TLS 1.3 support + */ + return 0x0304; +#else return TLS1_3_VERSION; - } #endif + } return 0; } @@ -280,18 +329,12 @@ 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 */ long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET; #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE sslopt |= SSL_OP_CIPHER_SERVER_PREFERENCE; #endif -#ifdef SSL_OP_NO_COMPRESSION - /* Disable compression - flag not available in OpenSSL 0.9.8 */ sslopt |= SSL_OP_NO_COMPRESSION; -#endif SSL_CTX_set_options(ctx->ctx, sslopt); @@ -307,17 +350,16 @@ tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) SSL_CTX_set_default_passwd_cb(ctx->ctx, pem_password_callback); /* Require peer certificate verification */ -#if P2MP_SERVER + int verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) { - flags = 0; + verify_flags = 0; } else if (ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) { - flags = SSL_VERIFY_PEER; + verify_flags = SSL_VERIFY_PEER; } -#endif - SSL_CTX_set_verify(ctx->ctx, flags, verify_callback); + SSL_CTX_set_verify(ctx->ctx, verify_flags, verify_callback); SSL_CTX_set_info_callback(ctx->ctx, info_callback); @@ -325,28 +367,8 @@ tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) } void -tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) +convert_tls_list_to_openssl(char *openssl_ciphers, size_t len,const char *ciphers) { - if (ciphers == NULL) - { - /* Use sane default TLS cipher list */ - if (!SSL_CTX_set_cipher_list(ctx->ctx, - /* Use openssl's default list as a basis */ - "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; @@ -355,12 +377,9 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) const tls_cipher_name_pair *cipher_pair; - char openssl_ciphers[4096]; size_t openssl_ciphers_len = 0; openssl_ciphers[0] = '\0'; - ASSERT(NULL != ctx); - /* Translate IANA cipher suite names to OpenSSL names */ begin_of_cipher = end_of_cipher = 0; for (; begin_of_cipher < strlen(ciphers); begin_of_cipher = end_of_cipher) @@ -397,11 +416,11 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) /* Make sure new cipher name fits in cipher string */ if ((SIZE_MAX - openssl_ciphers_len) < current_cipher_len - || ((sizeof(openssl_ciphers)-1) < openssl_ciphers_len + current_cipher_len)) + || (len - 1) < (openssl_ciphers_len + current_cipher_len)) { msg(M_FATAL, "Failed to set restricted TLS cipher list, too long (>%d).", - (int)sizeof(openssl_ciphers)-1); + (int)(len - 1)); } /* Concatenate cipher name to OpenSSL cipher string */ @@ -417,6 +436,35 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) { openssl_ciphers[openssl_ciphers_len-1] = '\0'; } +} + +void +tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) +{ + if (ciphers == NULL) + { + /* Use sane default TLS cipher list */ + if (!SSL_CTX_set_cipher_list(ctx->ctx, + /* Use openssl's default list as a basis */ + "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; + } + + char openssl_ciphers[4096]; + convert_tls_list_to_openssl(openssl_ciphers, sizeof(openssl_ciphers), ciphers); + + ASSERT(NULL != ctx); /* Set OpenSSL cipher list */ if (!SSL_CTX_set_cipher_list(ctx->ctx, openssl_ciphers)) @@ -462,10 +510,10 @@ tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers) return; } -#if (OPENSSL_VERSION_NUMBER < 0x1010100fL) || !defined(TLS1_3_VERSION) || defined(OPENSSL_NO_TLS1_3) - crypto_msg(M_WARN, "Not compiled with OpenSSL 1.1.1 or higher, or without TLS 1.3 support. " - "Ignoring TLS 1.3 only tls-ciphersuites '%s' setting.", - ciphers); +#if !defined(TLS1_3_VERSION) + crypto_msg(M_WARN, "Not compiled with OpenSSL 1.1.1 or higher. " + "Ignoring TLS 1.3 only tls-ciphersuites '%s' setting.", + ciphers); #else ASSERT(NULL != ctx); @@ -506,13 +554,64 @@ tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile) { msg(M_FATAL, "ERROR: Invalid cert profile: %s", profile); } -#else +#else /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */ if (profile) { - msg(M_WARN, "WARNING: OpenSSL 1.0.1 does not support --tls-cert-profile" + msg(M_WARN, "WARNING: OpenSSL 1.0.2 does not support --tls-cert-profile" ", ignoring user-set profile: '%s'", profile); } -#endif +#endif /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */ +} + +void +tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups) +{ + ASSERT(ctx); + struct gc_arena gc = gc_new(); + /* This method could be as easy as + * SSL_CTX_set1_groups_list(ctx->ctx, groups) + * but OpenSSL does not like the name secp256r1 for prime256v1 + * This is one of the important curves. + * To support the same name for OpenSSL and mbedTLS, we do + * this dance. + */ + + int groups_count = get_num_elements(groups, ':'); + + int *glist; + /* Allocate an array for them */ + ALLOC_ARRAY_CLEAR_GC(glist, int, groups_count, &gc); + + /* Parse allowed ciphers, getting IDs */ + int glistlen = 0; + char *tmp_groups = string_alloc(groups, &gc); + + const char *token; + while ((token = strsep(&tmp_groups, ":"))) + { + if (streq(token, "secp256r1")) + { + token = "prime256v1"; + } + int nid = OBJ_sn2nid(token); + + if (nid == 0) + { + msg(M_WARN, "Warning unknown curve/group specified: %s", token); + } + else + { + glist[glistlen] = nid; + glistlen++; + } + } + + if (!SSL_CTX_set1_groups(ctx->ctx, glist, glistlen)) + { + crypto_msg(M_FATAL, "Failed to set allowed TLS group list: %s", + groups); + } + gc_free(&gc); } void @@ -523,18 +622,11 @@ tls_ctx_check_cert_time(const struct tls_root_ctx *ctx) ASSERT(ctx); -#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) - /* OpenSSL 1.0.2 and up */ cert = SSL_CTX_get0_certificate(ctx->ctx); -#else - /* OpenSSL 1.0.1 and earlier need an SSL object to get at the certificate */ - SSL *ssl = SSL_new(ctx->ctx); - cert = SSL_get_certificate(ssl); -#endif if (cert == NULL) { - goto cleanup; /* Nothing to check if there is no certificate */ + return; /* Nothing to check if there is no certificate */ } ret = X509_cmp_time(X509_get0_notBefore(cert), NULL); @@ -556,27 +648,20 @@ tls_ctx_check_cert_time(const struct tls_root_ctx *ctx) { msg(M_WARN, "WARNING: Your certificate has expired!"); } - -cleanup: -#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) - SSL_free(ssl); -#endif - return; } void tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file, - const char *dh_file_inline - ) + bool dh_file_inline) { DH *dh; BIO *bio; ASSERT(NULL != ctx); - if (!strcmp(dh_file, INLINE_FILE_TAG) && dh_file_inline) + if (dh_file_inline) { - if (!(bio = BIO_new_mem_buf((char *)dh_file_inline, -1))) + if (!(bio = BIO_new_mem_buf((char *)dh_file, -1))) { crypto_msg(M_FATAL, "Cannot open memory BIO for inline DH parameters"); } @@ -595,7 +680,8 @@ tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file, if (!dh) { - crypto_msg(M_FATAL, "Cannot load DH parameters from %s", dh_file); + crypto_msg(M_FATAL, "Cannot load DH parameters from %s", + print_key_filename(dh_file, dh_file_inline)); } if (!SSL_CTX_set_tmp_dh(ctx->ctx, dh)) { @@ -628,7 +714,6 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name } else { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L #if (OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter @@ -639,29 +724,6 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name * so do nothing */ #endif return; -#else - /* For older OpenSSL we have to extract the curve from key on our own */ - 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_new(ctx->ctx); - if (!ssl) - { - crypto_msg(M_FATAL, "SSL_new failed"); - } - pkey = SSL_get_privatekey(ssl); - SSL_free(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); - } -#endif } /* Translate NID back to name , just for kicks */ @@ -699,9 +761,7 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name int tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, - const char *pkcs12_file_inline, - bool load_ca_file - ) + bool pkcs12_file_inline, bool load_ca_file) { FILE *fp; EVP_PKEY *pkey; @@ -713,11 +773,11 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, ASSERT(NULL != ctx); - if (!strcmp(pkcs12_file, INLINE_FILE_TAG) && pkcs12_file_inline) + if (pkcs12_file_inline) { BIO *b64 = BIO_new(BIO_f_base64()); - BIO *bio = BIO_new_mem_buf((void *) pkcs12_file_inline, - (int) strlen(pkcs12_file_inline)); + BIO *bio = BIO_new_mem_buf((void *) pkcs12_file, + (int) strlen(pkcs12_file)); ASSERT(b64 && bio); BIO_push(b64, bio); p12 = d2i_PKCS12_bio(b64, NULL); @@ -873,28 +933,19 @@ tls_ctx_add_extra_certs(struct tls_root_ctx *ctx, BIO *bio, bool optional) } } -/* Like tls_ctx_load_cert, but returns a copy of the certificate in **X509 */ -static void -tls_ctx_load_cert_file_and_copy(struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline, X509 **x509 - ) +void +tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, + bool cert_file_inline) { BIO *in = NULL; X509 *x = NULL; int ret = 0; - bool inline_file = false; ASSERT(NULL != ctx); - if (NULL != x509) - { - ASSERT(NULL == *x509); - } - - inline_file = (strcmp(cert_file, INLINE_FILE_TAG) == 0); - if (inline_file && cert_file_inline) + if (cert_file_inline) { - in = BIO_new_mem_buf((char *)cert_file_inline, -1); + in = BIO_new_mem_buf((char *) cert_file, -1); } else { @@ -925,7 +976,7 @@ tls_ctx_load_cert_file_and_copy(struct tls_root_ctx *ctx, end: if (!ret) { - if (inline_file) + if (cert_file_inline) { crypto_msg(M_FATAL, "Cannot load inline certificate file"); } @@ -943,27 +994,15 @@ end: { BIO_free(in); } - if (x509) - { - *x509 = x; - } - else if (x) + if (x) { X509_free(x); } } -void -tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, - const char *cert_file_inline) -{ - tls_ctx_load_cert_file_and_copy(ctx, cert_file, cert_file_inline, NULL); -} - int tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, - const char *priv_key_file_inline - ) + bool priv_key_file_inline) { SSL_CTX *ssl_ctx = NULL; BIO *in = NULL; @@ -974,9 +1013,9 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, ssl_ctx = ctx->ctx; - if (!strcmp(priv_key_file, INLINE_FILE_TAG) && priv_key_file_inline) + if (priv_key_file_inline) { - in = BIO_new_mem_buf((char *)priv_key_file_inline, -1); + in = BIO_new_mem_buf((char *) priv_key_file, -1); } else { @@ -991,6 +1030,11 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, pkey = PEM_read_bio_PrivateKey(in, NULL, SSL_CTX_get_default_passwd_cb(ctx->ctx), SSL_CTX_get_default_passwd_cb_userdata(ctx->ctx)); + if (!pkey) + { + pkey = engine_load_key(priv_key_file, ctx->ctx); + } + if (!pkey || !SSL_CTX_use_PrivateKey(ssl_ctx, pkey)) { #ifdef ENABLE_MANAGEMENT @@ -999,7 +1043,8 @@ tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, management_auth_failure(management, UP_TYPE_PRIVATE_KEY, NULL); } #endif - crypto_msg(M_WARN, "Cannot load private key file %s", priv_key_file); + crypto_msg(M_WARN, "Cannot load private key file %s", + print_key_filename(priv_key_file, priv_key_file_inline)); goto end; } @@ -1024,7 +1069,7 @@ end: void backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, - const char *crl_inline) + bool crl_inline) { BIO *in = NULL; @@ -1051,9 +1096,9 @@ backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, 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) + if (crl_inline) { - in = BIO_new_mem_buf((char *)crl_inline, -1); + in = BIO_new_mem_buf((char *) crl_file, -1); } else { @@ -1062,7 +1107,8 @@ backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, if (in == NULL) { - msg(M_WARN, "CRL: cannot read: %s", crl_file); + msg(M_WARN, "CRL: cannot read: %s", + print_key_filename(crl_file, crl_inline)); goto end; } @@ -1084,14 +1130,16 @@ backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, break; } - crypto_msg(M_WARN, "CRL: cannot read CRL from file %s", crl_file); + crypto_msg(M_WARN, "CRL: cannot read CRL from file %s", + print_key_filename(crl_file, crl_inline)); break; } if (!X509_STORE_add_crl(store, crl)) { X509_CRL_free(crl); - crypto_msg(M_WARN, "CRL: cannot add %s to store", crl_file); + crypto_msg(M_WARN, "CRL: cannot add %s to store", + print_key_filename(crl_file, crl_inline)); break; } X509_CRL_free(crl); @@ -1103,7 +1151,7 @@ end: } -#ifdef MANAGMENT_EXTERNAL_KEY +#ifdef ENABLE_MANAGEMENT /* encrypt */ static int @@ -1133,7 +1181,7 @@ rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i static int openvpn_extkey_rsa_finish(RSA *rsa) { - /* meth was allocated in tls_ctx_use_external_private_key() ; since + /* meth was allocated in tls_ctx_use_management_external_key() ; since * this function is called when the parent RSA object is destroyed, * it is no longer used after this point so kill it. */ const RSA_METHOD *meth = RSA_get_method(rsa); @@ -1141,74 +1189,93 @@ openvpn_extkey_rsa_finish(RSA *rsa) return 1; } -/* sign arbitrary data */ +/* + * Convert OpenSSL's constant to the strings used in the management + * interface query + */ +const char * +get_rsa_padding_name(const int padding) +{ + switch (padding) + { + case RSA_PKCS1_PADDING: + return "RSA_PKCS1_PADDING"; + + case RSA_NO_PADDING: + return "RSA_NO_PADDING"; + + default: + return "UNKNOWN"; + } +} + +/** + * Pass the input hash in 'dgst' to management and get the signature back. + * + * @param dgst hash to be signed + * @param dgstlen len of data in dgst + * @param sig On successful return signature is in sig. + * @param siglen length of buffer sig + * @param algorithm padding/hashing algorithm for the signature + * + * @return signature length or -1 on error. + */ static int -rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) +get_sig_from_man(const unsigned char *dgst, unsigned int dgstlen, + unsigned char *sig, unsigned int siglen, + const char *algorithm) { - /* optional app data in rsa->meth->app_data; */ char *in_b64 = NULL; char *out_b64 = NULL; - int ret = -1; - int len; + int len = -1; - if (padding != RSA_PKCS1_PADDING) - { - RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); - goto done; - } + int bencret = openvpn_base64_encode(dgst, dgstlen, &in_b64); - /* convert 'from' to base64 */ - if (openvpn_base64_encode(from, flen, &in_b64) <= 0) + if (management && bencret > 0) { - goto done; - } + out_b64 = management_query_pk_sig(management, in_b64, algorithm); - /* call MI for signature */ - if (management) - { - out_b64 = management_query_rsa_sig(management, in_b64); } - if (!out_b64) + if (out_b64) { - goto done; + len = openvpn_base64_decode(out_b64, sig, siglen); } - /* decode base64 signature to binary */ - len = RSA_size(rsa); - ret = openvpn_base64_decode(out_b64, to, len); + free(in_b64); + free(out_b64); + return len; +} - /* verify length */ - if (ret != len) - { - ret = -1; - } +/* sign arbitrary data */ +static int +rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, + int padding) +{ + unsigned int len = RSA_size(rsa); + int ret = -1; -done: - if (in_b64) + if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING) { - free(in_b64); - } - if (out_b64) - { - free(out_b64); + RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); + return -1; } - return ret; + + ret = get_sig_from_man(from, flen, to, len, get_rsa_padding_name(padding)); + + return (ret == len) ? ret : -1; } -int -tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, - const char *cert_file, const char *cert_file_inline) +static int +tls_ctx_use_external_rsa_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) { RSA *rsa = NULL; RSA *pub_rsa; RSA_METHOD *rsa_meth; - X509 *cert = NULL; ASSERT(NULL != ctx); - tls_ctx_load_cert_file_and_copy(ctx, cert_file, cert_file_inline, &cert); - - ASSERT(NULL != cert); + pub_rsa = EVP_PKEY_get0_RSA(pkey); + ASSERT(NULL != pub_rsa); /* allocate custom RSA method object */ rsa_meth = RSA_meth_new("OpenVPN external private key RSA Method", @@ -1230,18 +1297,6 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, goto err; } - /* get the public key */ - EVP_PKEY *pkey = X509_get0_pubkey(cert); - ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */ - pub_rsa = EVP_PKEY_get0_RSA(pkey); - - /* Certificate might not be RSA but DSA or EC */ - if (!pub_rsa) - { - crypto_msg(M_WARN, "management-external-key requires a RSA certificate"); - goto err; - } - /* initialize RSA object */ const BIGNUM *n = NULL; const BIGNUM *e = NULL; @@ -1250,8 +1305,10 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); if (!RSA_set_method(rsa, rsa_meth)) { + RSA_meth_free(rsa_meth); goto err; } + /* from this point rsa_meth will get freed with rsa */ /* bind our custom RSA object to ssl_ctx */ if (!SSL_CTX_use_RSAPrivateKey(ctx->ctx, rsa)) @@ -1259,15 +1316,10 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, goto err; } - X509_free(cert); RSA_free(rsa); /* doesn't necessarily free, just decrements refcount */ - return 0; + return 1; err: - if (cert) - { - X509_free(cert); - } if (rsa) { RSA_free(rsa); @@ -1279,11 +1331,195 @@ err: RSA_meth_free(rsa_meth); } } - crypto_msg(M_FATAL, "Cannot enable SSL external private key capability"); + return 0; +} + +#if ((OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) \ + || LIBRESSL_VERSION_NUMBER > 0x2090000fL) \ + && !defined(OPENSSL_NO_EC) + +/* called when EC_KEY is destroyed */ +static void +openvpn_extkey_ec_finish(EC_KEY *ec) +{ + /* release the method structure */ + const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec); + EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth); +} + +/* EC_KEY_METHOD callback: sign(). + * Sign the hash using EC key and return DER encoded signature in sig, + * its length in siglen. Return value is 1 on success, 0 on error. + */ +static int +ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, + unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) +{ + int capacity = ECDSA_size(ec); + /* + * ECDSA does not seem to have proper constants for paddings since + * there are only signatures without padding at the moment, use + * a generic ECDSA for the moment + */ + int len = get_sig_from_man(dgst, dgstlen, sig, capacity, "ECDSA"); + + if (len > 0) + { + *siglen = len; + return 1; + } + return 0; +} + +/* EC_KEY_METHOD callback: sign_setup(). We do no precomputations */ +static int +ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) +{ return 1; } -#endif /* ifdef MANAGMENT_EXTERNAL_KEY */ +/* EC_KEY_METHOD callback: sign_sig(). + * Sign the hash and return the result as a newly allocated ECDS_SIG + * struct or NULL on error. + */ +static ECDSA_SIG * +ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, const BIGNUM *in_kinv, + const BIGNUM *in_r, EC_KEY *ec) +{ + ECDSA_SIG *ecsig = NULL; + unsigned int len = ECDSA_size(ec); + struct gc_arena gc = gc_new(); + + unsigned char *buf = gc_malloc(len, false, &gc); + if (ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec) != 1) + { + goto out; + } + /* const char ** should be avoided: not up to us, so we cast our way through */ + ecsig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&buf, len); + +out: + gc_free(&gc); + return ecsig; +} + +static int +tls_ctx_use_external_ec_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) +{ + EC_KEY *ec = NULL; + EVP_PKEY *privkey = NULL; + EC_KEY_METHOD *ec_method; + + ASSERT(ctx); + + ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (!ec_method) + { + goto err; + } + + /* Among init methods, we only need the finish method */ + EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish, NULL, NULL, NULL, NULL); + EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); + + ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) + { + EC_KEY_METHOD_free(ec_method); + goto err; + } + if (!EC_KEY_set_method(ec, ec_method)) + { + EC_KEY_METHOD_free(ec_method); + goto err; + } + /* from this point ec_method will get freed when ec is freed */ + + privkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) + { + goto err; + } + /* from this point ec will get freed when privkey is freed */ + + if (!SSL_CTX_use_PrivateKey(ctx->ctx, privkey)) + { + ec = NULL; /* avoid double freeing it below */ + goto err; + } + + EVP_PKEY_free(privkey); /* this will down ref privkey and ec */ + return 1; + +err: + /* Reach here only when ec and privkey can be independenly freed */ + if (privkey) + { + EVP_PKEY_free(privkey); + } + if (ec) + { + EC_KEY_free(ec); + } + return 0; +} +#endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ + +int +tls_ctx_use_management_external_key(struct tls_root_ctx *ctx) +{ + int ret = 1; + + ASSERT(NULL != ctx); + + X509 *cert = SSL_CTX_get0_certificate(ctx->ctx); + + ASSERT(NULL != cert); + + /* get the public key */ + EVP_PKEY *pkey = X509_get0_pubkey(cert); + ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */ + + if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) + { + if (!tls_ctx_use_external_rsa_key(ctx, pkey)) + { + goto cleanup; + } + } +#if ((OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) \ + || LIBRESSL_VERSION_NUMBER > 0x2090000fL) \ + && !defined(OPENSSL_NO_EC) + else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) + { + if (!tls_ctx_use_external_ec_key(ctx, pkey)) + { + goto cleanup; + } + } + else + { + crypto_msg(M_WARN, "management-external-key requires an RSA or EC certificate"); + goto cleanup; + } +#else /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ + else + { + crypto_msg(M_WARN, "management-external-key requires an RSA certificate"); + goto cleanup; + } +#endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ + + ret = 0; +cleanup: + if (ret) + { + crypto_msg(M_FATAL, "Cannot enable SSL external private key capability"); + } + return ret; +} + +#endif /* ifdef ENABLE_MANAGEMENT */ static int sk_x509_name_cmp(const X509_NAME *const *a, const X509_NAME *const *b) @@ -1293,9 +1529,7 @@ sk_x509_name_cmp(const X509_NAME *const *a, const X509_NAME *const *b) void tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, - const char *ca_file_inline, - const char *ca_path, bool tls_server - ) + bool ca_file_inline, const char *ca_path, bool tls_server) { STACK_OF(X509_INFO) *info_stack = NULL; STACK_OF(X509_NAME) *cert_names = NULL; @@ -1316,9 +1550,9 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, /* Try to add certificates and CRLs from ca_file */ if (ca_file) { - if (!strcmp(ca_file, INLINE_FILE_TAG) && ca_file_inline) + if (ca_file_inline) { - in = BIO_new_mem_buf((char *)ca_file_inline, -1); + in = BIO_new_mem_buf((char *)ca_file, -1); } else { @@ -1390,11 +1624,11 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, { crypto_msg(M_WARN, "Cannot load CA certificate file %s (entry %d did not validate)", - np(ca_file), added); + print_key_filename(ca_file, ca_file_inline), + added); } prev = cnum; } - } sk_X509_INFO_pop_free(info_stack, X509_INFO_free); } @@ -1408,7 +1642,7 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, { crypto_msg(M_FATAL, "Cannot load CA certificate file %s (no entries were read)", - np(ca_file)); + print_key_filename(ca_file, ca_file_inline)); } if (tls_server) @@ -1418,7 +1652,8 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, { crypto_msg(M_FATAL, "Cannot load CA certificate file %s (only %d " "of %d entries were valid X509 names)", - np(ca_file), cnum, added); + print_key_filename(ca_file, ca_file_inline), cnum, + added); } } @@ -1446,13 +1681,12 @@ tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, void tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, - const char *extra_certs_file_inline - ) + bool extra_certs_file_inline) { BIO *in; - if (!strcmp(extra_certs_file, INLINE_FILE_TAG) && extra_certs_file_inline) + if (extra_certs_file_inline) { - in = BIO_new_mem_buf((char *)extra_certs_file_inline, -1); + in = BIO_new_mem_buf((char *)extra_certs_file, -1); } else { @@ -1461,7 +1695,10 @@ tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, if (in == NULL) { - crypto_msg(M_FATAL, "Cannot load extra-certs file: %s", extra_certs_file); + crypto_msg(M_FATAL, "Cannot load extra-certs file: %s", + print_key_filename(extra_certs_file, + extra_certs_file_inline)); + } else { @@ -1529,8 +1766,8 @@ bio_debug_data(const char *mode, BIO *bio, const uint8_t *buf, int len, const ch if (len > 0) { open_biofp(); - fprintf(biofp, "BIO_%s %s time=" time_format " bio=" ptr_format " len=%d data=%s\n", - mode, desc, time(NULL), (ptr_type)bio, len, format_hex(buf, len, 0, &gc)); + fprintf(biofp, "BIO_%s %s time=%" PRIi64 " bio=" ptr_format " len=%d data=%s\n", + mode, desc, (int64_t)time(NULL), (ptr_type)bio, len, format_hex(buf, len, 0, &gc)); fflush(biofp); } gc_free(&gc); @@ -1540,8 +1777,8 @@ static void bio_debug_oc(const char *mode, BIO *bio) { open_biofp(); - fprintf(biofp, "BIO %s time=" time_format " bio=" ptr_format "\n", - mode, time(NULL), (ptr_type)bio); + fprintf(biofp, "BIO %s time=%" PRIi64 " bio=" ptr_format "\n", + mode, (int64_t)time(NULL), (ptr_type)bio); fflush(biofp); } @@ -1848,7 +2085,7 @@ print_details(struct key_state_ssl *ks_ssl, const char *prefix) { EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey); const EC_GROUP *group = EC_KEY_get0_group(ec); - const char* curve; + const char *curve; int nid = EC_GROUP_get_curve_name(group); if (nid == 0 || (curve = OBJ_nid2sn(nid)) == NULL) @@ -1873,7 +2110,7 @@ print_details(struct key_state_ssl *ks_ssl, const char *prefix) void show_available_tls_ciphers_list(const char *cipher_list, const char *tls_cert_profile, - const bool tls13) + bool tls13) { struct tls_root_ctx tls_ctx; @@ -1883,10 +2120,11 @@ show_available_tls_ciphers_list(const char *cipher_list, crypto_msg(M_FATAL, "Cannot create SSL_CTX object"); } -#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL) && defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) +#if defined(TLS1_3_VERSION) if (tls13) { - SSL_CTX_set_min_proto_version(tls_ctx.ctx, TLS1_3_VERSION); + SSL_CTX_set_min_proto_version(tls_ctx.ctx, + openssl_tls_version(TLS_VER_1_3)); tls_ctx_restrict_ciphers_tls13(&tls_ctx, cipher_list); } else @@ -1904,12 +2142,13 @@ show_available_tls_ciphers_list(const char *cipher_list, crypto_msg(M_FATAL, "Cannot create SSL object"); } -#if (OPENSSL_VERSION_NUMBER < 0x1010000fL) +#if (OPENSSL_VERSION_NUMBER < 0x1010000fL) \ + || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER <= 0x2090000fL) STACK_OF(SSL_CIPHER) *sk = SSL_get_ciphers(ssl); #else STACK_OF(SSL_CIPHER) *sk = SSL_get1_supported_ciphers(ssl); #endif - for (int i=0;i < sk_SSL_CIPHER_num(sk);i++) + for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) { const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i); @@ -1920,7 +2159,7 @@ show_available_tls_ciphers_list(const char *cipher_list, if (tls13) { - printf("%s\n", cipher_name); + printf("%s\n", cipher_name); } else if (NULL == pair) { @@ -1947,6 +2186,8 @@ show_available_tls_ciphers_list(const char *cipher_list, void show_available_curves(void) { + printf("Consider using openssl 'ecparam -list_curves' as\n" + "alternative to running this command.\n"); #ifndef OPENSSL_NO_EC EC_builtin_curve *curves = NULL; size_t crv_len = 0; @@ -1956,7 +2197,7 @@ show_available_curves(void) ALLOC_ARRAY(curves, EC_builtin_curve, crv_len); if (EC_get_builtin_curves(curves, crv_len)) { - printf("Available Elliptic curves:\n"); + printf("\nAvailable Elliptic curves/groups:\n"); for (n = 0; n < crv_len; n++) { const char *sname; @@ -2008,7 +2249,7 @@ get_highest_preference_tls_cipher(char *buf, int size) const char * get_ssl_library_version(void) { - return SSLeay_version(SSLEAY_VERSION); + return OpenSSL_version(OPENSSL_VERSION); } -#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) */ +#endif /* defined(ENABLE_CRYPTO_OPENSSL) */ diff --git a/src/openvpn/ssl_openssl.h b/src/openvpn/ssl_openssl.h index dabb941..835878c 100644 --- a/src/openvpn/ssl_openssl.h +++ b/src/openvpn/ssl_openssl.h @@ -32,17 +32,6 @@ #include <openssl/ssl.h> /** - * 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 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_verify.c b/src/openvpn/ssl_verify.c index c7e595e..97ccb93 100644 --- a/src/openvpn/ssl_verify.c +++ b/src/openvpn/ssl_verify.c @@ -34,40 +34,26 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - -#include "misc.h" +#include "base64.h" #include "manage.h" #include "otime.h" -#include "base64.h" +#include "run_command.h" #include "ssl_verify.h" #include "ssl_verify_backend.h" #ifdef ENABLE_CRYPTO_OPENSSL #include "ssl_verify_openssl.h" #endif +#include "auth_token.h" +#include "push.h" /** Maximum length of common name */ #define TLS_USERNAME_LEN 64 -/** Legal characters in an X509 name with --compat-names */ -#define X509_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH|CC_COLON|CC_EQUAL) - -/** Legal characters in a common name with --compat-names */ -#define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH) - static void -string_mod_remap_name(char *str, const unsigned int restrictive_flags) +string_mod_remap_name(char *str) { - if (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NAMES) - && !compat_flag(COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING)) - { - string_mod(str, restrictive_flags, 0, '_'); - } - else - { - string_mod(str, CC_PRINT, CC_CRLF, '_'); - } + string_mod(str, CC_PRINT, CC_CRLF, '_'); } /* @@ -79,28 +65,6 @@ setenv_untrusted(struct tls_session *session) setenv_link_socket_actual(session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT); } - -/** - * Wipes the authentication token out of the memory, frees and cleans up related buffers and flags - * - * @param multi Pointer to a multi object holding the auth_token variables - */ -static void -wipe_auth_token(struct tls_multi *multi) -{ - if(multi) - { - if (multi->auth_token) - { - secure_memzero(multi->auth_token, AUTH_TOKEN_SIZE); - free(multi->auth_token); - } - multi->auth_token = NULL; - multi->auth_token_sent = false; - } -} - - /* * Remove authenticated state from all sessions in the given tunnel */ @@ -114,7 +78,7 @@ tls_deauthenticate(struct tls_multi *multi) { for (int j = 0; j < KS_SIZE; ++j) { - multi->session[i].key[j].authenticated = false; + multi->session[i].key[j].authenticated = KS_AUTH_FALSE; } } } @@ -524,7 +488,7 @@ verify_cert_call_plugin(const struct plugin_list *plugins, struct env_set *es, ret = plugin_call_ssl(plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, es, cert_depth, cert); - argv_reset(&argv); + argv_free(&argv); if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS) { @@ -549,9 +513,9 @@ verify_cert_export_cert(openvpn_x509_cert_t *peercert, const char *tmp_dir, stru /* create tmp file to store peer cert */ if (!tmp_dir - || !(peercert_filename = create_temp_file(tmp_dir, "pcf", gc))) + || !(peercert_filename = platform_create_temp_file(tmp_dir, "pcf", gc))) { - msg (M_WARN, "Failed to create peer cert file"); + msg(M_NONFATAL, "Failed to create peer cert file"); return NULL; } @@ -559,13 +523,16 @@ verify_cert_export_cert(openvpn_x509_cert_t *peercert, const char *tmp_dir, stru peercert_file = fopen(peercert_filename, "w+"); if (!peercert_file) { - msg(M_ERR, "Failed to open temporary file : %s", peercert_filename); + msg(M_NONFATAL|M_ERRNO, "Failed to open temporary file: %s", + peercert_filename); return NULL; } if (SUCCESS != x509_write_pem(peercert_file, peercert)) { - msg(M_ERR, "Error writing PEM file containing certificate"); + msg(M_NONFATAL, "Error writing PEM file containing certificate"); + (void) platform_unlink(peercert_filename); + peercert_filename = NULL; } fclose(peercert_file); @@ -614,7 +581,7 @@ verify_cert_call_command(const char *verify_command, struct env_set *es, cleanup: gc_free(&gc); - argv_reset(&argv); + argv_free(&argv); if (ret) { @@ -632,7 +599,8 @@ cleanup: * check peer cert against CRL directory */ static result_t -verify_check_crl_dir(const char *crl_dir, openvpn_x509_cert_t *cert) +verify_check_crl_dir(const char *crl_dir, openvpn_x509_cert_t *cert, + const char *subject, int cert_depth) { result_t ret = FAILURE; char fn[256]; @@ -640,6 +608,12 @@ verify_check_crl_dir(const char *crl_dir, openvpn_x509_cert_t *cert) struct gc_arena gc = gc_new(); char *serial = backend_x509_get_serial(cert, &gc); + if (!serial) + { + msg(D_HANDSHAKE, "VERIFY CRL: depth=%d, %s, serial number is not available", + cert_depth, subject); + goto cleanup; + } if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", crl_dir, OS_SPECIFIC_DIRSEP, serial)) { @@ -649,7 +623,8 @@ verify_check_crl_dir(const char *crl_dir, openvpn_x509_cert_t *cert) fd = platform_open(fn, O_RDONLY, 0); if (fd >= 0) { - msg(D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial); + msg(D_HANDSHAKE, "VERIFY CRL: depth=%d, %s, serial=%s is revoked", + cert_depth, subject, serial); goto cleanup; } @@ -689,7 +664,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep } /* enforce character class restrictions in X509 name */ - string_mod_remap_name(subject, X509_NAME_CHAR_CLASS); + string_mod_remap_name(subject); string_replace_leading(subject, '-', '_'); /* extract the username (default is CN) */ @@ -709,7 +684,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep } /* enforce character class restrictions in common name */ - string_mod_remap_name(common_name, COMMON_NAME_CHAR_CLASS); + string_mod_remap_name(common_name); /* warn if cert chain is too deep */ if (cert_depth >= MAX_CERT_DEPTH) @@ -725,24 +700,24 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep switch (opt->verify_hash_algo) { - case MD_SHA1: - ca_hash = x509_get_sha1_fingerprint(cert, &gc); - break; - - case MD_SHA256: - ca_hash = x509_get_sha256_fingerprint(cert, &gc); - break; - - default: - /* This should normally not happen at all; the algorithm used - * is parsed by add_option() [options.c] and set to a predefined - * value in an enumerated type. So if this unlikely scenario - * happens, consider this a failure - */ - msg(M_WARN, "Unexpected invalid algorithm used with " - "--verify-hash (%i)", opt->verify_hash_algo); - ret = FAILURE; - goto cleanup; + case MD_SHA1: + ca_hash = x509_get_sha1_fingerprint(cert, &gc); + break; + + case MD_SHA256: + ca_hash = x509_get_sha256_fingerprint(cert, &gc); + break; + + default: + /* This should normally not happen at all; the algorithm used + * is parsed by add_option() [options.c] and set to a predefined + * value in an enumerated type. So if this unlikely scenario + * happens, consider this a failure + */ + msg(M_WARN, "Unexpected invalid algorithm used with " + "--verify-hash (%i)", opt->verify_hash_algo); + ret = FAILURE; + goto cleanup; } if (memcmp(BPTR(&ca_hash), opt->verify_hash, BLEN(&ca_hash))) @@ -791,7 +766,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep { if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR) { - if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert)) + if (SUCCESS != verify_check_crl_dir(opt->crl_file, cert, subject, cert_depth)) { goto cleanup; } @@ -836,9 +811,8 @@ cleanup: #define ACF_FAILED 3 #endif -#ifdef MANAGEMENT_DEF_AUTH void -man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reason) +auth_set_client_reason(struct tls_multi *multi, const char *client_reason) { if (multi->client_reason) { @@ -847,11 +821,12 @@ man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reaso } if (client_reason && strlen(client_reason)) { - /* FIXME: Last alloc will never be freed */ multi->client_reason = string_alloc(client_reason, NULL); } } +#ifdef MANAGEMENT_DEF_AUTH + static inline unsigned int man_def_auth_test(const struct key_state *ks) { @@ -889,7 +864,7 @@ key_state_gen_auth_control_file(struct key_state *ks, const struct tls_options * struct gc_arena gc = gc_new(); key_state_rm_auth_control_file(ks); - const char *acf = create_temp_file(opt->tmp_dir, "acf", &gc); + const char *acf = platform_create_temp_file(opt->tmp_dir, "acf", &gc); if (acf) { ks->auth_control_file = string_alloc(acf, NULL); @@ -983,7 +958,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency) if (DECRYPT_KEY_ENABLED(multi, ks)) { active = true; - if (ks->authenticated) + if (ks->authenticated > KS_AUTH_FALSE) { #ifdef ENABLE_DEF_AUTH unsigned int s1 = ACF_DISABLED; @@ -1000,7 +975,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency) case ACF_SUCCEEDED: case ACF_DISABLED: success = true; - ks->auth_deferred = false; + ks->authenticated = KS_AUTH_TRUE; break; case ACF_UNDEFINED: @@ -1011,7 +986,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency) break; case ACF_FAILED: - ks->authenticated = false; + ks->authenticated = KS_AUTH_FALSE; break; default: @@ -1055,7 +1030,7 @@ tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, con if (multi) { int i; - man_def_auth_set_client_reason(multi, client_reason); + auth_set_client_reason(multi, client_reason); for (i = 0; i < KEY_SCAN_SIZE; ++i) { struct key_state *ks = multi->key_scan[i]; @@ -1085,7 +1060,8 @@ tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, con * Verify the user name and password using a script */ static bool -verify_user_pass_script(struct tls_session *session, const struct user_pass *up) +verify_user_pass_script(struct tls_session *session, struct tls_multi *multi, + const struct user_pass *up) { struct gc_arena gc = gc_new(); struct argv argv = argv_new(); @@ -1102,7 +1078,8 @@ verify_user_pass_script(struct tls_session *session, const struct user_pass *up) { struct status_output *so; - tmp_file = create_temp_file(session->opt->tmp_dir, "up", &gc); + tmp_file = platform_create_temp_file(session->opt->tmp_dir, "up", + &gc); if (tmp_file) { so = status_open(tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); @@ -1133,6 +1110,9 @@ verify_user_pass_script(struct tls_session *session, const struct user_pass *up) /* setenv client real IP address */ setenv_untrusted(session); + /* add auth-token environment */ + add_session_token_env(session, multi, up); + /* format command line */ argv_parse_cmd(&argv, session->opt->auth_user_pass_verify_script); argv_printf_cat(&argv, "%s", tmp_file); @@ -1157,7 +1137,7 @@ done: platform_unlink(tmp_file); } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); return ret; } @@ -1166,7 +1146,8 @@ done: * Verify the username and password using a plugin */ static int -verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, const char *raw_username) +verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi, + const struct user_pass *up) { int retval = OPENVPN_PLUGIN_FUNC_ERROR; #ifdef PLUGIN_DEF_AUTH @@ -1177,7 +1158,7 @@ verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username)) { /* set username/password in private env space */ - setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username)); + setenv_str(session->opt->es, "username", up->username); setenv_str(session->opt->es, "password", up->password); /* setenv incoming cert common name for script */ @@ -1186,13 +1167,15 @@ verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, /* setenv client real IP address */ setenv_untrusted(session); + /* add auth-token environment */ + add_session_token_env(session, multi, up); #ifdef PLUGIN_DEF_AUTH /* generate filename for deferred auth control file */ if (!key_state_gen_auth_control_file(ks, session->opt)) { - msg (D_TLS_ERRORS, "TLS Auth Error (%s): " - "could not create deferred auth control file", __func__); - goto cleanup; + msg(D_TLS_ERRORS, "TLS Auth Error (%s): " + "could not create deferred auth control file", __func__); + return retval; } #endif @@ -1208,17 +1191,12 @@ verify_user_pass_plugin(struct tls_session *session, const struct user_pass *up, #endif setenv_del(session->opt->es, "password"); - if (raw_username) - { - setenv_str(session->opt->es, "username", up->username); - } } else { msg(D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username"); } -cleanup: return retval; } @@ -1233,7 +1211,9 @@ cleanup: #define KMDA_DEF 3 static int -verify_user_pass_management(struct tls_session *session, const struct user_pass *up, const char *raw_username) +verify_user_pass_management(struct tls_session *session, + struct tls_multi *multi, + const struct user_pass *up) { int retval = KMDA_ERROR; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ @@ -1242,7 +1222,7 @@ verify_user_pass_management(struct tls_session *session, const struct user_pass if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username)) { /* set username/password in private env space */ - setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username)); + setenv_str(session->opt->es, "username", up->username); setenv_str(session->opt->es, "password", up->password); /* setenv incoming cert common name for script */ @@ -1251,16 +1231,17 @@ verify_user_pass_management(struct tls_session *session, const struct user_pass /* setenv client real IP address */ setenv_untrusted(session); + /* + * if we are using auth-gen-token, send also the session id of auth gen token to + * allow the management to figure out if it is a new session or a continued one + */ + add_session_token_env(session, multi, up); if (management) { management_notify_client_needing_auth(management, ks->mda_key_id, session->opt->mda_context, session->opt->es); } setenv_del(session->opt->es, "password"); - if (raw_username) - { - setenv_str(session->opt->es, "username", up->username); - } retval = KMDA_SUCCESS; } @@ -1273,8 +1254,12 @@ verify_user_pass_management(struct tls_session *session, const struct user_pass } #endif /* ifdef MANAGEMENT_DEF_AUTH */ + /* * Main username/password verification entry point + * + * Will set session->ks[KS_PRIMARY].authenticated according to + * result of the username/password verification */ void verify_user_pass(struct user_pass *up, struct tls_multi *multi, @@ -1284,9 +1269,6 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, bool s2 = true; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct gc_arena gc = gc_new(); - char *raw_username = NULL; - #ifdef MANAGEMENT_DEF_AUTH int man_def_auth = KMDA_UNDEF; @@ -1296,101 +1278,82 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, } #endif - /* - * Preserve the raw username before string_mod remapping, for plugins - * and management clients when in --compat-names mode - */ - if (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NAMES)) - { - ALLOC_ARRAY_CLEAR_GC(raw_username, char, USER_PASS_LEN, &gc); - strcpy(raw_username, up->username); - string_mod(raw_username, CC_PRINT, CC_CRLF, '_'); - } - /* enforce character class restrictions in username/password */ - string_mod_remap_name(up->username, COMMON_NAME_CHAR_CLASS); + string_mod_remap_name(up->username); 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 + /* + * If auth token succeeds we skip the auth + * methods unless otherwise specified + */ + bool skip_auth = false; + + /* + * If server is configured with --auth-gen-token and the client sends + * something that looks like an authentication token, this * 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) + if (session->opt->auth_token_generate && is_auth_token(up->password)) { - unsigned int ssl_flags = session->opt->ssl_flags; - - /* Ensure that the username has not changed */ - if (!tls_lock_username(multi, up->username)) - { - /* auth-token cleared in tls_lock_username() on failure */ - 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) + multi->auth_token_state_flags = verify_auth_token(up, multi, session); + if (session->opt->auth_token_call_auth) { - msg(D_HANDSHAKE, "Auth-token for client expired\n"); - wipe_auth_token(multi); - ks->authenticated = false; - goto done; + /* + * we do not care about the result here because it is + * the responsibility of the external authentication to + * decide what to do with the result + */ } - - /* The core authentication of the token itself */ - if (memcmp_constant_time(multi->auth_token, up->password, - strlen(multi->auth_token)) != 0) + else if (multi->auth_token_state_flags == AUTH_TOKEN_HMAC_OK) { - 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]" : ""); + /* + * We do not want the EXPIRED or EMPTY USER flags here so check + * for equality with AUTH_TOKEN_HMAC_OK + */ + msg(M_WARN, "TLS: Username/auth-token authentication " + "succeeded for username '%s'", + up->username); + skip_auth = true; } 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]" : ""); + wipe_auth_token(multi); + ks->authenticated = KS_AUTH_FALSE; + msg(M_WARN, "TLS: Username/auth-token authentication " + "failed for username '%s'", up->username); + return; } - goto done; } - /* call plugin(s) and/or script */ -#ifdef MANAGEMENT_DEF_AUTH - if (man_def_auth == KMDA_DEF) + if (!skip_auth) { - man_def_auth = verify_user_pass_management(session, up, raw_username); - } +#ifdef MANAGEMENT_DEF_AUTH + if (man_def_auth==KMDA_DEF) + { + man_def_auth = verify_user_pass_management(session, multi, up); + } #endif - if (plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) - { - s1 = verify_user_pass_plugin(session, up, raw_username); - } - if (session->opt->auth_user_pass_verify_script) - { - s2 = verify_user_pass_script(session, up); + if (plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) + { + s1 = verify_user_pass_plugin(session, multi, up); + } + + if (session->opt->auth_user_pass_verify_script) + { + s2 = verify_user_pass_script(session, multi, up); + } } /* check sizing of username if it will become our common name */ - if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen(up->username) > TLS_USERNAME_LEN) + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) + && strlen(up->username)>TLS_USERNAME_LEN) { - msg(D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN); + msg(D_TLS_ERRORS, + "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", + TLS_USERNAME_LEN); s1 = OPENVPN_PLUGIN_FUNC_ERROR; } - /* auth succeeded? */ if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS #ifdef PLUGIN_DEF_AUTH @@ -1402,67 +1365,76 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, #endif && tls_lock_username(multi, up->username)) { - ks->authenticated = true; + ks->authenticated = KS_AUTH_TRUE; #ifdef PLUGIN_DEF_AUTH if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) { - ks->auth_deferred = true; + ks->authenticated = KS_AUTH_DEFERRED; } #endif #ifdef MANAGEMENT_DEF_AUTH if (man_def_auth != KMDA_UNDEF) { - ks->auth_deferred = true; + ks->authenticated = KS_AUTH_DEFERRED; } #endif + if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)) + { + set_common_name(session, up->username); + } - if ((session->opt->auth_token_generate) && (NULL == multi->auth_token)) + if ((session->opt->auth_token_generate)) { - /* Server is configured with --auth-gen-token but no token has yet - * been generated for this client. Generate one and save it. + /* + * If we accepted a (not expired) token, i.e. + * initial auth via token on new connection, we need + * to store the auth-token in multi->auth_token, so + * the initial timestamp and session id can be extracted from it */ - uint8_t tok[AUTH_TOKEN_SIZE]; - - if (!rand_bytes(tok, AUTH_TOKEN_SIZE)) + if (!multi->auth_token + && (multi->auth_token_state_flags & AUTH_TOKEN_HMAC_OK) + && !(multi->auth_token_state_flags & AUTH_TOKEN_EXPIRED)) { - msg( M_FATAL, "Failed to get enough randomness for " - "authentication token"); + multi->auth_token = strdup(up->password); } - /* The token should be longer than the input when - * being base64 encoded + /* + * Server is configured with --auth-gen-token. Generate or renew + * the token. */ - ASSERT(openvpn_base64_encode(tok, AUTH_TOKEN_SIZE, - &multi->auth_token) > AUTH_TOKEN_SIZE); - multi->auth_token_tstamp = now; - dmsg(D_SHOW_KEYS, "Generated token for client: %s", - multi->auth_token); + generate_auth_token(up, multi); } - - if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)) + /* + * Auth token already sent to client, update auth-token on client. + * The initial auth-token is sent as part of the push message, for this + * update we need to schedule an extra push message. + * + * Otherwise the auth-token get pushed out as part of the "normal" + * push-reply + */ + if (multi->auth_token_initial) { - set_common_name(session, up->username); + /* + * We do not explicitly schedule the sending of the + * control message here but control message are only + * postponed when the control channel is not yet fully + * established and furthermore since this is called in + * the middle of authentication, there are other messages + * (new data channel keys) that are sent anyway and will + * trigger schedueling + */ + send_push_reply_auth_token(multi); } - -#ifdef ENABLE_DEF_AUTH msg(D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s", - ks->auth_deferred ? "deferred" : "succeeded", + (ks->authenticated == KS_AUTH_DEFERRED) ? "deferred" : "succeeded", up->username, (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); -#else - msg(D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s", - "succeeded", - up->username, - (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : ""); -#endif } else { + ks->authenticated = KS_AUTH_FALSE; msg(D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer"); } - -done: - gc_free(&gc); } void @@ -1477,7 +1449,7 @@ verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session) } /* Don't allow the CN to change once it's been locked */ - if (ks->authenticated && multi->locked_cn) + if (ks->authenticated > KS_AUTH_FALSE && multi->locked_cn) { const char *cn = session->common_name; if (cn && strcmp(cn, multi->locked_cn)) @@ -1493,7 +1465,7 @@ verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session) } /* Don't allow the cert hashes to change once they have been locked */ - if (ks->authenticated && multi->locked_cert_hash_set) + if (ks->authenticated > KS_AUTH_FALSE && multi->locked_cert_hash_set) { const struct cert_hash_set *chs = session->cert_hash_set; if (chs && !cert_hash_compare(chs, multi->locked_cert_hash_set)) @@ -1507,15 +1479,16 @@ verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session) } /* verify --client-config-dir based authentication */ - if (ks->authenticated && session->opt->client_config_dir_exclusive) + if (ks->authenticated > KS_AUTH_FALSE && session->opt->client_config_dir_exclusive) { struct gc_arena gc = gc_new(); const char *cn = session->common_name; - const char *path = gen_path(session->opt->client_config_dir_exclusive, cn, &gc); - if (!cn || !strcmp(cn, CCD_DEFAULT) || !test_file(path)) + const char *path = platform_gen_path(session->opt->client_config_dir_exclusive, + cn, &gc); + if (!cn || !strcmp(cn, CCD_DEFAULT) || !platform_test_file(path)) { - ks->authenticated = false; + ks->authenticated = KS_AUTH_FALSE; wipe_auth_token(multi); msg(D_TLS_ERRORS, "TLS Auth Error: --client-config-dir authentication failed for common name '%s' file='%s'", session->common_name, @@ -1541,5 +1514,3 @@ tls_x509_clear_env(struct env_set *es) item = next; } } - -#endif /* ENABLE_CRYPTO */ diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h index 3e2267a..b1ced95 100644 --- a/src/openvpn/ssl_verify.h +++ b/src/openvpn/ssl_verify.h @@ -29,8 +29,6 @@ #ifndef SSL_VERIFY_H_ #define SSL_VERIFY_H_ -#ifdef ENABLE_CRYPTO - #include "syshead.h" #include "misc.h" #include "ssl_common.h" @@ -226,23 +224,24 @@ struct x509_track #ifdef MANAGEMENT_DEF_AUTH bool tls_authenticate_key(struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason); -void man_def_auth_set_client_reason(struct tls_multi *multi, const char *client_reason); - #endif +/** + * Sets the reason why authentication of a client failed. This be will send to the client + * when the AUTH_FAILED message is sent + * An example would be "SESSION: Token expired" + * @param multi The multi tls struct + * @param client_reason The string to send to the client as part of AUTH_FAILED + */ +void auth_set_client_reason(struct tls_multi *multi, const char *client_reason); + static inline const char * tls_client_reason(struct tls_multi *multi) { -#ifdef ENABLE_DEF_AUTH return multi->client_reason; -#else - return NULL; -#endif } /** Remove any X509_ env variables from env_set es */ void tls_x509_clear_env(struct env_set *es); -#endif /* ENABLE_CRYPTO */ - #endif /* SSL_VERIFY_H_ */ diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h index 2a9e8bb..d6b31bf 100644 --- a/src/openvpn/ssl_verify_backend.h +++ b/src/openvpn/ssl_verify_backend.h @@ -130,6 +130,7 @@ result_t backend_x509_get_username(char *common_name, int cn_len, * --x509-username-field option. */ bool x509_username_field_ext_supported(const char *extname); + #endif /* @@ -175,7 +176,7 @@ void x509_setenv(struct env_set *es, int cert_depth, openvpn_x509_cert_t *cert); * * The tracked attributes are stored in ll_head. * - * @param ll_head The x509_track to store tracked atttributes in + * @param ll_head The x509_track to store tracked attributes in * @param name Name of the attribute to track * @param msglevel Message level for errors * @param gc Garbage collection arena for temp data diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c index 2d019ab..9389103 100644 --- a/src/openvpn/ssl_verify_mbedtls.c +++ b/src/openvpn/ssl_verify_mbedtls.c @@ -34,7 +34,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) +#if defined(ENABLE_CRYPTO_MBEDTLS) #include "crypto_mbedtls.h" #include "ssl_verify.h" @@ -68,6 +68,7 @@ verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, int ret = 0; char errstr[512] = { 0 }; char *subject = x509_get_subject(cert, &gc); + char *serial = backend_x509_get_serial(cert, &gc); ret = mbedtls_x509_crt_verify_info(errstr, sizeof(errstr)-1, "", *flags); if (ret <= 0 && !openvpn_snprintf(errstr, sizeof(errstr), @@ -82,8 +83,8 @@ verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, if (subject) { - msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s: %s", - cert_depth, subject, errstr); + msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s, serial=%s: %s", + cert_depth, subject, serial ? serial : "<not available>", errstr); } else { @@ -550,4 +551,4 @@ tls_verify_crl_missing(const struct tls_options *opt) return false; } -#endif /* #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */ +#endif /* #if defined(ENABLE_CRYPTO_MBEDTLS) */ diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c index b1ce06b..454efee 100644 --- a/src/openvpn/ssl_verify_openssl.c +++ b/src/openvpn/ssl_verify_openssl.c @@ -34,7 +34,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO_OPENSSL) #include "ssl_verify_openssl.h" @@ -44,8 +44,9 @@ #include "ssl_verify_backend.h" #include "openssl_compat.h" -#include <openssl/x509v3.h> +#include <openssl/bn.h> #include <openssl/err.h> +#include <openssl/x509v3.h> int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) @@ -70,6 +71,7 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { /* get the X509 name */ char *subject = x509_get_subject(current_cert, &gc); + char *serial = backend_x509_get_serial(current_cert, &gc); if (!subject) { @@ -88,10 +90,10 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx) } /* Remote site specified a certificate, but it's not correct */ - msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s", + msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s, serial=%s", X509_STORE_CTX_get_error_depth(ctx), X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)), - subject); + subject, serial ? serial : "<not available>"); ERR_clear_error(); @@ -113,7 +115,8 @@ cleanup: } #ifdef ENABLE_X509ALTUSERNAME -bool x509_username_field_ext_supported(const char *fieldname) +bool +x509_username_field_ext_supported(const char *fieldname) { int nid = OBJ_txt2nid(fieldname); return nid == NID_subject_alt_name || nid == NID_issuer_alt_name; @@ -331,18 +334,6 @@ x509_get_subject(X509 *cert, struct gc_arena *gc) BUF_MEM *subject_mem; char *subject = NULL; - /* - * Generate the subject string in OpenSSL proprietary format, - * when in --compat-names mode - */ - if (compat_flag(COMPAT_FLAG_QUERY | COMPAT_NAMES)) - { - subject = gc_malloc(256, false, gc); - X509_NAME_oneline(X509_get_subject_name(cert), subject, 256); - subject[255] = '\0'; - return subject; - } - subject_bio = BIO_new(BIO_s_mem()); if (subject_bio == NULL) { @@ -479,8 +470,7 @@ x509_setenv_track(const struct x509_track *xt, struct env_set *es, const int dep 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 */ + unsigned char *buf = NULL; if (ASN1_STRING_to_UTF8(&buf, val) >= 0) { do_setenv_x509(es, xt->name, (char *)buf, depth); @@ -535,7 +525,7 @@ x509_setenv(struct env_set *es, int cert_depth, openvpn_x509_cert_t *peer_cert) ASN1_STRING *val; X509_NAME_ENTRY *ent; const char *objbuf; - unsigned char *buf; + unsigned char *buf = NULL; char *name_expand; size_t name_expand_size; X509_NAME *x509 = X509_get_subject_name(peer_cert); @@ -568,7 +558,6 @@ x509_setenv(struct env_set *es, int cert_depth, openvpn_x509_cert_t *peer_cert) { continue; } - 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) { continue; @@ -600,7 +589,7 @@ x509_verify_ns_cert_type(openvpn_x509_cert_t *peer_cert, const int usage) * prevent it to take a const argument */ result_t result = X509_check_purpose(peer_cert, X509_PURPOSE_SSL_CLIENT, 0) ? - SUCCESS : FAILURE; + SUCCESS : FAILURE; /* * old versions of OpenSSL allow us to make the less strict check we used to @@ -628,7 +617,7 @@ x509_verify_ns_cert_type(openvpn_x509_cert_t *peer_cert, const int usage) * prevent it to take a const argument */ result_t result = X509_check_purpose(peer_cert, X509_PURPOSE_SSL_SERVER, 0) ? - SUCCESS : FAILURE; + SUCCESS : FAILURE; /* * old versions of OpenSSL allow us to make the less strict check we used to @@ -769,7 +758,7 @@ x509_write_pem(FILE *peercert_file, X509 *peercert) { if (PEM_write_X509(peercert_file, peercert) < 0) { - msg(M_ERR, "Failed to write peer certificate in PEM format"); + msg(M_NONFATAL, "Failed to write peer certificate in PEM format"); return FAILURE; } return SUCCESS; @@ -802,4 +791,4 @@ tls_verify_crl_missing(const struct tls_options *opt) return true; } -#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) */ +#endif /* defined(ENABLE_CRYPTO_OPENSSL) */ diff --git a/src/openvpn/status.c b/src/openvpn/status.c index 91391d1..e8dcf7c 100644 --- a/src/openvpn/status.c +++ b/src/openvpn/status.c @@ -146,19 +146,6 @@ status_trigger(struct status_output *so) } } -bool -status_trigger_tv(struct status_output *so, struct timeval *tv) -{ - if (so) - { - return event_timeout_trigger(&so->et, tv, ETT_DEFAULT); - } - else - { - return false; - } -} - void status_reset(struct status_output *so) { diff --git a/src/openvpn/status.h b/src/openvpn/status.h index 2a399d7..66e5bc5 100644 --- a/src/openvpn/status.h +++ b/src/openvpn/status.h @@ -69,8 +69,6 @@ struct status_output *status_open(const char *filename, const struct virtual_output *vout, const unsigned int flags); -bool status_trigger_tv(struct status_output *so, struct timeval *tv); - bool status_trigger(struct status_output *so); void status_reset(struct status_output *so); diff --git a/src/openvpn/syshead.h b/src/openvpn/syshead.h index 3ac9d70..8342eae 100644 --- a/src/openvpn/syshead.h +++ b/src/openvpn/syshead.h @@ -39,6 +39,7 @@ #ifdef _WIN32 #include <windows.h> #include <winsock2.h> +#include <tlhelp32.h> #define sleep(x) Sleep((x)*1000) #define random rand #define srandom srand @@ -47,6 +48,7 @@ #ifdef _MSC_VER /* Visual Studio */ #define __func__ __FUNCTION__ #define __attribute__(x) +#include <inttypes.h> #endif #if defined(__APPLE__) @@ -178,8 +180,8 @@ #include <resolv.h> #endif -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> +#ifdef HAVE_POLL_H +#include <poll.h> #endif #ifdef HAVE_SYS_EPOLL_H @@ -513,22 +515,16 @@ socket_defined(const socket_descriptor_t sd) * Do we have point-to-multipoint capability? */ -#if defined(ENABLE_CRYPTO) && defined(HAVE_GETTIMEOFDAY_NANOSECONDS) +#if defined(HAVE_GETTIMEOFDAY_NANOSECONDS) #define P2MP 1 #else #define P2MP 0 #endif -#if P2MP && !defined(ENABLE_CLIENT_ONLY) -#define P2MP_SERVER 1 -#else -#define P2MP_SERVER 0 -#endif - /* * HTTPS port sharing capability */ -#if defined(ENABLE_PORT_SHARE) && P2MP_SERVER && defined(SCM_RIGHTS) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG) +#if defined(ENABLE_PORT_SHARE) && defined(SCM_RIGHTS) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG) #define PORT_SHARE 1 #else #define PORT_SHARE 0 @@ -537,43 +533,27 @@ socket_defined(const socket_descriptor_t sd) /* * Enable deferred authentication? */ -#if defined(ENABLE_DEF_AUTH) && P2MP_SERVER && defined(ENABLE_PLUGIN) +#if defined(ENABLE_DEF_AUTH) && defined(ENABLE_PLUGIN) #define PLUGIN_DEF_AUTH #endif -#if defined(ENABLE_DEF_AUTH) && P2MP_SERVER && defined(ENABLE_MANAGEMENT) +#if defined(ENABLE_DEF_AUTH) && defined(ENABLE_MANAGEMENT) #define MANAGEMENT_DEF_AUTH #endif #if !defined(PLUGIN_DEF_AUTH) && !defined(MANAGEMENT_DEF_AUTH) #undef ENABLE_DEF_AUTH #endif -/* - * Enable external private key - */ -#if defined(ENABLE_MANAGEMENT) && defined(ENABLE_CRYPTO) -#define MANAGMENT_EXTERNAL_KEY -#endif - -/* Enable mbed TLS RNG prediction resistance support */ #ifdef ENABLE_CRYPTO_MBEDTLS #define ENABLE_PREDICTION_RESISTANCE #endif /* ENABLE_CRYPTO_MBEDTLS */ /* - * MANAGEMENT_IN_EXTRA allows the management interface to - * read multi-line inputs from clients. - */ -#if defined(MANAGEMENT_DEF_AUTH) || defined(MANAGMENT_EXTERNAL_KEY) -#define MANAGEMENT_IN_EXTRA -#endif - -/* * Enable packet filter? */ -#if defined(ENABLE_PF) && P2MP_SERVER && defined(ENABLE_PLUGIN) && defined(HAVE_STAT) +#if defined(ENABLE_PF) && defined(ENABLE_PLUGIN) && defined(HAVE_STAT) #define PLUGIN_PF #endif -#if defined(ENABLE_PF) && P2MP_SERVER && defined(MANAGEMENT_DEF_AUTH) +#if defined(ENABLE_PF) && defined(MANAGEMENT_DEF_AUTH) #define MANAGEMENT_PF #endif #if !defined(PLUGIN_PF) && !defined(MANAGEMENT_PF) @@ -590,39 +570,26 @@ socket_defined(const socket_descriptor_t sd) #endif /* - * Should we include OCC (options consistency check) code? - */ -#define ENABLE_OCC - -/* * Should we include NTLM proxy functionality */ -#if defined(ENABLE_CRYPTO) #define NTLM 1 -#else -#define NTLM 0 -#endif /* * Should we include proxy digest auth functionality */ -#if defined(ENABLE_CRYPTO) #define PROXY_DIGEST_AUTH 1 -#else -#define PROXY_DIGEST_AUTH 0 -#endif /* * Do we have CryptoAPI capability? */ -#if defined(_WIN32) && defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(_WIN32) && defined(ENABLE_CRYPTO_OPENSSL) #define ENABLE_CRYPTOAPI #endif /* * Is poll available on this platform? */ -#if defined(HAVE_POLL) && defined(HAVE_SYS_POLL_H) +#if defined(HAVE_POLL) && defined(HAVE_POLL_H) #define POLL 1 #else #define POLL 0 @@ -666,29 +633,6 @@ socket_defined(const socket_descriptor_t sd) #endif /* - * Do we have the capability to support the AUTO_USERID feature? - */ -#if defined(ENABLE_AUTO_USERID) -#define AUTO_USERID 1 -#else -#define AUTO_USERID 0 -#endif - -/* - * Do we support challenge/response authentication as client? - */ -#if defined(ENABLE_MANAGEMENT) -#define ENABLE_CLIENT_CR -#endif - -/* - * Do we support pushing peer info? - */ -#if defined(ENABLE_CRYPTO) -#define ENABLE_PUSH_PEER_INFO -#endif - -/* * Compression support */ #if defined(ENABLE_LZO) || defined(ENABLE_LZ4) \ diff --git a/src/openvpn/tls_crypt.c b/src/openvpn/tls_crypt.c index ecc654e..7b5016d 100644 --- a/src/openvpn/tls_crypt.c +++ b/src/openvpn/tls_crypt.c @@ -29,12 +29,24 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO +#include "argv.h" +#include "base64.h" #include "crypto.h" +#include "platform.h" +#include "run_command.h" #include "session_id.h" +#include "ssl.h" #include "tls_crypt.h" +const char *tls_crypt_v2_cli_pem_name = "OpenVPN tls-crypt-v2 client key"; +const char *tls_crypt_v2_srv_pem_name = "OpenVPN tls-crypt-v2 server key"; + +/** Metadata contains user-specified data */ +static const uint8_t TLS_CRYPT_METADATA_TYPE_USER = 0x00; +/** Metadata contains a 64-bit unix timestamp in network byte order */ +static const uint8_t TLS_CRYPT_METADATA_TYPE_TIMESTAMP = 0x01; + static struct key_type tls_crypt_kt(void) { @@ -67,14 +79,14 @@ tls_crypt_buf_overhead(void) void tls_crypt_init_key(struct key_ctx_bi *key, const char *key_file, - const char *key_inline, bool tls_server) + bool key_inline, bool tls_server) { const int key_direction = tls_server ? KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; struct key_type kt = tls_crypt_kt(); if (!kt.cipher || !kt.digest) { - msg (M_FATAL, "ERROR: --tls-crypt not supported"); + msg(M_FATAL, "ERROR: --tls-crypt not supported"); } crypto_read_openvpn_key(&kt, key, key_file, key_inline, key_direction, "Control Channel Encryption", "tls-crypt"); @@ -266,4 +278,466 @@ error_exit: return false; } -#endif /* EMABLE_CRYPTO */ +static inline void +tls_crypt_v2_load_client_key(struct key_ctx_bi *key, const struct key2 *key2, + bool tls_server) +{ + const int key_direction = tls_server ? + KEY_DIRECTION_NORMAL : KEY_DIRECTION_INVERSE; + struct key_type kt = tls_crypt_kt(); + if (!kt.cipher || !kt.digest) + { + msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported"); + } + init_key_ctx_bi(key, key2, key_direction, &kt, + "Control Channel Encryption"); +} + +void +tls_crypt_v2_init_client_key(struct key_ctx_bi *key, struct buffer *wkc_buf, + const char *key_file, bool key_inline) +{ + struct buffer client_key = alloc_buf(TLS_CRYPT_V2_CLIENT_KEY_LEN + + TLS_CRYPT_V2_MAX_WKC_LEN); + + if (!read_pem_key_file(&client_key, tls_crypt_v2_cli_pem_name, + key_file, key_inline)) + { + msg(M_FATAL, "ERROR: invalid tls-crypt-v2 client key format"); + } + + struct key2 key2; + if (!buf_read(&client_key, &key2.keys, sizeof(key2.keys))) + { + msg(M_FATAL, "ERROR: not enough data in tls-crypt-v2 client key"); + } + + tls_crypt_v2_load_client_key(key, &key2, false); + secure_memzero(&key2, sizeof(key2)); + + *wkc_buf = client_key; +} + +void +tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, + const char *key_file, bool key_inline) +{ + struct key srv_key; + struct buffer srv_key_buf; + + buf_set_write(&srv_key_buf, (void *)&srv_key, sizeof(srv_key)); + if (!read_pem_key_file(&srv_key_buf, tls_crypt_v2_srv_pem_name, + key_file, key_inline)) + { + msg(M_FATAL, "ERROR: invalid tls-crypt-v2 server key format"); + } + + struct key_type kt = tls_crypt_kt(); + if (!kt.cipher || !kt.digest) + { + msg(M_FATAL, "ERROR: --tls-crypt-v2 not supported"); + } + init_key_ctx(key_ctx, &srv_key, &kt, encrypt, "tls-crypt-v2 server key"); + secure_memzero(&srv_key, sizeof(srv_key)); +} + +static bool +tls_crypt_v2_wrap_client_key(struct buffer *wkc, + const struct key2 *src_key, + const struct buffer *src_metadata, + struct key_ctx *server_key, struct gc_arena *gc) +{ + cipher_ctx_t *cipher_ctx = server_key->cipher; + struct buffer work = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN + + cipher_ctx_block_size(cipher_ctx), gc); + + /* Calculate auth tag and synthetic IV */ + uint8_t *tag = buf_write_alloc(&work, TLS_CRYPT_TAG_SIZE); + if (!tag) + { + msg(M_WARN, "ERROR: could not write tag"); + return false; + } + uint16_t net_len = htons(sizeof(src_key->keys) + BLEN(src_metadata) + + TLS_CRYPT_V2_TAG_SIZE + sizeof(uint16_t)); + hmac_ctx_t *hmac_ctx = server_key->hmac; + hmac_ctx_reset(hmac_ctx); + hmac_ctx_update(hmac_ctx, (void *)&net_len, sizeof(net_len)); + hmac_ctx_update(hmac_ctx, (void *)src_key->keys, sizeof(src_key->keys)); + hmac_ctx_update(hmac_ctx, BPTR(src_metadata), BLEN(src_metadata)); + hmac_ctx_final(hmac_ctx, tag); + + dmsg(D_CRYPTO_DEBUG, "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(cipher_ctx, tag)); + + /* Overflow check (OpenSSL requires an extra block in the dst buffer) */ + if (buf_forward_capacity(&work) < (sizeof(src_key->keys) + + BLEN(src_metadata) + + sizeof(net_len) + + cipher_ctx_block_size(cipher_ctx))) + { + msg(M_WARN, "ERROR: could not crypt: insufficient space in dst"); + return false; + } + + /* Encrypt */ + int outlen = 0; + ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, + (void *)src_key->keys, sizeof(src_key->keys))); + ASSERT(buf_inc_len(&work, outlen)); + ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, + BPTR(src_metadata), BLEN(src_metadata))); + ASSERT(buf_inc_len(&work, outlen)); + ASSERT(cipher_ctx_final(cipher_ctx, BEND(&work), &outlen)); + ASSERT(buf_inc_len(&work, outlen)); + ASSERT(buf_write(&work, &net_len, sizeof(net_len))); + + return buf_copy(wkc, &work); +} + +static bool +tls_crypt_v2_unwrap_client_key(struct key2 *client_key, struct buffer *metadata, + struct buffer wrapped_client_key, + struct key_ctx *server_key) +{ + const char *error_prefix = __func__; + bool ret = false; + struct gc_arena gc = gc_new(); + /* The crypto API requires one extra cipher block of buffer head room when + * decrypting, which nicely matches the tag size of WKc. So + * TLS_CRYPT_V2_MAX_WKC_LEN is always large enough for the plaintext. */ + uint8_t plaintext_buf_data[TLS_CRYPT_V2_MAX_WKC_LEN] = { 0 }; + struct buffer plaintext = { 0 }; + + dmsg(D_TLS_DEBUG_MED, "%s: unwrapping client key (len=%d): %s", __func__, + BLEN(&wrapped_client_key), format_hex(BPTR(&wrapped_client_key), + BLEN(&wrapped_client_key), + 0, &gc)); + + if (TLS_CRYPT_V2_MAX_WKC_LEN < BLEN(&wrapped_client_key)) + { + CRYPT_ERROR("wrapped client key too big"); + } + + /* Decrypt client key and metadata */ + uint16_t net_len = 0; + const uint8_t *tag = BPTR(&wrapped_client_key); + + if (BLEN(&wrapped_client_key) < sizeof(net_len)) + { + CRYPT_ERROR("failed to read length"); + } + memcpy(&net_len, BEND(&wrapped_client_key) - sizeof(net_len), + sizeof(net_len)); + + if (ntohs(net_len) != BLEN(&wrapped_client_key)) + { + dmsg(D_TLS_DEBUG_LOW, "%s: net_len=%u, BLEN=%i", __func__, + ntohs(net_len), BLEN(&wrapped_client_key)); + CRYPT_ERROR("invalid length"); + } + + buf_inc_len(&wrapped_client_key, -(int)sizeof(net_len)); + + if (!buf_advance(&wrapped_client_key, TLS_CRYPT_TAG_SIZE)) + { + CRYPT_ERROR("failed to read tag"); + } + + if (!cipher_ctx_reset(server_key->cipher, tag)) + { + CRYPT_ERROR("failed to initialize IV"); + } + buf_set_write(&plaintext, plaintext_buf_data, sizeof(plaintext_buf_data)); + int outlen = 0; + if (!cipher_ctx_update(server_key->cipher, BPTR(&plaintext), &outlen, + BPTR(&wrapped_client_key), + BLEN(&wrapped_client_key))) + { + CRYPT_ERROR("could not decrypt client key"); + } + ASSERT(buf_inc_len(&plaintext, outlen)); + + if (!cipher_ctx_final(server_key->cipher, BEND(&plaintext), &outlen)) + { + CRYPT_ERROR("cipher final failed"); + } + ASSERT(buf_inc_len(&plaintext, outlen)); + + /* Check authentication */ + uint8_t tag_check[TLS_CRYPT_TAG_SIZE] = { 0 }; + hmac_ctx_reset(server_key->hmac); + hmac_ctx_update(server_key->hmac, (void *)&net_len, sizeof(net_len)); + hmac_ctx_update(server_key->hmac, BPTR(&plaintext), + BLEN(&plaintext)); + hmac_ctx_final(server_key->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("client key authentication error"); + } + + if (buf_len(&plaintext) < sizeof(client_key->keys)) + { + CRYPT_ERROR("failed to read client key"); + } + memcpy(&client_key->keys, BPTR(&plaintext), sizeof(client_key->keys)); + ASSERT(buf_advance(&plaintext, sizeof(client_key->keys))); + + if (!buf_copy(metadata, &plaintext)) + { + CRYPT_ERROR("metadata too large for supplied buffer"); + } + + ret = true; +error_exit: + if (!ret) + { + secure_memzero(client_key, sizeof(*client_key)); + } + buf_clear(&plaintext); + gc_free(&gc); + return ret; +} + +static bool +tls_crypt_v2_verify_metadata(const struct tls_wrap_ctx *ctx, + const struct tls_options *opt) +{ + bool ret = false; + struct gc_arena gc = gc_new(); + const char *tmp_file = NULL; + struct buffer metadata = ctx->tls_crypt_v2_metadata; + int metadata_type = buf_read_u8(&metadata); + if (metadata_type < 0) + { + msg(M_WARN, "ERROR: no metadata type"); + goto cleanup; + } + + tmp_file = platform_create_temp_file(opt->tmp_dir, "tls_crypt_v2_metadata_", + &gc); + if (!tmp_file || !buffer_write_file(tmp_file, &metadata)) + { + msg(M_WARN, "ERROR: could not write metadata to file"); + goto cleanup; + } + + char metadata_type_str[4] = { 0 }; /* Max value: 255 */ + openvpn_snprintf(metadata_type_str, sizeof(metadata_type_str), + "%i", metadata_type); + struct env_set *es = env_set_create(NULL); + setenv_str(es, "script_type", "tls-crypt-v2-verify"); + setenv_str(es, "metadata_type", metadata_type_str); + setenv_str(es, "metadata_file", tmp_file); + + struct argv argv = argv_new(); + argv_parse_cmd(&argv, opt->tls_crypt_v2_verify_script); + argv_msg_prefix(D_TLS_DEBUG, &argv, "Executing tls-crypt-v2-verify"); + + ret = openvpn_run_script(&argv, es, 0, "--tls-crypt-v2-verify"); + + argv_free(&argv); + env_set_destroy(es); + + if (!platform_unlink(tmp_file)) + { + msg(M_WARN, "WARNING: failed to remove temp file '%s", tmp_file); + } + + if (ret) + { + msg(D_HANDSHAKE, "TLS CRYPT V2 VERIFY SCRIPT OK"); + } + else + { + msg(D_HANDSHAKE, "TLS CRYPT V2 VERIFY SCRIPT ERROR"); + } + +cleanup: + gc_free(&gc); + return ret; +} + +bool +tls_crypt_v2_extract_client_key(struct buffer *buf, + struct tls_wrap_ctx *ctx, + const struct tls_options *opt) +{ + if (!ctx->tls_crypt_v2_server_key.cipher) + { + msg(D_TLS_ERRORS, + "Client wants tls-crypt-v2, but no server key present."); + return false; + } + + msg(D_HANDSHAKE, "Control Channel: using tls-crypt-v2 key"); + + struct buffer wrapped_client_key = *buf; + uint16_t net_len = 0; + + if (BLEN(&wrapped_client_key) < sizeof(net_len)) + { + msg(D_TLS_ERRORS, "failed to read length"); + } + memcpy(&net_len, BEND(&wrapped_client_key) - sizeof(net_len), + sizeof(net_len)); + + size_t wkc_len = ntohs(net_len); + if (!buf_advance(&wrapped_client_key, BLEN(&wrapped_client_key) - wkc_len)) + { + msg(D_TLS_ERRORS, "Can not locate tls-crypt-v2 client key"); + return false; + } + + struct key2 client_key = { 0 }; + ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN); + if (!tls_crypt_v2_unwrap_client_key(&client_key, + &ctx->tls_crypt_v2_metadata, + wrapped_client_key, + &ctx->tls_crypt_v2_server_key)) + { + msg(D_TLS_ERRORS, "Can not unwrap tls-crypt-v2 client key"); + secure_memzero(&client_key, sizeof(client_key)); + return false; + } + + /* Load the decrypted key */ + ctx->mode = TLS_WRAP_CRYPT; + ctx->cleanup_key_ctx = true; + ctx->opt.flags |= CO_PACKET_ID_LONG_FORM; + memset(&ctx->opt.key_ctx_bi, 0, sizeof(ctx->opt.key_ctx_bi)); + tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, &client_key, true); + secure_memzero(&client_key, sizeof(client_key)); + + /* Remove client key from buffer so tls-crypt code can unwrap message */ + ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key)))); + + if (opt && opt->tls_crypt_v2_verify_script) + { + return tls_crypt_v2_verify_metadata(ctx, opt); + } + + return true; +} + +void +tls_crypt_v2_write_server_key_file(const char *filename) +{ + write_pem_key_file(filename, tls_crypt_v2_srv_pem_name); +} + +void +tls_crypt_v2_write_client_key_file(const char *filename, + const char *b64_metadata, + const char *server_key_file, + bool server_key_inline) +{ + struct gc_arena gc = gc_new(); + struct key_ctx server_key = { 0 }; + struct buffer client_key_pem = { 0 }; + struct buffer dst = alloc_buf_gc(TLS_CRYPT_V2_CLIENT_KEY_LEN + + TLS_CRYPT_V2_MAX_WKC_LEN, &gc); + struct key2 client_key = { 2 }; + + if (!rand_bytes((void *)client_key.keys, sizeof(client_key.keys))) + { + msg(M_FATAL, "ERROR: could not generate random key"); + goto cleanup; + } + ASSERT(buf_write(&dst, client_key.keys, sizeof(client_key.keys))); + + struct buffer metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, &gc); + if (b64_metadata) + { + if (TLS_CRYPT_V2_MAX_B64_METADATA_LEN < strlen(b64_metadata)) + { + msg(M_FATAL, + "ERROR: metadata too long (%d bytes, max %u bytes)", + (int)strlen(b64_metadata), TLS_CRYPT_V2_MAX_B64_METADATA_LEN); + } + ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_USER, 1)); + int decoded_len = openvpn_base64_decode(b64_metadata, BEND(&metadata), + BCAP(&metadata)); + if (decoded_len < 0) + { + msg(M_FATAL, "ERROR: failed to base64 decode provided metadata"); + goto cleanup; + } + ASSERT(buf_inc_len(&metadata, decoded_len)); + } + else + { + int64_t timestamp = htonll((uint64_t)now); + ASSERT(buf_write(&metadata, &TLS_CRYPT_METADATA_TYPE_TIMESTAMP, 1)); + ASSERT(buf_write(&metadata, ×tamp, sizeof(timestamp))); + } + + tls_crypt_v2_init_server_key(&server_key, true, server_key_file, + server_key_inline); + if (!tls_crypt_v2_wrap_client_key(&dst, &client_key, &metadata, &server_key, + &gc)) + { + msg(M_FATAL, "ERROR: could not wrap generated client key"); + goto cleanup; + } + + /* PEM-encode Kc || WKc */ + if (!crypto_pem_encode(tls_crypt_v2_cli_pem_name, &client_key_pem, &dst, + &gc)) + { + msg(M_FATAL, "ERROR: could not PEM-encode client key"); + goto cleanup; + } + + const char *client_file = filename; + bool client_inline = false; + + if (!filename || streq(filename, "")) + { + printf("%.*s\n", BLEN(&client_key_pem), BPTR(&client_key_pem)); + client_file = (const char *)BPTR(&client_key_pem); + client_inline = true; + } + else if (!buffer_write_file(filename, &client_key_pem)) + { + msg(M_FATAL, "ERROR: could not write client key file"); + goto cleanup; + } + + /* Sanity check: load client key (as "client") */ + struct key_ctx_bi test_client_key; + struct buffer test_wrapped_client_key; + msg(D_GENKEY, "Testing client-side key loading..."); + tls_crypt_v2_init_client_key(&test_client_key, &test_wrapped_client_key, + client_file, client_inline); + free_key_ctx_bi(&test_client_key); + + /* Sanity check: unwrap and load client key (as "server") */ + struct buffer test_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN, + &gc); + struct key2 test_client_key2 = { 0 }; + free_key_ctx(&server_key); + tls_crypt_v2_init_server_key(&server_key, false, server_key_file, + server_key_inline); + msg(D_GENKEY, "Testing server-side key loading..."); + ASSERT(tls_crypt_v2_unwrap_client_key(&test_client_key2, &test_metadata, + test_wrapped_client_key, &server_key)); + secure_memzero(&test_client_key2, sizeof(test_client_key2)); + free_buf(&test_wrapped_client_key); + +cleanup: + secure_memzero(&client_key, sizeof(client_key)); + free_key_ctx(&server_key); + buf_clear(&client_key_pem); + buf_clear(&dst); + + gc_free(&gc); +} diff --git a/src/openvpn/tls_crypt.h b/src/openvpn/tls_crypt.h index 05fcc4e..2e127f2 100644 --- a/src/openvpn/tls_crypt.h +++ b/src/openvpn/tls_crypt.h @@ -22,15 +22,13 @@ */ /** - * @defgroup tls_crypt Control channel encryption (--tls-crypt) + * @defgroup tls_crypt Control channel encryption (--tls-crypt, --tls-crypt-v2) * @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. @@ -38,11 +36,20 @@ * - 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 + * --tls-crypt uses a tls-auth-style group key, where all servers and clients + * share the same group key. --tls-crypt-v2 adds support for client-specific + * keys, where all servers share the same client-key encryption key, and each + * clients receives a unique client key, both in plaintext and in encrypted + * form. When connecting to a server, the client sends the encrypted key to + * the server in the first packet (P_CONTROL_HARD_RESET_CLIENT_V3). The server + * then decrypts that key, and both parties can use the same client-specific + * key for tls-crypt packets. See doc/tls-crypt-v2.txt for more details. + * + * @par On-the-wire tls-crypt packet specification + * @parblock * 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) @@ -57,28 +64,27 @@ * 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 * <tt>- XXX -</tt> means authenticated, and * <tt>* XXX *</tt> means authenticated and encrypted. + * + * @endparblock */ #ifndef TLSCRYPT_H #define TLSCRYPT_H -#ifdef ENABLE_CRYPTO - +#include "base64.h" #include "buffer.h" #include "crypto.h" #include "session_id.h" +#include "ssl_common.h" #define TLS_CRYPT_TAG_SIZE (256/8) #define TLS_CRYPT_PID_SIZE (sizeof(packet_id_type) + sizeof(net_time_t)) @@ -88,18 +94,28 @@ #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) +#define TLS_CRYPT_V2_MAX_WKC_LEN (1024) +#define TLS_CRYPT_V2_CLIENT_KEY_LEN (2048 / 8) +#define TLS_CRYPT_V2_SERVER_KEY_LEN (sizeof(struct key)) +#define TLS_CRYPT_V2_TAG_SIZE (TLS_CRYPT_TAG_SIZE) +#define TLS_CRYPT_V2_MAX_METADATA_LEN (unsigned)(TLS_CRYPT_V2_MAX_WKC_LEN \ + - (TLS_CRYPT_V2_CLIENT_KEY_LEN + TLS_CRYPT_V2_TAG_SIZE \ + + sizeof(uint16_t))) +#define TLS_CRYPT_V2_MAX_B64_METADATA_LEN \ + OPENVPN_BASE64_LENGTH(TLS_CRYPT_V2_MAX_METADATA_LEN - 1) + /** * 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 key_file The file to read the key from or the key itself if + * key_inline is true. + * @param key_inline True if key_file contains an inline key, False + * otherwise. * @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); + bool key_inline, bool tls_server); /** * Returns the maximum overhead (in bytes) added to the destination buffer by @@ -140,8 +156,73 @@ bool tls_crypt_wrap(const struct buffer *src, struct buffer *dst, bool tls_crypt_unwrap(const struct buffer *src, struct buffer *dst, struct crypto_options *opt); -/** @} */ +/** + * Initialize a tls-crypt-v2 server key (used to encrypt/decrypt client keys). + * + * @param key Key structure to be initialized. Must be non-NULL. + * @parem encrypt If true, initialize the key structure for encryption, + * otherwise for decryption. + * @param key_file File path of the key file to load or the key itself if + * key_inline is true. + * @param key_inline True if key_file contains an inline key, False + * otherwise. + * + */ +void tls_crypt_v2_init_server_key(struct key_ctx *key_ctx, bool encrypt, + const char *key_file, bool key_inline); + +/** + * Initialize a tls-crypt-v2 client key. + * + * @param key Key structure to be initialized with the client + * key. + * @param wrapped_key_buf Returns buffer containing the wrapped key that will + * be sent to the server when connecting. Caller must + * free this buffer when no longer needed. + * @param key_file File path of the key file to load or the key itself + * if key_inline is true. + * @param key_inline True if key_file contains an inline key, False + * otherwise. + */ +void tls_crypt_v2_init_client_key(struct key_ctx_bi *key, + struct buffer *wrapped_key_buf, + const char *key_file, bool key_inline); + +/** + * Extract a tls-crypt-v2 client key from a P_CONTROL_HARD_RESET_CLIENT_V3 + * message, and load the key into the supplied tls wrap context. + * + * @param buf Buffer containing a received P_CONTROL_HARD_RESET_CLIENT_V3 + * message. + * @param ctx tls-wrap context to be initialized with the client key. + * + * @returns true if a key was successfully extracted. + */ +bool tls_crypt_v2_extract_client_key(struct buffer *buf, + struct tls_wrap_ctx *ctx, + const struct tls_options *opt); + +/** + * Generate a tls-crypt-v2 server key, and write to file. + * + * @param filename Filename of the server key file to create. + */ +void tls_crypt_v2_write_server_key_file(const char *filename); + +/** + * Generate a tls-crypt-v2 client key, and write to file. + * + * @param filename Filename of the client key file to create. + * @param b64_metadata Base64 metadata to be included in the client key. + * @param key_file File path of the server key to use for wrapping the + * client key or the key itself if key_inline is true. + * @param key_inline True if key_file contains an inline key, False + * otherwise. + */ +void tls_crypt_v2_write_client_key_file(const char *filename, + const char *b64_metadata, + const char *key_file, bool key_inline); -#endif /* ENABLE_CRYPTO */ +/** @} */ #endif /* TLSCRYPT_H */ diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 80eaa2c..3045445 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -40,12 +40,13 @@ #include "tun.h" #include "fdmisc.h" #include "common.h" -#include "misc.h" +#include "run_command.h" #include "socket.h" #include "manage.h" #include "route.h" #include "win32.h" #include "block_dns.h" +#include "networking.h" #include "memdbg.h" @@ -57,6 +58,9 @@ #ifdef _WIN32 +const static GUID GUID_DEVCLASS_NET = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } }; +const static GUID GUID_DEVINTERFACE_NET = { 0xcac88484, 0x7515, 0x4c03, { 0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61 } }; + /* #define SIMULATE_DHCP_FAILED */ /* simulate bad DHCP negotiation */ #define NI_TEST_FIRST (1<<0) @@ -69,6 +73,10 @@ static void netsh_ifconfig(const struct tuntap_options *to, const in_addr_t netmask, const unsigned int flags); +static void windows_set_mtu(const int iface_index, + const short family, + const int mtu); + static void netsh_set_dns6_servers(const struct in6_addr *addr_list, const int addr_len, const char *flex_name); @@ -82,7 +90,6 @@ 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(); @@ -106,8 +113,8 @@ do_address_service(const bool add, const short family, const struct tuntap *tt) if (addr.family == AF_INET) { - addr.address.ipv4.s_addr = tt->local; - addr.prefix_len = 32; + addr.address.ipv4.s_addr = htonl(tt->local); + addr.prefix_len = netmask_to_netbits2(tt->adapter_netmask); } else { @@ -115,11 +122,8 @@ do_address_service(const bool add, const short family, const struct tuntap *tt) addr.prefix_len = tt->netbits_ipv6; } - if (!WriteFile(pipe, &addr, sizeof(addr), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &addr, sizeof(addr), &ack, "TUN")) { - msg(M_WARN, "TUN: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -139,14 +143,15 @@ out: } static bool -do_dns6_service(bool add, const struct tuntap *tt) +do_dns_service(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; - int addr_len = add ? tt->options.dns6_len : 0; + int len = family == AF_INET6 ? tt->options.dns6_len : tt->options.dns_len; + int addr_len = add ? len : 0; + const char *ip_proto_name = family == AF_INET6 ? "IPv6" : "IPv4"; if (addr_len == 0 && add) /* no addresses to add */ { @@ -161,7 +166,7 @@ do_dns6_service(bool add, const struct tuntap *tt) }, .iface = { .index = tt->adapter_index, .name = "" }, .domains = "", - .family = AF_INET6, + .family = family, .addr_len = addr_len }; @@ -173,35 +178,39 @@ do_dns6_service(bool add, const struct tuntap *tt) { addr_len = _countof(dns.addr); dns.addr_len = addr_len; - msg(M_WARN, "Number of IPv6 DNS addresses sent to service truncated to %d", - addr_len); + msg(M_WARN, "Number of %s DNS addresses sent to service truncated to %d", + ip_proto_name, addr_len); } for (int i = 0; i < addr_len; ++i) { - dns.addr[i].ipv6 = tt->options.dns6[i]; + if (family == AF_INET6) + { + dns.addr[i].ipv6 = tt->options.dns6[i]; + } + else + { + dns.addr[i].ipv4.s_addr = htonl(tt->options.dns[i]); + } } - msg(D_LOW, "%s IPv6 dns servers on '%s' (if_index = %d) using service", - (add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index); + msg(D_LOW, "%s %s dns servers on '%s' (if_index = %d) using service", + (add ? "Setting" : "Deleting"), ip_proto_name, dns.iface.name, dns.iface.index); - if (!WriteFile(pipe, &dns, sizeof(dns), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &dns, sizeof(dns), &ack, "TUN")) { - 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 IPv6 dns failed using service: %s [status=%u if_name=%s]", - (add ? "adding" : "deleting"), strerror_win32(ack.error_number, &gc), + msg(M_WARN, "TUN: %s %s dns failed using service: %s [status=%u if_name=%s]", + (add ? "adding" : "deleting"), ip_proto_name, strerror_win32(ack.error_number, &gc), ack.error_number, dns.iface.name); goto out; } - msg(M_INFO, "IPv6 dns servers %s using service", (add ? "set" : "deleted")); + msg(M_INFO, "%s dns servers %s using service", ip_proto_name, (add ? "set" : "deleted")); ret = true; out: @@ -209,6 +218,52 @@ out: return ret; } +static bool +do_set_mtu_service(const struct tuntap *tt, const short family, const int mtu) +{ + DWORD len; + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new(); + HANDLE pipe = tt->options.msg_channel; + const char *family_name = (family == AF_INET6) ? "IPv6" : "IPv4"; + set_mtu_message_t mtu_msg = { + .header = { + msg_set_mtu, + sizeof(set_mtu_message_t), + 0 + }, + .iface = {.index = tt->adapter_index}, + .mtu = mtu, + .family = family + }; + strncpynt(mtu_msg.iface.name, tt->actual_name, sizeof(mtu_msg.iface.name)); + if (family == AF_INET6 && mtu < 1280) + { + msg(M_INFO, "NOTE: IPv6 interface MTU < 1280 conflicts with IETF standards and might not work"); + } + + if (!send_msg_iservice(pipe, &mtu_msg, sizeof(mtu_msg), &ack, "Set_mtu")) + { + goto out; + } + + if (ack.error_number != NO_ERROR) + { + msg(M_NONFATAL, "TUN: setting %s mtu using service failed: %s [status=%u if_index=%d]", + family_name, strerror_win32(ack.error_number, &gc), ack.error_number, mtu_msg.iface.index); + } + else + { + msg(M_INFO, "%s MTU set to %d on interface %d using service", family_name, mtu, mtu_msg.iface.index); + ret = true; + } + +out: + gc_free(&gc); + return ret; +} + #endif /* ifdef _WIN32 */ #ifdef TARGET_SOLARIS @@ -342,16 +397,6 @@ ifconfig_sanity_check(bool tun, in_addr_t addr, int topology) } /* - * For TAP-style devices, generate a broadcast address. - */ -static in_addr_t -generate_ifconfig_broadcast_addr(in_addr_t local, - in_addr_t netmask) -{ - return local | ~netmask; -} - -/* * Check that --local and --remote addresses do not * clash with ifconfig addresses or subnet. */ @@ -460,13 +505,13 @@ check_subnet_conflict(const in_addr_t ip, } void -warn_on_use_of_common_subnets(void) +warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct route_gateway_info rgi; const int needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED); - get_default_gateway(&rgi); + get_default_gateway(&rgi, ctx); if ((rgi.flags & needed) == needed) { const in_addr_t lan_network = rgi.gateway.addr & rgi.gateway.netmask; @@ -561,8 +606,8 @@ is_tun_p2p(const struct tuntap *tt) bool tun = false; if (tt->type == DEV_TYPE_TAP - || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - || tt->type == DEV_TYPE_NULL ) + || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) + || tt->type == DEV_TYPE_NULL) { tun = false; } @@ -602,9 +647,7 @@ do_ifconfig_setenv(const struct tuntap *tt, struct env_set *es) } else { - const char *ifconfig_broadcast = print_in_addr_t(tt->broadcast, 0, &gc); setenv_str(es, "ifconfig_netmask", ifconfig_remote_netmask); - setenv_str(es, "ifconfig_broadcast", ifconfig_broadcast); } } @@ -639,7 +682,8 @@ init_tun(const char *dev, /* --dev option */ struct addrinfo *local_public, struct addrinfo *remote_public, const bool strict_warn, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct tuntap *tt; @@ -730,14 +774,6 @@ init_tun(const char *dev, /* --dev option */ } } - /* - * If TAP-style interface, generate broadcast address. - */ - if (!tun) - { - tt->broadcast = generate_ifconfig_broadcast_addr(tt->local, tt->remote_netmask); - } - #ifdef _WIN32 /* * Make sure that both ifconfig addresses are part of the @@ -798,10 +834,40 @@ init_tun_post(struct tuntap *tt, #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; - tt->rw_handle.write = tt->writes.overlapped.hEvent; tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; -#endif + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + tt->wintun_send_ring_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, + 0, + sizeof(struct tun_ring), + NULL); + tt->wintun_receive_ring_handle = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + sizeof(struct tun_ring), + NULL); + if ((tt->wintun_send_ring_handle == NULL) || (tt->wintun_receive_ring_handle == NULL)) + { + msg(M_FATAL, "Cannot allocate memory for ring buffer"); + } + + tt->rw_handle.read = CreateEvent(NULL, FALSE, FALSE, NULL); + tt->rw_handle.write = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ((tt->rw_handle.read == NULL) || (tt->rw_handle.write == NULL)) + { + msg(M_FATAL, "Cannot create events for ring buffer"); + } + } + else + { + tt->rw_handle.read = tt->reads.overlapped.hEvent; + tt->rw_handle.write = tt->writes.overlapped.hEvent; + } +#endif /* ifdef _WIN32 */ } #if defined(_WIN32) \ @@ -812,7 +878,7 @@ init_tun_post(struct tuntap *tt, * an extra call to "route add..." * -> helper function to simplify code below */ -void +static void add_route_connected_v6_net(struct tuntap *tt, const struct env_set *es) { @@ -824,12 +890,11 @@ add_route_connected_v6_net(struct tuntap *tt, r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ r6.flags = RT_DEFINED | RT_METRIC_DEFINED; - add_route_ipv6(&r6, tt, 0, es); + add_route_ipv6(&r6, tt, 0, es, NULL); } void -delete_route_connected_v6_net(struct tuntap *tt, - const struct env_set *es) +delete_route_connected_v6_net(const struct tuntap *tt) { struct route_ipv6 r6; @@ -840,7 +905,7 @@ delete_route_connected_v6_net(struct tuntap *tt, r6.metric = 0; /* connected route */ r6.flags = RT_DEFINED | RT_ADDED | RT_METRIC_DEFINED; route_ipv6_clear_host_bits(&r6); - delete_route_ipv6(&r6, tt, 0, es); + delete_route_ipv6(&r6, tt, 0, NULL, NULL); } #endif /* if defined(_WIN32) || defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD) */ @@ -871,739 +936,576 @@ create_arbitrary_remote( struct tuntap *tt ) } #endif -/* execute the ifconfig command through the shell */ -void -do_ifconfig(struct tuntap *tt, - const char *actual, /* actual device name */ - int tun_mtu, - const struct env_set *es) +/** + * do_ifconfig_ipv6 - perform platform specific ifconfig6 commands + * + * @param tt the tuntap interface context + * @param ifname the human readable interface name + * @param mtu the MTU value to set the interface to + * @param es the environment to be used when executing the commands + * @param ctx the networking API opaque context + */ +static void +do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) { +#if !defined(TARGET_LINUX) + struct argv argv = argv_new(); struct gc_arena gc = gc_new(); + const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); +#endif - if (tt->did_ifconfig_setup) +#if defined(TARGET_LINUX) + if (net_iface_mtu_set(ctx, ifname, tun_mtu) < 0) { - bool tun = false; - const char *ifconfig_local = NULL; - const char *ifconfig_remote_netmask = NULL; - const char *ifconfig_broadcast = NULL; - const char *ifconfig_ipv6_local = NULL; - bool do_ipv6 = false; - struct argv argv = argv_new(); + msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, ifname); + } - msg( D_LOW, "do_ifconfig, tt->did_ifconfig_ipv6_setup=%d", - tt->did_ifconfig_ipv6_setup ); + if (net_iface_up(ctx, ifname, true) < 0) + { + msg(M_FATAL, "Linux can't bring %s up", ifname); + } - /* - * We only handle TUN/TAP devices here, not --dev null devices. - */ - tun = is_tun_p2p(tt); + if (net_addr_v6_add(ctx, ifname, &tt->local_ipv6, + tt->netbits_ipv6) < 0) + { + msg(M_FATAL, "Linux can't add IPv6 to interface %s", ifname); + } +#elif defined(TARGET_ANDROID) + char out6[64]; - /* - * Set ifconfig parameters - */ - ifconfig_local = print_in_addr_t(tt->local, 0, &gc); - ifconfig_remote_netmask = print_in_addr_t(tt->remote_netmask, 0, &gc); + openvpn_snprintf(out6, sizeof(out6), "%s/%d %d", + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + management_android_control(management, "IFCONFIG6", out6); +#elif defined(TARGET_SOLARIS) + argv_printf(&argv, "%s %s inet6 unplumb", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, 0, NULL); - if (tt->did_ifconfig_ipv6_setup) - { - ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); - do_ipv6 = true; - } + if (tt->type == DEV_TYPE_TUN) + { + const char *ifconfig_ipv6_remote = print_in6_addr(tt->remote_ipv6, 0, &gc); - /* - * If TAP-style device, generate broadcast address. - */ - if (!tun) - { - ifconfig_broadcast = print_in_addr_t(tt->broadcast, 0, &gc); - } + argv_printf(&argv, "%s %s inet6 plumb %s/%d %s mtu %d up", + IFCONFIG_PATH, ifname, ifconfig_ipv6_local, + tt->netbits_ipv6, ifconfig_ipv6_remote, tun_mtu); + } + else /* tap mode */ + { + /* base IPv6 tap interface needs to be brought up first */ + argv_printf(&argv, "%s %s inet6 plumb up", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); -#ifdef ENABLE_MANAGEMENT - if (management) + if (!openvpn_execve_check(&argv, es, 0, + "Solaris ifconfig IPv6 (prepare) failed")) { - management_set_state(management, - OPENVPN_STATE_ASSIGN_IP, - NULL, - &tt->local, - &tt->local_ipv6, - NULL, - NULL); + solaris_error_close(tt, es, ifname, true); } -#endif + /* we might need to do "ifconfig %s inet6 auto-dhcp drop" + * after the system has noticed the interface and fired up + * the DHCPv6 client - but this takes quite a while, and the + * server will ignore the DHCPv6 packets anyway. So we don't. + */ -#if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - /* - * Set the MTU for the device + /* static IPv6 addresses need to go to a subinterface (tap0:1) + * and we cannot set an mtu here (must go to the "parent") */ - argv_printf(&argv, - "%s link set dev %s up mtu %d", - iproute_path, - actual, - tun_mtu - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip link set failed"); + argv_printf(&argv, "%s %s inet6 addif %s/%d up", IFCONFIG_PATH, + ifname, ifconfig_ipv6_local, tt->netbits_ipv6 ); + } + argv_msg(M_INFO, &argv); - if (tun) - { + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 failed")) + { + solaris_error_close(tt, es, ifname, true); + } - /* - * Set the address for the device - */ - argv_printf(&argv, - "%s addr add dev %s local %s peer %s", - iproute_path, - actual, - ifconfig_local, - ifconfig_remote_netmask - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed"); - } - else - { - argv_printf(&argv, - "%s addr add dev %s %s/%d broadcast %s", - iproute_path, - actual, - ifconfig_local, - netmask_to_netbits2(tt->remote_netmask), - ifconfig_broadcast - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed"); - } - if (do_ipv6) - { - argv_printf( &argv, - "%s -6 addr add %s/%d dev %s", - iproute_path, - ifconfig_ipv6_local, - tt->netbits_ipv6, - actual - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip -6 addr add failed"); - } - tt->did_ifconfig = true; -#else /* ifdef ENABLE_IPROUTE */ - if (tun) - { - argv_printf(&argv, - "%s %s %s pointopoint %s mtu %d", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } + if (tt->type != DEV_TYPE_TUN) + { + argv_printf(&argv, "%s %s inet6 mtu %d", IFCONFIG_PATH, + ifname, tun_mtu); argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig failed"); - if (do_ipv6) - { - argv_printf(&argv, - "%s %s add %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig inet6 failed"); - } - tt->did_ifconfig = true; - -#endif /*ENABLE_IPROUTE*/ -#elif defined(TARGET_ANDROID) + openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 mtu failed"); + } +#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) \ + || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) \ + || defined(TARGET_DRAGONFLY) + argv_printf(&argv, "%s %s inet6 %s/%d mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + argv_msg(M_INFO, &argv); - 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)); - } + openvpn_execve_check(&argv, es, S_FATAL, + "generic BSD ifconfig inet6 failed"); - struct buffer out = alloc_buf_gc(64, &gc); +#if defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) \ + || defined(TARGET_DARWIN) + /* and, hooray, we explicitly need to add a route... */ + add_route_connected_v6_net(tt, es); +#endif +#elif defined(TARGET_AIX) + argv_printf(&argv, "%s %s inet6 %s/%d mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + argv_msg(M_INFO, &argv); - char *top; - switch (tt->topology) - { - case TOP_NET30: - top = "net30"; - break; + /* AIX ifconfig will complain if it can't find ODM path in env */ + es = env_set_create(NULL); + env_set_add(es, "ODMDIR=/etc/objrepos"); - case TOP_P2P: - top = "p2p"; - break; + openvpn_execve_check(&argv, es, S_FATAL, + "generic BSD ifconfig inet6 failed"); - case TOP_SUBNET: - top = "subnet"; - break; + env_set_destroy(es); +#elif defined (_WIN32) + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + msg(M_INFO, "******** NOTE: Please manually set the v6 IP of '%s' to %s (if it is not already set)", + ifname, ifconfig_ipv6_local); + } + else if (tt->options.msg_channel) + { + do_address_service(true, AF_INET6, tt); + add_route_connected_v6_net(tt, es); + do_dns_service(true, AF_INET6, tt); + do_set_mtu_service(tt, AF_INET6, tun_mtu); + } + else + { + /* example: netsh interface ipv6 set address interface=42 + * 2001:608:8003::d store=active + */ + char iface[64]; - default: - top = "undef"; - } + openvpn_snprintf(iface, sizeof(iface), "interface=%lu", + tt->adapter_index); + argv_printf(&argv, "%s%s 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); + add_route_connected_v6_net(tt, es); + /* set ipv6 dns servers if any are specified */ + netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, ifname); + windows_set_mtu(tt->adapter_index, AF_INET6, tun_mtu); + } +#else /* platforms we have no IPv6 code for */ + msg(M_FATAL, "Sorry, but I don't know how to do IPv6 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); +#endif /* outer "if defined(TARGET_xxx)" conditional */ - buf_printf(&out, "%s %s %d %s", ifconfig_local, ifconfig_remote_netmask, tun_mtu, top); - management_android_control(management, "IFCONFIG", buf_bptr(&out)); +#if !defined(TARGET_LINUX) + gc_free(&gc); + argv_free(&argv); +#endif +} -#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 - * ifconfig tun2 netmask 255.255.255.255 - */ - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-1 failed")) - { - solaris_error_close(tt, es, actual, false); - } +/** + * do_ifconfig_ipv4 - perform platform specific ifconfig commands + * + * @param tt the tuntap interface context + * @param ifname the human readable interface name + * @param mtu the MTU value to set the interface to + * @param es the environment to be used when executing the commands + * @param ctx the networking API opaque context + */ +static void +do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) +{ + /* + * We only handle TUN/TAP devices here, not --dev null devices. + */ + bool tun = is_tun_p2p(tt); - argv_printf(&argv, - "%s %s netmask 255.255.255.255", - IFCONFIG_PATH, - actual - ); - } - else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - argv_printf(&argv, - "%s %s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - argv_printf(&argv, - " %s %s %s netmask %s broadcast + up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask - ); - } +#if !defined(TARGET_LINUX) + const char *ifconfig_local = NULL; + const char *ifconfig_remote_netmask = NULL; + struct argv argv = argv_new(); + struct gc_arena gc = gc_new(); - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed")) - { - solaris_error_close(tt, es, actual, false); - } + /* + * Set ifconfig parameters + */ + ifconfig_local = print_in_addr_t(tt->local, 0, &gc); + ifconfig_remote_netmask = print_in_addr_t(tt->remote_netmask, 0, &gc); +#endif - if (do_ipv6) - { - argv_printf(&argv, "%s %s inet6 unplumb", - IFCONFIG_PATH, actual ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, 0, NULL); +#if defined(TARGET_LINUX) + if (net_iface_mtu_set(ctx, ifname, tun_mtu) < 0) + { + msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, ifname); + } - 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, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6, - ifconfig_ipv6_remote - ); - } - else /* tap mode */ - { - /* base IPv6 tap interface needs to be brought up first - */ - argv_printf(&argv, "%s %s inet6 plumb up", - IFCONFIG_PATH, actual ); - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 (prepare) failed")) - { - solaris_error_close(tt, es, actual, true); - } + if (net_iface_up(ctx, ifname, true) < 0) + { + msg(M_FATAL, "Linux can't bring %s up", ifname); + } - /* we might need to do "ifconfig %s inet6 auto-dhcp drop" - * after the system has noticed the interface and fired up - * the DHCPv6 client - but this takes quite a while, and the - * server will ignore the DHCPv6 packets anyway. So we don't. - */ - - /* static IPv6 addresses need to go to a subinterface (tap0:1) - */ - argv_printf(&argv, - "%s %s inet6 addif %s/%d up", - IFCONFIG_PATH, actual, - ifconfig_ipv6_local, tt->netbits_ipv6 ); - } - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 failed")) - { - solaris_error_close(tt, es, actual, true); - } + if (tun) + { + if (net_addr_ptp_v4_add(ctx, ifname, &tt->local, + &tt->remote_netmask) < 0) + { + msg(M_FATAL, "Linux can't add IP to interface %s", ifname); } - - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) + } + else + { + if (net_addr_v4_add(ctx, ifname, &tt->local, + netmask_to_netbits2(tt->remote_netmask)) < 0) { - /* Add a network route for the local tun interface */ - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED | RT_METRIC_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = tt->local; - r.metric = 0; - add_route(&r, tt, 0, NULL, es); + msg(M_FATAL, "Linux can't add IP to interface %s", ifname); } + } +#elif defined(TARGET_ANDROID) + char out[64]; - tt->did_ifconfig = true; + char *top; + switch (tt->topology) + { + case TOP_NET30: + top = "net30"; + break; -#elif defined(TARGET_OPENBSD) + case TOP_P2P: + top = "p2p"; + break; - in_addr_t remote_end; /* for "virtual" subnet topology */ + case TOP_SUBNET: + top = "subnet"; + break; - /* - * On OpenBSD, tun interfaces are persistent if created with - * "ifconfig tunX create", and auto-destroyed if created by - * opening "/dev/tunX" (so we just use the /dev/tunX) - */ + default: + top = "undef"; + } - /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up -link0", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && 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, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s link0", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig failed"); + openvpn_snprintf(out, sizeof(out), "%s %s %d %s", ifconfig_local, + ifconfig_remote_netmask, tun_mtu, top); + management_android_control(management, "IFCONFIG", out); - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) +#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 + * ifconfig tun2 netmask 255.255.255.255 + */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_local, ifconfig_remote_netmask, tun_mtu); + + argv_msg(M_INFO, &argv); + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-1 failed")) { - 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); + solaris_error_close(tt, es, ifname, false); } - if (do_ipv6) - { - 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, es, S_FATAL, "OpenBSD ifconfig inet6 failed"); + argv_printf(&argv, "%s %s netmask 255.255.255.255", IFCONFIG_PATH, + ifname); + } + else if (tt->topology == TOP_SUBNET) + { + argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask); + } - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } - tt->did_ifconfig = true; + argv_msg(M_INFO, &argv); + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed")) + { + solaris_error_close(tt, es, ifname, false); + } -#elif defined(TARGET_NETBSD) + if (!tun && tt->topology == TOP_SUBNET) + { + /* Add a network route for the local tun interface */ + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED | RT_METRIC_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = tt->local; + r.metric = 0; + add_route(&r, tt, 0, NULL, es, NULL); + } - in_addr_t remote_end; /* for "virtual" subnet topology */ +#elif defined(TARGET_OPENBSD) - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && 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, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - /* - * NetBSD has distinct tun and tap devices - * so we don't need the "link0" extra parameter to specify we want to do - * tunneling at the ethernet level - */ - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig failed"); + in_addr_t remote_end; /* for "virtual" subnet topology */ - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_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); - } + /* + * On OpenBSD, tun interfaces are persistent if created with + * "ifconfig tunX create", and auto-destroyed if created by + * opening "/dev/tunX" (so we just use the /dev/tunX) + */ - if (do_ipv6) - { - 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, es, S_FATAL, "NetBSD ifconfig inet6 failed"); + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, + "%s %s %s %s mtu %d netmask 255.255.255.255 up -link0", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + 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, ifname, ifconfig_local, + print_in_addr_t(remote_end, 0, &gc), tun_mtu, + ifconfig_remote_netmask); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s mtu %d link0", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig failed"); - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } - tt->did_ifconfig = true; + /* 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, NULL); + } -#elif defined(TARGET_DARWIN) +#elif defined(TARGET_NETBSD) + in_addr_t remote_end; /* for "virtual" subnet topology */ + + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + 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, + ifname, ifconfig_local, print_in_addr_t(remote_end, 0, &gc), + tun_mtu, ifconfig_remote_netmask); + } + else + { /* - * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD... + * NetBSD has distinct tun and tap devices + * so we don't need the "link0" extra parameter to specify we want to do + * tunneling at the ethernet level */ + argv_printf(&argv, "%s %s %s netmask %s mtu %d", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig failed"); - argv_printf(&argv, - "%s %s delete", - IFCONFIG_PATH, - actual); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, 0, NULL); - msg(M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure"); + /* 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, NULL); + } + +#elif defined(TARGET_DARWIN) + /* + * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD... + */ + argv_printf(&argv, "%s %s delete", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, 0, NULL); + msg(M_INFO, + "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure"); - /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ - if (tun) + + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else + { + if (tt->topology == TOP_SUBNET) { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); + argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", + IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); } else { - if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - argv_printf(&argv, - "%s %s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - 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, es, S_FATAL, "Mac OS X ifconfig failed"); - tt->did_ifconfig = true; - - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_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 = tt->local; - add_route(&r, tt, 0, NULL, es); + argv_printf(&argv, "%s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_remote_netmask, + tun_mtu); } + } - if (do_ipv6) - { - 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, es, S_FATAL, "MacOS X ifconfig inet6 failed"); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "Mac OS X ifconfig failed"); - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } + /* 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 = tt->local; + add_route(&r, tt, 0, NULL, es, NULL); + } #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) - in_addr_t remote_end; /* for "virtual" subnet topology */ + 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, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && 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, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + 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, + ifname, ifconfig_local, print_in_addr_t(remote_end, 0, &gc), + tun_mtu, ifconfig_remote_netmask); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); + } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig failed"); - tt->did_ifconfig = true; + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig failed"); - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_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); - } + /* 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, NULL); + } - if (do_ipv6) +#elif defined(TARGET_AIX) + { + /* 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) { - 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, es, S_FATAL, "FreeBSD ifconfig inet6 failed"); + msg(M_FATAL, "no tun support on AIX (canthappen)"); } -#elif defined(TARGET_AIX) - { - /* 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" ); + /* 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, + ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); - if (tun) - { - msg(M_FATAL, "no tun support on AIX (canthappen)"); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, aix_es, S_FATAL, "AIX ifconfig failed"); - /* 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) - { - 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); - } + env_set_destroy(aix_es); + } #elif defined (_WIN32) - { - ASSERT(actual != NULL); + ASSERT(ifname != NULL); - switch (tt->options.ip_win32_type) - { - case IPW32_SET_MANUAL: - msg(M_INFO, "******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)", - actual, - ifconfig_local, - print_in_addr_t(tt->adapter_netmask, 0, &gc)); - break; + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + msg(M_INFO, + "******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)", + ifname, ifconfig_local, + print_in_addr_t(tt->adapter_netmask, 0, &gc)); + } + else if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ || tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* Let the DHCP configure the interface. */ + } + else if (tt->options.msg_channel) + { + do_address_service(true, AF_INET, tt); + do_dns_service(true, AF_INET, tt); + } + else if (tt->options.ip_win32_type == IPW32_SET_NETSH) + { + netsh_ifconfig(&tt->options, ifname, tt->local, + tt->adapter_netmask, NI_IP_NETMASK|NI_OPTIONS); + } + if (tt->options.msg_channel) + { + do_set_mtu_service(tt, AF_INET, tun_mtu); + } + else + { + windows_set_mtu(tt->adapter_index, AF_INET, tun_mtu); + } +#else /* if defined(TARGET_LINUX) */ + 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."); +#endif /* if defined(TARGET_LINUX) */ - case IPW32_SET_NETSH: - netsh_ifconfig(&tt->options, - actual, - tt->local, - tt->adapter_netmask, - NI_IP_NETMASK|NI_OPTIONS); +#if !defined(TARGET_LINUX) + gc_free(&gc); + argv_free(&argv); +#endif +} - break; - } - tt->did_ifconfig = true; - } +/* execute the ifconfig command through the shell */ +void +do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) +{ + msg(D_LOW, "do_ifconfig, ipv4=%d, ipv6=%d", tt->did_ifconfig_setup, + tt->did_ifconfig_ipv6_setup); - if (do_ipv6) - { - if (tt->options.ip_win32_type == IPW32_SET_MANUAL) - { - msg(M_INFO, "******** NOTE: Please manually set the v6 IP of '%s' to %s (if it is not already set)", - actual, - ifconfig_ipv6_local); - } - else if (tt->options.msg_channel) - { - do_address_service(true, AF_INET6, tt); - do_dns6_service(true, 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); - /* set ipv6 dns servers if any are specified */ - netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, actual); - } +#ifdef ENABLE_MANAGEMENT + if (management) + { + management_set_state(management, + OPENVPN_STATE_ASSIGN_IP, + NULL, + &tt->local, + &tt->local_ipv6, + NULL, + NULL); + } +#endif - /* explicit route needed */ - if (tt->options.ip_win32_type != IPW32_SET_MANUAL) - { - add_route_connected_v6_net(tt, es); - } - } -#else /* if defined(TARGET_LINUX) */ - 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."); -#endif /* if defined(TARGET_LINUX) */ - argv_reset(&argv); + if (tt->did_ifconfig_setup) + { + do_ifconfig_ipv4(tt, ifname, tun_mtu, es, ctx); } - gc_free(&gc); + + if (tt->did_ifconfig_ipv6_setup) + { + do_ifconfig_ipv6(tt, ifname, tun_mtu, es, ctx); + } + + /* release resources potentially allocated during interface setup */ + net_ctx_free(ctx); } static void @@ -1913,13 +1815,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int @@ -2065,12 +1966,14 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun ASSERT(0); } -#endif /* !PENDANTIC */ +#endif /* !PEDANTIC */ #ifdef ENABLE_FEATURE_TUN_PERSIST 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) +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, openvpn_net_ctx_t *ctx) { struct tuntap *tt; @@ -2109,86 +2012,95 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_ msg(M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", groupname, dev); } } - close_tun(tt); + close_tun(tt, ctx); msg(M_INFO, "Persist state set to: %s", (persist_mode ? "ON" : "OFF")); } #endif /* ENABLE_FEATURE_TUN_PERSIST */ -void -close_tun(struct tuntap *tt) +static void +undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) +#if defined(TARGET_LINUX) + int netbits = netmask_to_netbits2(tt->remote_netmask); + + if (is_tun_p2p(tt)) + { + if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local, + &tt->remote_netmask) < 0) + { + msg(M_WARN, "Linux can't del IP from iface %s", + tt->actual_name); + } + } + else { - if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig) + if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0) { - struct argv argv = argv_new(); - struct gc_arena gc = gc_new(); + msg(M_WARN, "Linux can't del IP from iface %s", + tt->actual_name); + } + } +#else /* ifndef TARGET_LINUX */ + struct argv argv = argv_new(); -#ifdef ENABLE_IPROUTE - if (is_tun_p2p(tt)) - { - argv_printf(&argv, - "%s addr del dev %s local %s peer %s", - iproute_path, - tt->actual_name, - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->remote_netmask, 0, &gc) - ); - } - else - { - argv_printf(&argv, - "%s addr del dev %s %s/%d", - iproute_path, - tt->actual_name, - print_in_addr_t(tt->local, 0, &gc), - netmask_to_netbits2(tt->remote_netmask) - ); - } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, - "%s %s 0.0.0.0", - IFCONFIG_PATH, - tt->actual_name - ); -#endif /* ifdef ENABLE_IPROUTE */ - - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ip addr del failed"); - - if (tt->did_ifconfig_ipv6_setup) - { - const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); - -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 addr del %s/%d dev %s", - iproute_path, - ifconfig_ipv6_local, - tt->netbits_ipv6, - tt->actual_name - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed"); -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, - "%s %s del %s/%d", - IFCONFIG_PATH, - tt->actual_name, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ifconfig inet6 del failed"); -#endif - } + argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed"); + + argv_free(&argv); +#endif /* ifdef TARGET_LINUX */ +} - argv_reset(&argv); - gc_free(&gc); +static void +undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ +#if defined(TARGET_LINUX) + if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6, + tt->netbits_ipv6) < 0) + { + msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name); + } +#else /* ifndef TARGET_LINUX */ + struct gc_arena gc = gc_new(); + const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc); + struct argv argv = argv_new(); + + argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name, + ifconfig_ipv6_local, tt->netbits_ipv6); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed"); + + argv_free(&argv); + gc_free(&gc); +#endif /* ifdef TARGET_LINUX */ +} + +void +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + ASSERT(tt); + + if (tt->type != DEV_TYPE_NULL) + { + if (tt->did_ifconfig_setup) + { + undo_ifconfig_ipv4(tt, ctx); } - close_tun_generic(tt); - free(tt); + + if (tt->did_ifconfig_ipv6_setup) + { + undo_ifconfig_ipv6(tt, ctx); + } + + /* release resources potentially allocated during undo */ + net_ctx_reset(ctx); } + + close_tun_generic(tt); + free(tt); } int @@ -2446,57 +2358,54 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun static void solaris_close_tun(struct tuntap *tt) { - if (tt) + /* IPv6 interfaces need to be 'manually' de-configured */ + if (tt->did_ifconfig_ipv6_setup) { - /* IPv6 interfaces need to be 'manually' de-configured */ - if (tt->did_ifconfig_ipv6_setup) + struct argv argv = argv_new(); + argv_printf( &argv, "%s %s inet6 unplumb", + IFCONFIG_PATH, tt->actual_name ); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed"); + argv_free(&argv); + } + + if (tt->ip_fd >= 0) + { + struct lifreq ifr; + CLEAR(ifr); + strncpynt(ifr.lifr_name, tt->actual_name, sizeof(ifr.lifr_name)); + + if (ioctl(tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0) { - struct argv argv = argv_new(); - argv_printf( &argv, "%s %s inet6 unplumb", - IFCONFIG_PATH, tt->actual_name ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed"); - argv_reset(&argv); + msg(M_WARN | M_ERRNO, "Can't get iface flags"); } - if (tt->ip_fd >= 0) + if (ioctl(tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0) { - struct lifreq ifr; - CLEAR(ifr); - strncpynt(ifr.lifr_name, tt->actual_name, sizeof(ifr.lifr_name)); - - if (ioctl(tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0) - { - msg(M_WARN | M_ERRNO, "Can't get iface flags"); - } - - if (ioctl(tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0) - { - msg(M_WARN | M_ERRNO, "Can't get multiplexor id"); - } - - if (tt->type == DEV_TYPE_TAP) - { - if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0) - { - msg(M_WARN | M_ERRNO, "Can't unlink interface(arp)"); - } - } + msg(M_WARN | M_ERRNO, "Can't get multiplexor id"); + } - if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0) + if (tt->type == DEV_TYPE_TAP) + { + if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0) { - msg(M_WARN | M_ERRNO, "Can't unlink interface(ip)"); + msg(M_WARN | M_ERRNO, "Can't unlink interface(arp)"); } - - close(tt->ip_fd); - tt->ip_fd = -1; } - if (tt->fd >= 0) + if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0) { - close(tt->fd); - tt->fd = -1; + msg(M_WARN | M_ERRNO, "Can't unlink interface(ip)"); } + + close(tt->ip_fd); + tt->ip_fd = -1; + } + + if (tt->fd >= 0) + { + close(tt->fd); + tt->fd = -1; } } @@ -2504,20 +2413,19 @@ solaris_close_tun(struct tuntap *tt) * Close TUN device. */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - solaris_close_tun(tt); + ASSERT(tt); - if (tt->actual_name) - { - free(tt->actual_name); - } + solaris_close_tun(tt); - clear_tuntap(tt); - free(tt); + if (tt->actual_name) + { + free(tt->actual_name); } + + clear_tuntap(tt); + free(tt); } static void @@ -2541,9 +2449,9 @@ solaris_error_close(struct tuntap *tt, const struct env_set *es, argv_msg(M_INFO, &argv); openvpn_execve_check(&argv, es, 0, "Solaris ifconfig unplumb failed"); - close_tun(tt); + close_tun(tt, NULL); msg(M_FATAL, "Solaris ifconfig failed"); - argv_reset(&argv); + argv_free(&argv); } int @@ -2604,33 +2512,34 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { + ASSERT(tt); + /* only *TAP* devices need destroying, tun devices auto-self-destruct */ - if (tt && (tt->type == DEV_TYPE_TUN || tt->persistent_if ) ) + if (tt->type == DEV_TYPE_TUN || tt->persistent_if) { close_tun_generic(tt); free(tt); + return; } - else if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } int @@ -2686,36 +2595,37 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun /* the current way OpenVPN handles tun devices on NetBSD leads to * lingering tunX interfaces after close -> for a full cleanup, they - * need to be explicitely destroyed + * need to be explicitly destroyed */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { + ASSERT(tt); + /* only tun devices need destroying, tap devices auto-self-destruct */ - if (tt && ( tt->type != DEV_TYPE_TUN || tt->persistent_if ) ) + if (tt->type != DEV_TYPE_TUN || tt->persistent_if) { close_tun_generic(tt); free(tt); + return; } - else if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "NetBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "NetBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } static inline int @@ -2829,30 +2739,34 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun * we need to call "ifconfig ... destroy" for cleanup */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt && tt->persistent_if) /* keep pre-existing if around */ + ASSERT(tt); + + if (tt->persistent_if) /* keep pre-existing if around */ { close_tun_generic(tt); free(tt); + return; } - else if (tt) /* close and destroy */ - { - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + /* close and destroy */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "FreeBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, + "FreeBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } int @@ -2941,13 +2855,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int @@ -3037,14 +2950,16 @@ utun_open_helper(struct ctl_info ctlInfo, int utunnum) if (fd < 0) { - msg(M_INFO | M_ERRNO, "Opening utun (socket(SYSPROTO_CONTROL))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (socket(SYSPROTO_CONTROL))", + utunnum); return -2; } if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) { close(fd); - msg(M_INFO | M_ERRNO, "Opening utun (ioctl(CTLIOCGINFO))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (ioctl(CTLIOCGINFO))", + utunnum); return -2; } @@ -3062,7 +2977,8 @@ utun_open_helper(struct ctl_info ctlInfo, int utunnum) if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) { - msg(M_INFO | M_ERRNO, "Opening utun (connect(AF_SYS_CONTROL))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (connect(AF_SYS_CONTROL))", + utunnum); close(fd); return -1; } @@ -3105,11 +3021,18 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st /* try to open first available utun device if no specific utun is requested */ if (utunnum == -1) { - for (utunnum = 0; utunnum<255; utunnum++) + for (utunnum = 0; utunnum < 255; utunnum++) { + char ifname[20]; + /* if the interface exists silently skip it */ + ASSERT(snprintf(ifname, sizeof(ifname), "utun%d", utunnum) > 0); + if (if_nametoindex(ifname)) + { + continue; + } fd = utun_open_helper(ctlInfo, utunnum); /* Break if the fd is valid, - * or if early initalization failed (-2) */ + * or if early initialization failed (-2) */ if (fd !=-1) { break; @@ -3198,29 +3121,28 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); + ASSERT(tt); - if (tt->did_ifconfig_ipv6_setup) - { - const char *ifconfig_ipv6_local = - print_in6_addr(tt->local_ipv6, 0, &gc); + struct gc_arena gc = gc_new(); + struct argv argv = argv_new(); - argv_printf(&argv, "%s delete -inet6 %s", - ROUTE_PATH, ifconfig_ipv6_local ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)"); - } + if (tt->did_ifconfig_ipv6_setup) + { + const char *ifconfig_ipv6_local = + print_in6_addr(tt->local_ipv6, 0, &gc); - close_tun_generic(tt); - free(tt); - argv_reset(&argv); - gc_free(&gc); + argv_printf(&argv, "%s delete -inet6 %s", + ROUTE_PATH, ifconfig_ipv6_local ); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)"); } + + close_tun_generic(tt); + free(tt); + argv_free(&argv); + gc_free(&gc); } int @@ -3323,6 +3245,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun env_set_add( es, "ODMDIR=/etc/objrepos" ); openvpn_execve_check(&argv, es, S_FATAL, "AIX 'create tun interface' failed"); env_set_destroy(es); + argv_free(&argv); } else { @@ -3346,17 +3269,13 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun /* tap devices need to be manually destroyed on AIX */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - struct gc_arena gc = gc_new(); + ASSERT(tt); + 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) @@ -3377,6 +3296,7 @@ close_tun(struct tuntap *tt) free(tt); env_set_destroy(es); + argv_free(&argv); } int @@ -3393,6 +3313,22 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len) #elif defined(_WIN32) +static const char * +print_windows_driver(enum windows_driver_type windows_driver) +{ + switch (windows_driver) + { + case WINDOWS_DRIVER_TAP_WINDOWS6: + return "tap-windows6"; + + case WINDOWS_DRIVER_WINTUN: + return "wintun"; + + default: + return "unspecified"; + } +} + int tun_read_queue(struct tuntap *tt, int maxsize) { @@ -3604,7 +3540,123 @@ tun_finalize( return ret; } -const struct tap_reg * +static const struct device_instance_id_interface * +get_device_instance_id_interface(struct gc_arena *gc) +{ + HDEVINFO dev_info_set; + DWORD err; + struct device_instance_id_interface *first = NULL; + struct device_instance_id_interface *last = NULL; + + dev_info_set = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + if (dev_info_set == INVALID_HANDLE_VALUE) + { + err = GetLastError(); + msg(M_FATAL, "Error [%u] opening device information set key: %s", (unsigned int)err, strerror_win32(err, gc)); + } + + for (DWORD i = 0;; ++i) + { + SP_DEVINFO_DATA device_info_data; + BOOL res; + HKEY dev_key; + char net_cfg_instance_id_string[] = "NetCfgInstanceId"; + char net_cfg_instance_id[256]; + char device_instance_id[256]; + DWORD len; + DWORD data_type; + LONG status; + ULONG dev_interface_list_size; + CONFIGRET cr; + struct buffer dev_interface_list; + + ZeroMemory(&device_info_data, sizeof(SP_DEVINFO_DATA)); + device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + res = SetupDiEnumDeviceInfo(dev_info_set, i, &device_info_data); + if (!res) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + { + break; + } + else + { + continue; + } + } + + dev_key = SetupDiOpenDevRegKey(dev_info_set, &device_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (dev_key == INVALID_HANDLE_VALUE) + { + continue; + } + + len = sizeof(net_cfg_instance_id); + data_type = REG_SZ; + status = RegQueryValueEx(dev_key, + net_cfg_instance_id_string, + NULL, + &data_type, + net_cfg_instance_id, + &len); + if (status != ERROR_SUCCESS) + { + goto next; + } + + len = sizeof(device_instance_id); + res = SetupDiGetDeviceInstanceId(dev_info_set, &device_info_data, device_instance_id, len, &len); + if (!res) + { + goto next; + } + + cr = CM_Get_Device_Interface_List_Size(&dev_interface_list_size, + (LPGUID)&GUID_DEVINTERFACE_NET, + device_instance_id, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + + if (cr != CR_SUCCESS) + { + goto next; + } + + dev_interface_list = alloc_buf_gc(dev_interface_list_size, gc); + cr = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_NET, device_instance_id, + BPTR(&dev_interface_list), + dev_interface_list_size, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + { + goto next; + } + + struct device_instance_id_interface *dev_if; + ALLOC_OBJ_CLEAR_GC(dev_if, struct device_instance_id_interface, gc); + dev_if->net_cfg_instance_id = string_alloc(net_cfg_instance_id, gc); + dev_if->device_interface_list = string_alloc(BSTR(&dev_interface_list), gc); + + /* link into return list */ + if (!first) + { + first = dev_if; + } + if (last) + { + last->next = dev_if; + } + last = dev_if; + +next: + RegCloseKey(dev_key); + } + + SetupDiDestroyDeviceInfoList(dev_info_set); + + return first; +} + +static const struct tap_reg * get_tap_reg(struct gc_arena *gc) { HKEY adapter_key; @@ -3700,12 +3752,24 @@ get_tap_reg(struct gc_arena *gc) if (status == ERROR_SUCCESS && data_type == REG_SZ) { - if (!strcmp(component_id, TAP_WIN_COMPONENT_ID) || - !strcmp(component_id, "root\\" TAP_WIN_COMPONENT_ID)) + /* Is this adapter supported? */ + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + if (strcasecmp(component_id, TAP_WIN_COMPONENT_ID) == 0 + || strcasecmp(component_id, "root\\" TAP_WIN_COMPONENT_ID) == 0) + { + windows_driver = WINDOWS_DRIVER_TAP_WINDOWS6; + } + else if (strcasecmp(component_id, WINTUN_COMPONENT_ID) == 0) + { + windows_driver = WINDOWS_DRIVER_WINTUN; + } + + if (windows_driver != WINDOWS_DRIVER_UNSPECIFIED) { struct tap_reg *reg; ALLOC_OBJ_CLEAR_GC(reg, struct tap_reg, gc); reg->guid = string_alloc(net_cfg_instance_id, gc); + reg->windows_driver = windows_driver; /* link into return list */ if (!first) @@ -3729,7 +3793,7 @@ get_tap_reg(struct gc_arena *gc) return first; } -const struct panel_reg * +static const struct panel_reg * get_panel_reg(struct gc_arena *gc) { LONG status; @@ -3936,7 +4000,7 @@ show_tap_win_adapters(int msglev, int warnlev) const struct tap_reg *tap_reg = get_tap_reg(&gc); const struct panel_reg *panel_reg = get_panel_reg(&gc); - msg(msglev, "Available TAP-WIN32 adapters [name, GUID]:"); + msg(msglev, "Available TAP-WIN32 / Wintun adapters [name, GUID, driver]:"); /* loop through each TAP-Windows adapter registry entry */ for (tr = tap_reg; tr != NULL; tr = tr->next) @@ -3948,7 +4012,7 @@ show_tap_win_adapters(int msglev, int warnlev) { if (!strcmp(tr->guid, pr->guid)) { - msg(msglev, "'%s' %s", pr->name, tr->guid); + msg(msglev, "'%s' %s %s", pr->name, tr->guid, print_windows_driver(tr->windows_driver)); ++links; } } @@ -3998,10 +4062,10 @@ show_tap_win_adapters(int msglev, int warnlev) } /* - * Confirm that GUID is a TAP-Windows adapter. + * Lookup a TAP-Windows or Wintun adapter by GUID. */ -static bool -is_tap_win(const char *guid, const struct tap_reg *tap_reg) +static const struct tap_reg * +get_adapter_by_guid(const char *guid, const struct tap_reg *tap_reg) { const struct tap_reg *tr; @@ -4009,11 +4073,11 @@ is_tap_win(const char *guid, const struct tap_reg *tap_reg) { if (guid && !strcmp(tr->guid, guid)) { - return true; + return tr; } } - return false; + return NULL; } static const char * @@ -4032,16 +4096,16 @@ guid_to_name(const char *guid, const struct panel_reg *panel_reg) return NULL; } -static const char * -name_to_guid(const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg) +static const struct tap_reg * +get_adapter_by_name(const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg) { const struct panel_reg *pr; for (pr = panel_reg; pr != NULL; pr = pr->next) { - if (name && !strcmp(pr->name, name) && is_tap_win(pr->guid, tap_reg)) + if (name && !strcmp(pr->name, name)) { - return pr->guid; + return get_adapter_by_guid(pr->guid, tap_reg); } } @@ -4053,7 +4117,7 @@ at_least_one_tap_win(const struct tap_reg *tap_reg) { if (!tap_reg) { - msg(M_FATAL, "There are no TAP-Windows adapters on this system. You should be able to create a TAP-Windows adapter by going to Start -> All Programs -> TAP-Windows -> Utilities -> Add a new TAP-Windows virtual ethernet adapter."); + msg(M_FATAL, "There are no TAP-Windows nor Wintun adapters on this system. You should be able to create an adapter by using tapctl.exe utility."); } } @@ -4067,6 +4131,7 @@ get_unspecified_device_guid(const int device_number, int actual_name_size, const struct tap_reg *tap_reg_src, const struct panel_reg *panel_reg_src, + enum windows_driver_type *windows_driver, struct gc_arena *gc) { const struct tap_reg *tap_reg = tap_reg_src; @@ -4116,23 +4181,29 @@ get_unspecified_device_guid(const int device_number, /* Save GUID for return value */ ret = alloc_buf_gc(256, gc); buf_printf(&ret, "%s", tap_reg->guid); + if (windows_driver != NULL) + { + *windows_driver = tap_reg->windows_driver; + } return BSTR(&ret); } /* * Lookup a --dev-node adapter name in the registry - * returning the GUID and optional actual_name. + * returning the GUID and optional actual_name and device type */ static const char * get_device_guid(const char *name, char *actual_name, int actual_name_size, + enum windows_driver_type *windows_driver, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg, struct gc_arena *gc) { struct buffer ret = alloc_buf_gc(256, gc); struct buffer actual = clear_buf(); + const struct tap_reg *tr; /* Make sure we have at least one TAP adapter */ if (!tap_reg) @@ -4148,7 +4219,8 @@ get_device_guid(const char *name, } /* Check if GUID was explicitly specified as --dev-node parameter */ - if (is_tap_win(name, tap_reg)) + tr = get_adapter_by_guid(name, tap_reg); + if (tr) { const char *act = guid_to_name(name, panel_reg); buf_printf(&ret, "%s", name); @@ -4160,16 +4232,24 @@ get_device_guid(const char *name, { buf_printf(&actual, "%s", name); } + if (windows_driver) + { + *windows_driver = tr->windows_driver; + } return BSTR(&ret); } /* Lookup TAP adapter in network connections list */ { - const char *guid = name_to_guid(name, tap_reg, panel_reg); - if (guid) + tr = get_adapter_by_name(name, tap_reg, panel_reg); + if (tr) { buf_printf(&actual, "%s", name); - buf_printf(&ret, "%s", guid); + if (windows_driver) + { + *windows_driver = tr->windows_driver; + } + buf_printf(&ret, "%s", tr->guid); return BSTR(&ret); } } @@ -4649,8 +4729,7 @@ get_adapter_index_method_1(const char *guid) DWORD index; ULONG aindex; wchar_t wbuf[256]; - swprintf(wbuf, SIZE(wbuf), L"\\DEVICE\\TCPIP_%S", guid); - wbuf [SIZE(wbuf) - 1] = 0; + openvpn_swprintf(wbuf, SIZE(wbuf), L"\\DEVICE\\TCPIP_%S", guid); if (GetAdapterIndex(wbuf, &aindex) != NO_ERROR) { index = TUN_ADAPTER_INDEX_INVALID; @@ -4714,11 +4793,14 @@ get_adapter_index_flexible(const char *name) /* actual name or GUID */ { const struct tap_reg *tap_reg = get_tap_reg(&gc); const struct panel_reg *panel_reg = get_panel_reg(&gc); - const char *guid = name_to_guid(name, tap_reg, panel_reg); - index = get_adapter_index_method_1(guid); - if (index == TUN_ADAPTER_INDEX_INVALID) + const struct tap_reg *tr = get_adapter_by_name(name, tap_reg, panel_reg); + if (tr) { - index = get_adapter_index_method_2(guid); + index = get_adapter_index_method_1(tr->guid); + if (index == TUN_ADAPTER_INDEX_INVALID) + { + index = get_adapter_index_method_2(tr->guid); + } } } if (index == TUN_ADAPTER_INDEX_INVALID) @@ -4851,7 +4933,7 @@ tap_allow_nonadmin_access(const char *dev_node) if (dev_node) { /* Get the device GUID for the device specified with --dev-node. */ - device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), tap_reg, panel_reg, &gc); + device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), NULL, tap_reg, panel_reg, &gc); if (!device_guid) { @@ -4894,6 +4976,7 @@ tap_allow_nonadmin_access(const char *dev_node) sizeof(actual_buffer), tap_reg, panel_reg, + NULL, &gc); if (!device_guid) @@ -5049,19 +5132,19 @@ ipconfig_register_dns(const struct env_set *es) msg(D_TUNTAP_INFO, "Start ipconfig commands for register-dns..."); netcmd_semaphore_lock(); - argv_printf(&argv, "%s%sc /flushdns", + argv_printf(&argv, "%s%s /flushdns", get_win_sys_path(), WIN_IPCONFIG_PATH_SUFFIX); argv_msg(D_TUNTAP_INFO, &argv); openvpn_execve_check(&argv, es, 0, err); - argv_reset(&argv); + argv_free(&argv); - argv_printf(&argv, "%s%sc /registerdns", + argv_printf(&argv, "%s%s /registerdns", get_win_sys_path(), WIN_IPCONFIG_PATH_SUFFIX); argv_msg(D_TUNTAP_INFO, &argv); openvpn_execve_check(&argv, es, 0, err); - argv_reset(&argv); + argv_free(&argv); netcmd_semaphore_release(); msg(D_TUNTAP_INFO, "End ipconfig commands for register-dns..."); @@ -5170,8 +5253,8 @@ netsh_set_dns6_servers(const struct in6_addr *addr_list, for (int i = 0; i < addr_len; ++i) { const char *fmt = (i == 0) ? - "%s%sc interface ipv6 set dns %s static %s" - : "%s%sc interface ipv6 add dns %s %s"; + "%s%s interface ipv6 set dns %s static %s" + : "%s%s interface ipv6 add dns %s %s"; argv_printf(&argv, fmt, get_win_sys_path(), NETSH_PATH_SUFFIX, flex_name, print_in6_addr(addr_list[i], 0, &gc)); @@ -5186,7 +5269,7 @@ netsh_set_dns6_servers(const struct in6_addr *addr_list, netsh_command(&argv, 1, (i==0) ? M_FATAL : M_NONFATAL); } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5201,6 +5284,7 @@ netsh_ifconfig_options(const char *type, struct gc_arena gc = gc_new(); struct argv argv = argv_new(); bool delete_first = false; + bool is_dns = !strcmp(type, "dns"); /* first check if we should delete existing DNS/WINS settings from TAP interface */ if (test_first) @@ -5218,7 +5302,7 @@ netsh_ifconfig_options(const char *type, /* delete existing DNS/WINS settings from TAP interface */ if (delete_first) { - argv_printf(&argv, "%s%sc interface ip delete %s %s all", + argv_printf(&argv, "%s%s interface ip delete %s %s all", get_win_sys_path(), NETSH_PATH_SUFFIX, type, @@ -5235,8 +5319,8 @@ netsh_ifconfig_options(const char *type, if (delete_first || !test_first || !ip_addr_member_of(addr_list[i], current)) { const char *fmt = count ? - "%s%sc interface ip add %s %s %s" - : "%s%sc interface ip set %s %s static %s"; + "%s%s interface ip add %s %s %s" + : "%s%s interface ip set %s %s static %s"; argv_printf(&argv, fmt, get_win_sys_path(), @@ -5244,6 +5328,14 @@ netsh_ifconfig_options(const char *type, type, flex_name, print_in_addr_t(addr_list[i], 0, &gc)); + + /* disable slow address validation on Windows 7 and higher */ + /* only for DNS */ + if (is_dns && win32_version_info() >= WIN_7) + { + argv_printf_cat(&argv, "%s", "validate=no"); + } + netsh_command(&argv, 2, M_FATAL); ++count; @@ -5258,7 +5350,7 @@ netsh_ifconfig_options(const char *type, } } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5312,7 +5404,7 @@ netsh_ifconfig(const struct tuntap_options *to, else { /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */ - argv_printf(&argv, "%s%sc interface ip set address %s static %s %s", + argv_printf(&argv, "%s%s interface ip set address %s static %s %s", get_win_sys_path(), NETSH_PATH_SUFFIX, flex_name, @@ -5349,7 +5441,7 @@ netsh_ifconfig(const struct tuntap_options *to, BOOL_CAST(flags & NI_TEST_FIRST)); } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5360,21 +5452,20 @@ netsh_enable_dhcp(const char *actual_name) /* example: netsh interface ip set address my-tap dhcp */ argv_printf(&argv, - "%s%sc interface ip set address %s dhcp", + "%s%s interface ip set address %s dhcp", get_win_sys_path(), NETSH_PATH_SUFFIX, actual_name); netsh_command(&argv, 4, M_FATAL); - argv_reset(&argv); + argv_free(&argv); } /* Enable dhcp on tap adapter using iservice */ static bool service_enable_dhcp(const struct tuntap *tt) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); @@ -5389,11 +5480,8 @@ service_enable_dhcp(const struct tuntap *tt) .iface = { .index = tt->adapter_index, .name = "" } }; - if (!WriteFile(pipe, &dhcp, sizeof(dhcp), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &dhcp, sizeof(dhcp), &ack, "Enable_dhcp")) { - msg(M_WARN, "Enable_dhcp: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -5413,6 +5501,45 @@ out: return ret; } +static void +windows_set_mtu(const int iface_index, const short family, + const int mtu) +{ + DWORD err = 0; + struct gc_arena gc = gc_new(); + MIB_IPINTERFACE_ROW ipiface; + InitializeIpInterfaceEntry(&ipiface); + const char *family_name = (family == AF_INET6) ? "IPv6" : "IPv4"; + ipiface.Family = family; + ipiface.InterfaceIndex = iface_index; + if (family == AF_INET6 && mtu < 1280) + { + msg(M_INFO, "NOTE: IPv6 interface MTU < 1280 conflicts with IETF standards and might not work"); + } + + err = GetIpInterfaceEntry(&ipiface); + if (err == NO_ERROR) + { + if (family == AF_INET) + { + ipiface.SitePrefixLength = 0; + } + ipiface.NlMtu = mtu; + err = SetIpInterfaceEntry(&ipiface); + } + + if (err != NO_ERROR) + { + msg(M_WARN, "TUN: Setting %s mtu failed: %s [status=%u if_index=%d]", + family_name, strerror_win32(err, &gc), err, iface_index); + } + else + { + msg(M_INFO, "%s MTU set to %d on interface %d using SetIpInterfaceEntry()", family_name, mtu, iface_index); + } +} + + /* * Return a TAP name for netsh commands. */ @@ -5428,13 +5555,13 @@ netsh_get_id(const char *dev_node, struct gc_arena *gc) if (dev_node) { - guid = get_device_guid(dev_node, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, gc); + guid = get_device_guid(dev_node, BPTR(&actual), BCAP(&actual), NULL, tap_reg, panel_reg, gc); } else { - guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, gc); + guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, NULL, gc); - if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */ + if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, NULL, gc)) /* ambiguous if more than one TAP-Windows adapter */ { guid = NULL; } @@ -5556,6 +5683,75 @@ write_dhcp_str(struct buffer *buf, const int type, const char *str, bool *error) buf_write(buf, str, len); } +/* + * RFC3397 states that multiple searchdomains are encoded as follows: + * - at start the length of the entire option is given + * - each subdomain is preceded by its length + * - each searchdomain is separated by a NUL character + * e.g. if you want "openvpn.net" and "duckduckgo.com" then you end up with + * 0x1D 0x7 openvpn 0x3 net 0x00 0x0A duckduckgo 0x3 com 0x00 + */ +static void +write_dhcp_search_str(struct buffer *buf, const int type, const char * const *str_array, + int array_len, bool *error) +{ + char tmp_buf[256]; + int i; + int len = 0; + int label_length_pos; + + for (i=0; i < array_len; i++) + { + const char *ptr = str_array[i]; + + if (strlen(ptr) + len + 1 > sizeof(tmp_buf)) + { + *error = true; + msg(M_WARN, "write_dhcp_search_str: temp buffer overflow building DHCP options"); + return; + } + /* Loop over all subdomains separated by a dot and replace the dot + with the length of the subdomain */ + + /* label_length_pos points to the byte to be replaced by the length + * of the following domain label */ + label_length_pos = len++; + + while (true) + { + if (*ptr == '.' || *ptr == '\0' ) + { + tmp_buf[label_length_pos] = (len-label_length_pos)-1; + label_length_pos = len; + if (*ptr == '\0') + { + break; + } + } + tmp_buf[len++] = *ptr++; + } + /* And close off with an extra NUL char */ + tmp_buf[len++] = 0; + } + + if (!buf_safe(buf, 2 + len)) + { + *error = true; + msg(M_WARN, "write_search_dhcp_str: buffer overflow building DHCP options"); + return; + } + if (len > 255) + { + *error = true; + msg(M_WARN, "write_dhcp_search_str: search domain string must be <= 255 bytes"); + return; + } + + buf_write_u8(buf, type); + buf_write_u8(buf, len); + buf_write(buf, tmp_buf, len); +} + static bool build_dhcp_options_string(struct buffer *buf, const struct tuntap_options *o) { @@ -5580,6 +5776,13 @@ build_dhcp_options_string(struct buffer *buf, const struct tuntap_options *o) write_dhcp_u32_array(buf, 42, (uint32_t *)o->ntp, o->ntp_len, &error); write_dhcp_u32_array(buf, 45, (uint32_t *)o->nbdd, o->nbdd_len, &error); + if (o->domain_search_list_len > 0) + { + write_dhcp_search_str(buf, 119, o->domain_search_list, + o->domain_search_list_len, + &error); + } + /* the MS DHCP server option 'Disable Netbios-over-TCP/IP * is implemented as vendor option 001, value 002. * A value of 001 means 'leave NBT alone' which is the default */ @@ -5618,7 +5821,7 @@ fork_dhcp_action(struct tuntap *tt) { buf_printf(&cmd, " --dhcp-renew"); } - buf_printf(&cmd, " --dhcp-internal %u", (unsigned int)tt->adapter_index); + buf_printf(&cmd, " --dhcp-internal %lu", tt->adapter_index); fork_to_self(BSTR(&cmd)); gc_free(&gc); @@ -5628,18 +5831,16 @@ 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)) + if (!send_msg_iservice(msg_channel, &rdns, sizeof(rdns), &ack, "Register_dns")) { - msg(M_WARN, "Register_dns: could not talk to service: %s [status=0x%lx]", - strerror_win32(GetLastError(), &gc), GetLastError()); + gc_free(&gc); + return; } else if (ack.error_number != NO_ERROR) @@ -5656,6 +5857,46 @@ register_dns_service(const struct tuntap *tt) gc_free(&gc); } +static bool +service_register_ring_buffers(const struct tuntap *tt) +{ + HANDLE msg_channel = tt->options.msg_channel; + ack_message_t ack; + bool ret = true; + struct gc_arena gc = gc_new(); + + register_ring_buffers_message_t msg = { + .header = { + msg_register_ring_buffers, + sizeof(register_ring_buffers_message_t), + 0 + }, + .device = tt->hand, + .send_ring_handle = tt->wintun_send_ring_handle, + .receive_ring_handle = tt->wintun_receive_ring_handle, + .send_tail_moved = tt->rw_handle.read, + .receive_tail_moved = tt->rw_handle.write + }; + + if (!send_msg_iservice(msg_channel, &msg, sizeof(msg), &ack, "Register ring buffers")) + { + ret = false; + } + else if (ack.error_number != NO_ERROR) + { + msg(M_NONFATAL, "Register ring buffers failed using service: %s [status=0x%x]", + strerror_win32(ack.error_number, &gc), ack.error_number); + ret = false; + } + else + { + msg(M_INFO, "Ring buffers registered via service"); + } + + gc_free(&gc); + return ret; +} + void fork_register_dns_action(struct tuntap *tt) { @@ -5704,511 +5945,650 @@ dhcp_masq_addr(const in_addr_t local, const in_addr_t netmask, const int offset) return htonl(dsa); } -void -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +static void +tuntap_get_version_info(const struct tuntap *tt) { - struct gc_arena gc = gc_new(); - char device_path[256]; - const char *device_guid = NULL; + ULONG info[3]; DWORD len; - bool dhcp_masq = false; - bool dhcp_masq_post = false; - - /*netcmd_semaphore_lock ();*/ - - msg( M_INFO, "open_tun"); - - if (tt->type == DEV_TYPE_NULL) + CLEAR(info); + if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, + &info, sizeof(info), + &info, sizeof(info), &len, NULL)) { - open_null(tt); - gc_free(&gc); - return; + msg(D_TUNTAP_INFO, "TAP-Windows Driver Version %d.%d %s", + (int)info[0], + (int)info[1], + (info[2] ? "(DEBUG)" : "")); + } - else if (tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN) + if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR)) { + msg(M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.", + TAP_WIN_MIN_MAJOR, + TAP_WIN_MIN_MINOR); } - else + + /* usage of numeric constants is ugly, but this is really tied to + * *this* version of the driver + */ + if (tt->type == DEV_TYPE_TUN + && info[0] == 9 && info[1] < 8) { - msg(M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev); + 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]); } - /* - * Lookup the device name in the registry, using the --dev-node high level name. + /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy */ + if (tt->type == DEV_TYPE_TUN + && info[0] == 9 && info[1] == 8) { - const struct tap_reg *tap_reg = get_tap_reg(&gc); - const struct panel_reg *panel_reg = get_panel_reg(&gc); - char actual_buffer[256]; - - at_least_one_tap_win(tap_reg); + 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]); + } +} - if (dev_node) - { - /* Get the device GUID for the device specified with --dev-node. */ - device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), tap_reg, panel_reg, &gc); +static void +tuntap_get_mtu(struct tuntap *tt) +{ + ULONG mtu = 0; + DWORD len; + if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, + &mtu, sizeof(mtu), + &mtu, sizeof(mtu), &len, NULL)) + { + tt->post_open_mtu = (int)mtu; + msg(D_MTU_INFO, "TAP-Windows MTU=%d", (int)mtu); + } +} - if (!device_guid) - { - msg(M_FATAL, "TAP-Windows adapter '%s' not found", dev_node); - } +static void +tuntap_set_ip_addr(struct tuntap *tt, + const char *device_guid, + bool dhcp_masq_post) +{ + struct gc_arena gc = gc_new(); + const DWORD index = tt->adapter_index; - /* Open Windows TAP-Windows adapter */ - openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); + /* flush arp cache */ + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 + && index != TUN_ADAPTER_INDEX_INVALID) + { + DWORD status = -1; - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 - ); + 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 (tt->hand == INVALID_HANDLE_VALUE) + if (send_msg_iservice(tt->options.msg_channel, &msg, sizeof(msg), + &ack, "TUN")) { - msg(M_ERR, "CreateFile failed on TAP device: %s", device_path); + status = ack.error_number; } } else { - int device_number = 0; - - /* Try opening all TAP devices until we find one available */ - while (true) - { - device_guid = get_unspecified_device_guid(device_number, - actual_buffer, - sizeof(actual_buffer), - tap_reg, - panel_reg, - &gc); - - if (!device_guid) - { - msg(M_FATAL, "All TAP-Windows adapters on this system are currently in use."); - } - - /* Open Windows TAP-Windows adapter */ - openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); - - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 - ); - - if (tt->hand == INVALID_HANDLE_VALUE) - { - msg(D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path); - } - else - { - break; - } - - device_number++; - } + status = FlushIpNetTable(index); } - /* translate high-level device name into a device instance - * GUID using the registry */ - tt->actual_name = string_alloc(actual_buffer, NULL); - } - - msg(M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path); - tt->adapter_index = get_adapter_index(device_guid); - - /* get driver version info */ - { - ULONG info[3]; - CLEAR(info); - if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, - &info, sizeof(info), - &info, sizeof(info), &len, NULL)) + if (status == NO_ERROR) { - msg(D_TUNTAP_INFO, "TAP-Windows Driver Version %d.%d %s", - (int) info[0], - (int) info[1], - (info[2] ? "(DEBUG)" : "")); - + msg(M_INFO, "Successful ARP Flush on interface [%lu] %s", + index, + device_guid); } - if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR)) + else if (status != -1) { - msg(M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.", - TAP_WIN_MIN_MAJOR, - TAP_WIN_MIN_MINOR); + msg(D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%lu] %s (status=%lu) : %s", + index, + device_guid, + status, + strerror_win32(status, &gc)); } + } - /* usage of numeric constants is ugly, but this is really tied to - * *this* version of the driver - */ - if (tt->type == DEV_TYPE_TUN - && info[0] == 9 && info[1] < 8) + /* + * If the TAP-Windows driver is masquerading as a DHCP server + * make sure the TCP/IP properties for the adapter are + * set correctly. + */ + if (dhcp_masq_post) + { + /* check dhcp enable status */ + if (dhcp_status(index) == DHCP_STATUS_DISABLED) { - 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] ); + msg(M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); } - /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy - */ - if (tt->type == DEV_TYPE_TUN - && info[0] == 9 && info[1] == 8) + /* force an explicit DHCP lease renewal on TAP adapter? */ + if (tt->options.dhcp_pre_release) { - 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] ); + dhcp_release(tt); } + if (tt->options.dhcp_renew) + { + dhcp_renew(tt); + } + } + else + { + fork_dhcp_action(tt); } - /* get driver MTU */ + if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI) { - ULONG mtu; - if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, - &mtu, sizeof(mtu), - &mtu, sizeof(mtu), &len, NULL)) + DWORD status; + const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')"; + + /* couldn't get adapter index */ + if (index == TUN_ADAPTER_INDEX_INVALID) { - tt->post_open_mtu = (int) mtu; - msg(D_MTU_INFO, "TAP-Windows MTU=%d", (int) mtu); + msg(M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s", + device_guid, + error_suffix); } - } - /* - * Preliminaries for setting TAP-Windows adapter TCP/IP - * properties via --ip-win32 dynamic or --ip-win32 adaptive. - */ - if (tt->did_ifconfig_setup) - { - if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ) + /* check dhcp enable status */ + if (dhcp_status(index) == DHCP_STATUS_DISABLED) { - /* - * If adapter is set to non-DHCP, set to DHCP mode. - */ - if (dhcp_status(tt->adapter_index) == DHCP_STATUS_DISABLED) - { - /* try using the service if available, else directly execute netsh */ - if (tt->options.msg_channel) - { - service_enable_dhcp(tt); - } - else - { - netsh_enable_dhcp(tt->actual_name); - } - } - dhcp_masq = true; - dhcp_masq_post = true; + msg(M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); } - else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + + /* delete previously added IP addresses which were not + * correctly deleted */ + delete_temp_addresses(index); + + /* add a new IP address */ + if ((status = AddIPAddress(htonl(tt->local), + htonl(tt->adapter_netmask), + index, + &tt->ipapi_context, + &tt->ipapi_instance)) == NO_ERROR) { - /* - * If adapter is set to non-DHCP, use netsh right away. - */ - if (dhcp_status(tt->adapter_index) != DHCP_STATUS_ENABLED) - { - netsh_ifconfig(&tt->options, - tt->actual_name, - tt->local, - tt->adapter_netmask, - NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS); - } - else - { - dhcp_masq = true; - } + msg(M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid + ); + } + else + { + msg(M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%lu, status=%lu (windows error: '%s') -- %s", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid, + index, + status, + strerror_win32(status, &gc), + error_suffix); } + tt->ipapi_context_defined = true; } - /* set point-to-point mode if TUN device */ + gc_free(&gc); +} - if (tt->type == DEV_TYPE_TUN) +static bool +wintun_register_ring_buffer(struct tuntap *tt, const char *device_guid) +{ + bool ret = true; + + tt->wintun_send_ring = (struct tun_ring *)MapViewOfFile(tt->wintun_send_ring_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof(struct tun_ring)); + + tt->wintun_receive_ring = (struct tun_ring *)MapViewOfFile(tt->wintun_receive_ring_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof(struct tun_ring)); + + if (tt->options.msg_channel) { - if (!tt->did_ifconfig_setup) - { - msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig"); - } + ret = service_register_ring_buffers(tt); + } + else + { + msg(M_FATAL, "ERROR: Wintun requires SYSTEM privileges and therefore " + "should be used with interactive service. If you want to " + "use openvpn from command line, you need to do SYSTEM " + "elevation yourself (for example with psexec)."); + } - if (tt->topology == TOP_SUBNET) - { - in_addr_t ep[3]; - BOOL status; + return ret; +} - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->local & tt->remote_netmask); - ep[2] = htonl(tt->remote_netmask); +static void +tuntap_set_connected(const struct tuntap *tt) +{ + ULONG status = TRUE; + DWORD len; + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, + &status, sizeof(status), + &status, sizeof(status), &len, NULL)) + { + msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + } - status = DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_TUN, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL); + int s = tt->options.tap_sleep; + if (s > 0) + { + msg(M_INFO, "Sleeping for %d seconds...", s); + management_sleep(s); + } +} +static void +tuntap_set_ptp(const struct tuntap *tt) +{ + DWORD len; + struct gc_arena gc = gc_new(); + + if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup) + { + msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig"); + } + + /* send 0/0/0 to the TAP driver even if we have no IPv4 configured to + * ensure it is somehow initialized. + */ + if (!tt->did_ifconfig_setup || tt->topology == TOP_SUBNET) + { + in_addr_t ep[3]; + BOOL status; + + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->local & tt->remote_netmask); + ep[2] = htonl(tt->remote_netmask); + + status = DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_TUN, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL); + + if (tt->did_ifconfig_setup) + { msg(status ? M_INFO : M_FATAL, "Set TAP-Windows TUN subnet mode network/local/netmask = %s/%s/%s [%s]", print_in_addr_t(ep[1], IA_NET_ORDER, &gc), print_in_addr_t(ep[0], IA_NET_ORDER, &gc), print_in_addr_t(ep[2], IA_NET_ORDER, &gc), status ? "SUCCEEDED" : "FAILED"); - } else { + msg(status ? M_INFO : M_FATAL, "Set TAP-Windows TUN with fake IPv4 [%s]", + status ? "SUCCEEDED" : "FAILED"); + } + } + else + { + in_addr_t ep[2]; + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->remote_netmask); - in_addr_t ep[2]; - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->remote_netmask); - - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); - } + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); } } - /* should we tell the TAP-Windows driver to masquerade as a DHCP server as a means - * of setting the adapter address? */ - if (dhcp_masq) - { - uint32_t ep[4]; + gc_free(&gc); +} - /* We will answer DHCP requests with a reply to set IP/subnet to these values */ - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->adapter_netmask); +static void +tuntap_dhcp_mask(const struct tuntap *tt, const char *device_guid) +{ + struct gc_arena gc = gc_new(); + DWORD len; + uint32_t ep[4]; - /* At what IP address should the DHCP server masquerade at? */ - if (tt->type == DEV_TYPE_TUN) + /* We will answer DHCP requests with a reply to set IP/subnet to these values */ + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->adapter_netmask); + + /* At what IP address should the DHCP server masquerade at? */ + if (tt->type == DEV_TYPE_TUN) + { + if (tt->topology == TOP_SUBNET) { - if (tt->topology == TOP_SUBNET) + if (tt->options.dhcp_masq_custom_offset) { - if (tt->options.dhcp_masq_custom_offset) - { - ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, tt->options.dhcp_masq_offset); - } - else - { - ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, -1); - } + ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, tt->options.dhcp_masq_offset); } else { - ep[2] = htonl(tt->remote_netmask); + ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, -1); } } else { - ASSERT(tt->type == DEV_TYPE_TAP); - ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); + ep[2] = htonl(tt->remote_netmask); } + } + else + { + ASSERT(tt->type == DEV_TYPE_TAP); + ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); + } - /* lease time in seconds */ - ep[3] = (uint32_t) tt->options.dhcp_lease_time; + /* lease time in seconds */ + ep[3] = (uint32_t)tt->options.dhcp_lease_time; - ASSERT(ep[3] > 0); + ASSERT(ep[3] > 0); #ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */ - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); - } + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); + } - msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid, - print_in_addr_t(ep[2], IA_NET_ORDER, &gc), - ep[3] - ); + msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid, + print_in_addr_t(ep[2], IA_NET_ORDER, &gc), + ep[3] + ); - /* user-supplied DHCP options capability */ - if (tt->options.dhcp_options) + /* user-supplied DHCP options capability */ + if (tt->options.dhcp_options) + { + struct buffer buf = alloc_buf(256); + if (build_dhcp_options_string(&buf, &tt->options)) { - struct buffer buf = alloc_buf(256); - if (build_dhcp_options_string(&buf, &tt->options)) + msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, + BPTR(&buf), BLEN(&buf), + BPTR(&buf), BLEN(&buf), &len, NULL)) { - msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, - BPTR(&buf), BLEN(&buf), - BPTR(&buf), BLEN(&buf), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); - } - } - else - { - msg(M_WARN, "DHCP option string not set due to error"); + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); } - free_buf(&buf); } -#endif /* ifndef SIMULATE_DHCP_FAILED */ + else + { + msg(M_WARN, "DHCP option string not set due to error"); + } + free_buf(&buf); } +#endif /* ifndef SIMULATE_DHCP_FAILED */ - /* set driver media status to 'connected' */ + gc_free(&gc); +} + +static bool +tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct device_instance_id_interface *device_instance_id_interface) +{ + const char *path = NULL; + char tuntap_device_path[256]; + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) { - ULONG status = TRUE; - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, - &status, sizeof(status), - &status, sizeof(status), &len, NULL)) + const struct device_instance_id_interface *dev_if; + + /* Open Wintun adapter */ + for (dev_if = device_instance_id_interface; dev_if != NULL; dev_if = dev_if->next) { - msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + if (strcmp(dev_if->net_cfg_instance_id, device_guid) == 0) + { + path = dev_if->device_interface_list; + break; + } } + if (path == NULL) + { + return false; + } + } + else + { + /* Open TAP-Windows adapter */ + openvpn_snprintf(tuntap_device_path, sizeof(tuntap_device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAP_WIN_SUFFIX); + path = tuntap_device_path; } - /* possible wait for adapter to come up */ + tt->hand = CreateFile(path, + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0); + if (tt->hand == INVALID_HANDLE_VALUE) { - int s = tt->options.tap_sleep; - if (s > 0) + msg(D_TUNTAP_INFO, "CreateFile failed on %s device: %s", print_windows_driver(tt->windows_driver), path); + return false; + } + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + /* Wintun adapter may be considered "open" after ring buffers are successfuly registered. */ + if (!wintun_register_ring_buffer(tt, device_guid)) { - msg(M_INFO, "Sleeping for %d seconds...", s); - management_sleep(s); + msg(D_TUNTAP_INFO, "Failed to register %s adapter ring buffers", device_guid); + CloseHandle(tt->hand); + tt->hand = NULL; + return false; } } - /* possibly use IP Helper API to set IP address on adapter */ + return true; +} + +static void +tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc) +{ + const struct tap_reg *tap_reg = get_tap_reg(gc); + const struct panel_reg *panel_reg = get_panel_reg(gc); + const struct device_instance_id_interface *device_instance_id_interface = get_device_instance_id_interface(gc); + char actual_buffer[256]; + + at_least_one_tap_win(tap_reg); + + /* + * Lookup the device name in the registry, using the --dev-node high level name. + */ + if (dev_node) { - const DWORD index = tt->adapter_index; + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + + /* Get the device GUID for the device specified with --dev-node. */ + *device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), &windows_driver, tap_reg, panel_reg, gc); - /* flush arp cache */ - if (index != TUN_ADAPTER_INDEX_INVALID) + if (!*device_guid) { - DWORD status = -1; + msg(M_FATAL, "Adapter '%s' not found", dev_node); + } - 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()); - } + if (tt->windows_driver != windows_driver) + { + msg(M_FATAL, "Adapter '%s' is using %s driver, %s expected. If you want to use this device, adjust --windows-driver.", + dev_node, print_windows_driver(windows_driver), print_windows_driver(tt->windows_driver)); + } - status = ack.error_number; - } - else + if (!tun_try_open_device(tt, *device_guid, device_instance_id_interface)) + { + msg(M_FATAL, "Failed to open %s adapter: %s", print_windows_driver(tt->windows_driver), dev_node); + } + } + else + { + int device_number = 0; + + /* Try opening all TAP devices until we find one available */ + while (true) + { + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + *device_guid = get_unspecified_device_guid(device_number, + actual_buffer, + sizeof(actual_buffer), + tap_reg, + panel_reg, + &windows_driver, + gc); + + if (!*device_guid) { - status = FlushIpNetTable(index); + msg(M_FATAL, "All %s adapters on this system are currently in use.", print_windows_driver(tt->windows_driver)); } - if (status == NO_ERROR) + if (tt->windows_driver != windows_driver) { - msg(M_INFO, "Successful ARP Flush on interface [%u] %s", - (unsigned int)index, - device_guid); + goto next; } - else if (status != -1) + + if (tun_try_open_device(tt, *device_guid, device_instance_id_interface)) { - msg(D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", - (unsigned int)index, - device_guid, - (unsigned int)status, - strerror_win32(status, &gc)); + break; } + +next: + device_number++; } + } + + /* translate high-level device name into a device instance + * GUID using the registry */ + tt->actual_name = string_alloc(actual_buffer, NULL); + msg(M_INFO, "%s device [%s] opened", print_windows_driver(tt->windows_driver), tt->actual_name); + tt->adapter_index = get_adapter_index(*device_guid); +} + +static void +tuntap_set_ip_props(const struct tuntap *tt, bool *dhcp_masq, bool *dhcp_masq_post) +{ + if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ) + { /* - * If the TAP-Windows driver is masquerading as a DHCP server - * make sure the TCP/IP properties for the adapter are - * set correctly. + * If adapter is set to non-DHCP, set to DHCP mode. */ - if (dhcp_masq_post) + if (dhcp_status(tt->adapter_index) == DHCP_STATUS_DISABLED) { - /* check dhcp enable status */ - if (dhcp_status(index) == DHCP_STATUS_DISABLED) - { - msg(M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); - } - - /* force an explicit DHCP lease renewal on TAP adapter? */ - if (tt->options.dhcp_pre_release) + /* try using the service if available, else directly execute netsh */ + if (tt->options.msg_channel) { - dhcp_release(tt); + service_enable_dhcp(tt); } - if (tt->options.dhcp_renew) + else { - dhcp_renew(tt); + netsh_enable_dhcp(tt->actual_name); } } + *dhcp_masq = true; + *dhcp_masq_post = true; + } + else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* + * If adapter is set to non-DHCP, use netsh right away. + */ + if (dhcp_status(tt->adapter_index) != DHCP_STATUS_ENABLED) + { + netsh_ifconfig(&tt->options, + tt->actual_name, + tt->local, + tt->adapter_netmask, + NI_TEST_FIRST | NI_IP_NETMASK | NI_OPTIONS); + } else { - fork_dhcp_action(tt); + *dhcp_masq = true; } + } +} - if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI) - { - DWORD status; - const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')"; +static void +tuntap_post_open(struct tuntap *tt, const char *device_guid) +{ + bool dhcp_masq = false; + bool dhcp_masq_post = false; - /* couldn't get adapter index */ - if (index == TUN_ADAPTER_INDEX_INVALID) - { - msg(M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s", - device_guid, - error_suffix); - } + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) + { + /* get driver version info */ + tuntap_get_version_info(tt); - /* check dhcp enable status */ - if (dhcp_status(index) == DHCP_STATUS_DISABLED) - { - msg(M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); - } + /* get driver MTU */ + tuntap_get_mtu(tt); - /* delete previously added IP addresses which were not - * correctly deleted */ - delete_temp_addresses(index); + /* + * Preliminaries for setting TAP-Windows adapter TCP/IP + * properties via --ip-win32 dynamic or --ip-win32 adaptive. + */ + if (tt->did_ifconfig_setup) + { + tuntap_set_ip_props(tt, &dhcp_masq, &dhcp_masq_post); + } - /* add a new IP address */ - if ((status = AddIPAddress(htonl(tt->local), - htonl(tt->adapter_netmask), - index, - &tt->ipapi_context, - &tt->ipapi_instance)) == NO_ERROR) - { - msg(M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid - ); - } - else - { - msg(M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%d, status=%u (windows error: '%s') -- %s", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid, - (int)index, - (unsigned int)status, - strerror_win32(status, &gc), - error_suffix); - } - tt->ipapi_context_defined = true; + /* set point-to-point mode if TUN device */ + if (tt->type == DEV_TYPE_TUN) + { + tuntap_set_ptp(tt); } + + /* should we tell the TAP-Windows driver to masquerade as a DHCP server as a means + * of setting the adapter address? */ + if (dhcp_masq) + { + tuntap_dhcp_mask(tt, device_guid); + } + + /* set driver media status to 'connected' */ + tuntap_set_connected(tt); } - /*netcmd_semaphore_release ();*/ + + /* possibly use IP Helper API to set IP address on adapter */ + tuntap_set_ip_addr(tt, device_guid, dhcp_masq_post); +} + +void +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + const char *device_guid = NULL; + + /*netcmd_semaphore_lock ();*/ + + msg( M_INFO, "open_tun"); + + if (tt->type == DEV_TYPE_NULL) + { + open_null(tt); + return; + } + else if (tt->type != DEV_TYPE_TAP && tt->type != DEV_TYPE_TUN) + { + msg(M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev); + } + + struct gc_arena gc = gc_new(); /* used also for device_guid allocation */ + tun_open_device(tt, dev_node, &device_guid, &gc); + + tuntap_post_open(tt, device_guid); + gc_free(&gc); + + /*netcmd_semaphore_release ();*/ } const char * tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc) { - if (tt && tt->hand != NULL) + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) { struct buffer out = alloc_buf_gc(256, gc); DWORD len; @@ -6226,7 +6606,7 @@ tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc) void tun_show_debug(struct tuntap *tt) { - if (tt && tt->hand != NULL) + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) { struct buffer out = alloc_buf(1024); DWORD len; @@ -6241,107 +6621,160 @@ tun_show_debug(struct tuntap *tt) } } -void -close_tun(struct tuntap *tt) +static void +netsh_delete_address_dns(const struct tuntap *tt, bool ipv6, struct gc_arena *gc) { - struct gc_arena gc = gc_new(); + const char *ifconfig_ip_local; + struct argv argv = argv_new(); - if (tt) + /* delete ipvX dns servers if any were set */ + int len = ipv6 ? tt->options.dns6_len : tt->options.dns_len; + if (len > 0) { - if (tt->did_ifconfig_ipv6_setup) - { - /* remove route pointing to interface */ - delete_route_connected_v6_net(tt, NULL); + argv_printf(&argv, + "%s%s interface %s delete dns %s all", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + ipv6 ? "ipv6" : "ipv4", + tt->actual_name); + netsh_command(&argv, 1, M_WARN); + } - if (tt->options.msg_channel) - { - do_address_service(false, AF_INET6, tt); - if (tt->options.dns6_len > 0) - { - do_dns6_service(false, tt); - } - } - else - { - const char *ifconfig_ipv6_local; - struct argv argv = argv_new(); + if (ipv6) + { + delete_route_connected_v6_net(tt); + } - /* "store=active" is needed in Windows 8(.1) to delete the - * address we added (pointed out by Cedric Tabary). - */ + /* "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 interface ipvX delete address \"%s\" %s */ + if (ipv6) + { + ifconfig_ip_local = print_in6_addr(tt->local_ipv6, 0, gc); + } + else + { + ifconfig_ip_local = print_in_addr_t(tt->local, 0, gc); + } + argv_printf(&argv, + "%s%s interface %s delete address %s %s store=active", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + ipv6 ? "ipv6" : "ipv4", + tt->actual_name, + ifconfig_ip_local); + netsh_command(&argv, 1, M_WARN); - netsh_command(&argv, 1, M_WARN); + argv_free(&argv); +} - /* delete ipv6 dns servers if any were set */ - if (tt->options.dns6_len > 0) - { - argv_printf(&argv, - "%s%sc interface ipv6 delete dns %s all", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - tt->actual_name); - netsh_command(&argv, 1, M_WARN); - } - argv_reset(&argv); - } +void +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + ASSERT(tt); + + struct gc_arena gc = gc_new(); + + if (tt->did_ifconfig_ipv6_setup) + { + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + /* We didn't do ifconfig. */ } -#if 1 - if (tt->ipapi_context_defined) + else if (tt->options.msg_channel) { - DWORD status; - if ((status = DeleteIPAddress(tt->ipapi_context)) != NO_ERROR) + if (tt->options.dns6_len > 0) { - msg(M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Windows adapter, status=%u : %s", - (unsigned int)tt->ipapi_context, - (unsigned int)status, - strerror_win32(status, &gc)); + do_dns_service(false, AF_INET6, tt); } + delete_route_connected_v6_net(tt); + do_address_service(false, AF_INET6, tt); } -#endif - - dhcp_release(tt); + else + { + netsh_delete_address_dns(tt, true, &gc); + } + } - if (tt->hand != NULL) + if (tt->did_ifconfig_setup) + { + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) { - dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Windows adapter"); - if (!CancelIo(tt->hand)) - { - msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Windows adapter"); - } + /* We didn't do ifconfig. */ } + else if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ || tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* We don't have to clean the configuration with DHCP. */ + } + else if (tt->options.msg_channel) + { + do_dns_service(false, AF_INET, tt); + do_address_service(false, AF_INET, tt); + } + else if (tt->options.ip_win32_type == IPW32_SET_NETSH) + { + netsh_delete_address_dns(tt, false, &gc); + } + } - dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter"); - overlapped_io_close(&tt->reads); + if (tt->ipapi_context_defined) + { + DWORD status; + if ((status = DeleteIPAddress(tt->ipapi_context)) != NO_ERROR) + { + msg(M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Windows adapter, status=%u : %s", + (unsigned int)tt->ipapi_context, + (unsigned int)status, + strerror_win32(status, &gc)); + } + } - dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Windows adapter"); - overlapped_io_close(&tt->writes); + dhcp_release(tt); - if (tt->hand != NULL) + if (tt->hand != NULL) + { + dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Windows adapter"); + if (!CancelIo(tt->hand)) { - dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Windows adapter"); - if (!CloseHandle(tt->hand)) - { - msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Windows adapter"); - } + msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Windows adapter"); } + } - if (tt->actual_name) + dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter"); + overlapped_io_close(&tt->reads); + + dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Windows adapter"); + overlapped_io_close(&tt->writes); + + if (tt->hand != NULL) + { + dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Windows adapter"); + if (!CloseHandle(tt->hand)) { - free(tt->actual_name); + msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Windows adapter"); } + } - clear_tuntap(tt); - free(tt); + if (tt->actual_name) + { + free(tt->actual_name); + } + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + CloseHandle(tt->rw_handle.read); + CloseHandle(tt->rw_handle.write); + UnmapViewOfFile(tt->wintun_send_ring); + UnmapViewOfFile(tt->wintun_receive_ring); + CloseHandle(tt->wintun_send_ring_handle); + CloseHandle(tt->wintun_receive_ring_handle); } + + + clear_tuntap(tt); + free(tt); gc_free(&gc); } @@ -6418,13 +6851,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index 54e1dfa..99826cf 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -27,6 +27,8 @@ #ifdef _WIN32 #include <winioctl.h> #include <tap-windows.h> +#include <setupapi.h> +#include <cfgmgr32.h> #endif #include "buffer.h" @@ -36,6 +38,18 @@ #include "event.h" #include "proto.h" #include "misc.h" +#include "networking.h" +#include "ring_buffer.h" + +#ifdef _WIN32 +#define WINTUN_COMPONENT_ID "wintun" + +enum windows_driver_type { + WINDOWS_DRIVER_UNSPECIFIED, + WINDOWS_DRIVER_TAP_WINDOWS6, + WINDOWS_DRIVER_WINTUN +}; +#endif #if defined(_WIN32) || defined(TARGET_ANDROID) @@ -98,6 +112,12 @@ struct tuntap_options { in_addr_t nbdd[N_DHCP_ADDR]; int nbdd_len; +#define N_SEARCH_LIST_LEN 10 /* Max # of entries in domin-search list */ + + /* SEARCH (119), MacOS, Linux, Win10 1809+ */ + const char *domain_search_list[N_SEARCH_LIST_LEN]; + int domain_search_list_len; + /* DISABLE_NBT (43, Vendor option 001) */ bool disable_nbt; @@ -138,7 +158,6 @@ struct tuntap bool did_ifconfig_setup; bool did_ifconfig_ipv6_setup; - bool did_ifconfig; bool persistent_if; /* if existed before, keep on program end */ @@ -152,7 +171,6 @@ struct tuntap /* ifconfig parameters */ in_addr_t local; in_addr_t remote_netmask; - in_addr_t broadcast; struct in6_addr local_ipv6; struct in6_addr remote_ipv6; @@ -175,10 +193,16 @@ struct tuntap * ~0 if undefined */ DWORD adapter_index; + enum windows_driver_type windows_driver; int standby_iter; + + HANDLE wintun_send_ring_handle; + HANDLE wintun_receive_ring_handle; + struct tun_ring *wintun_send_ring; + struct tun_ring *wintun_receive_ring; #else /* ifdef _WIN32 */ int fd; /* file descriptor for TUN/TAP dev */ -#endif +#endif /* ifdef _WIN32 */ #ifdef TARGET_SOLARIS int ip_fd; @@ -205,6 +229,20 @@ tuntap_defined(const struct tuntap *tt) #endif } +#ifdef _WIN32 +static inline bool +tuntap_is_wintun(struct tuntap *tt) +{ + return tt && tt->windows_driver == WINDOWS_DRIVER_WINTUN; +} + +static inline bool +tuntap_ring_empty(struct tuntap *tt) +{ + return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail); +} +#endif + /* * Function prototypes */ @@ -212,7 +250,7 @@ tuntap_defined(const struct tuntap *tt) void open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt); -void close_tun(struct tuntap *tt); +void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx); int write_tun(struct tuntap *tt, uint8_t *buf, int len); @@ -220,7 +258,8 @@ int read_tun(struct tuntap *tt, uint8_t *buf, int len); 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); + const char *groupname, const struct tuntap_options *options, + openvpn_net_ctx_t *ctx); const char *guess_tuntap_dev(const char *dev, const char *dev_type, @@ -238,7 +277,8 @@ struct tuntap *init_tun(const char *dev, /* --dev option */ struct addrinfo *local_public, struct addrinfo *remote_public, const bool strict_warn, - struct env_set *es); + struct env_set *es, + openvpn_net_ctx_t *ctx); void init_tun_post(struct tuntap *tt, const struct frame *frame, @@ -247,10 +287,17 @@ void init_tun_post(struct tuntap *tt, void do_ifconfig_setenv(const struct tuntap *tt, struct env_set *es); -void do_ifconfig(struct tuntap *tt, - const char *actual, /* actual device name */ - int tun_mtu, - const struct env_set *es); +/** + * do_ifconfig - configure the tunnel interface + * + * @param tt the tuntap interface context + * @param ifname the human readable interface name + * @param mtu the MTU value to set the interface to + * @param es the environment to be used when executing the commands + * @param ctx the networking API opaque context + */ +void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx); bool is_dev_type(const char *dev, const char *dev_type, const char *match_type); @@ -266,7 +313,7 @@ void check_subnet_conflict(const in_addr_t ip, const in_addr_t netmask, const char *prefix); -void warn_on_use_of_common_subnets(void); +void warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx); /* * Inline functions @@ -327,11 +374,10 @@ route_order(void) #ifdef _WIN32 -#define TUN_PASS_BUFFER - struct tap_reg { const char *guid; + enum windows_driver_type windows_driver; struct tap_reg *next; }; @@ -342,6 +388,13 @@ struct panel_reg struct panel_reg *next; }; +struct device_instance_id_interface +{ + const char *net_cfg_instance_id; + const char *device_interface_list; + struct device_instance_id_interface *next; +}; + int ascii2ipset(const char *name); const char *ipset2ascii(int index); @@ -457,10 +510,158 @@ read_tun_buffered(struct tuntap *tt, struct buffer *buf) return tun_finalize(tt->hand, &tt->reads, buf); } +static inline ULONG +wintun_ring_packet_align(ULONG size) +{ + return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1); +} + +static inline ULONG +wintun_ring_wrap(ULONG value) +{ + return value & (WINTUN_RING_CAPACITY - 1); +} + +static inline void +read_wintun(struct tuntap *tt, struct buffer *buf) +{ + struct tun_ring *ring = tt->wintun_send_ring; + ULONG head = ring->head; + ULONG tail = ring->tail; + ULONG content_len; + struct TUN_PACKET *packet; + ULONG aligned_packet_size; + + *buf = tt->reads.buf_init; + buf->len = 0; + + if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) + { + msg(M_INFO, "Wintun: ring capacity exceeded"); + buf->len = -1; + return; + } + + if (head == tail) + { + /* nothing to read */ + return; + } + + content_len = wintun_ring_wrap(tail - head); + if (content_len < sizeof(struct TUN_PACKET_HEADER)) + { + msg(M_INFO, "Wintun: incomplete packet header in send ring"); + buf->len = -1; + return; + } + + packet = (struct TUN_PACKET *) &ring->data[head]; + if (packet->size > WINTUN_MAX_PACKET_SIZE) + { + msg(M_INFO, "Wintun: packet too big in send ring"); + buf->len = -1; + return; + } + + aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + packet->size); + if (aligned_packet_size > content_len) + { + msg(M_INFO, "Wintun: incomplete packet in send ring"); + buf->len = -1; + return; + } + + buf_write(buf, packet->data, packet->size); + + head = wintun_ring_wrap(head + aligned_packet_size); + ring->head = head; +} + +static inline bool +is_ip_packet_valid(const struct buffer *buf) +{ + const struct openvpn_iphdr *ih = (const struct openvpn_iphdr *)BPTR(buf); + + if (OPENVPN_IPH_GET_VER(ih->version_len) == 4) + { + if (BLEN(buf) < sizeof(struct openvpn_iphdr)) + { + return false; + } + } + else if (OPENVPN_IPH_GET_VER(ih->version_len) == 6) + { + if (BLEN(buf) < sizeof(struct openvpn_ipv6hdr)) + { + return false; + } + } + else + { + return false; + } + + return true; +} + +static inline int +write_wintun(struct tuntap *tt, struct buffer *buf) +{ + struct tun_ring *ring = tt->wintun_receive_ring; + ULONG head = ring->head; + ULONG tail = ring->tail; + ULONG aligned_packet_size; + ULONG buf_space; + struct TUN_PACKET *packet; + + /* wintun marks ring as corrupted (overcapacity) if it receives invalid IP packet */ + if (!is_ip_packet_valid(buf)) + { + msg(D_LOW, "write_wintun(): drop invalid IP packet"); + return 0; + } + + if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) + { + msg(M_INFO, "write_wintun(): head/tail value is over capacity"); + return -1; + } + + aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + BLEN(buf)); + buf_space = wintun_ring_wrap(head - tail - WINTUN_PACKET_ALIGN); + if (aligned_packet_size > buf_space) + { + msg(M_INFO, "write_wintun(): ring is full"); + return 0; + } + + /* copy packet size and data into ring */ + packet = (struct TUN_PACKET * )&ring->data[tail]; + packet->size = BLEN(buf); + memcpy(packet->data, BPTR(buf), BLEN(buf)); + + /* move ring tail */ + ring->tail = wintun_ring_wrap(tail + aligned_packet_size); + if (ring->alertable != 0) + { + SetEvent(tt->rw_handle.write); + } + + return BLEN(buf); +} + static inline int write_tun_buffered(struct tuntap *tt, struct buffer *buf) { - return tun_write_win32(tt, buf); + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + return write_wintun(tt, buf); + } + else + { + return tun_write_win32(tt, buf); + } } #else /* ifdef _WIN32 */ @@ -504,7 +705,7 @@ tun_event_handle(const struct tuntap *tt) #endif } -static inline unsigned int +static inline void tun_set(struct tuntap *tt, struct event_set *es, unsigned int rwflags, @@ -523,14 +724,13 @@ tun_set(struct tuntap *tt, } } #ifdef _WIN32 - if (rwflags & EVENT_READ) + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ)) { tun_read_queue(tt, 0); } #endif tt->rwflags_debug = rwflags; } - return rwflags; } const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc); diff --git a/src/openvpn/vlan.c b/src/openvpn/vlan.c new file mode 100644 index 0000000..dd8d7c1 --- /dev/null +++ b/src/openvpn/vlan.c @@ -0,0 +1,333 @@ +/* + * 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-2019 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2010 Fabian Knittel <fabian.knittel@lettink.de> + * + * 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; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" + +#include "multi.h" +#include "options.h" +#include "vlan.h" + +/* + * Retrieve the VLAN Identifier (VID) from the IEEE 802.1Q header. + * + * @param hdr Pointer to the Ethernet header with IEEE 802.1Q tagging. + * @return Returns the VID in host byte order. + */ +static uint16_t +vlanhdr_get_vid(const struct openvpn_8021qhdr *hdr) +{ + return ntohs(hdr->pcp_cfi_vid & OPENVPN_8021Q_MASK_VID); +} + +/* + * Set the VLAN Identifier (VID) in an IEEE 802.1Q header. + * + * @param hdr Pointer to the Ethernet header with IEEE 802.1Q tagging. + * @param vid The VID to set (in host byte order). + */ +static void +vlanhdr_set_vid(struct openvpn_8021qhdr *hdr, const uint16_t vid) +{ + hdr->pcp_cfi_vid = (hdr->pcp_cfi_vid & ~OPENVPN_8021Q_MASK_VID) + | (htons(vid) & OPENVPN_8021Q_MASK_VID); +} + +/* + * vlan_decapsulate - remove 802.1q header and return VID + * + * For vlan_accept == VLAN_ONLY_UNTAGGED_OR_PRIORITY: + * Only untagged frames and frames that are priority-tagged (VID == 0) are + * accepted. (This means that VLAN-tagged frames are dropped.) For frames + * that aren't dropped, the global vlan_pvid is returned as VID. + * + * For vlan_accept == VLAN_ONLY_TAGGED: + * If a frame is VLAN-tagged the tagging is removed and the embedded VID is + * returned. Any included priority information is lost. + * If a frame isn't VLAN-tagged, the frame is dropped. + * + * For vlan_accept == VLAN_ALL: + * Accepts both VLAN-tagged and untagged (or priority-tagged) frames and + * and handles them as described above. + * + * @param c The global context. + * @param buf The ethernet frame. + * @return Returns -1 if the frame is dropped or the VID if it is accepted. + */ +int16_t +vlan_decapsulate(const struct context *c, struct buffer *buf) +{ + const struct openvpn_8021qhdr *vlanhdr; + struct openvpn_ethhdr *ethhdr; + uint16_t vid; + + /* assume untagged frame */ + if (BLEN(buf) < sizeof(*ethhdr)) + { + goto drop; + } + + ethhdr = (struct openvpn_ethhdr *)BPTR(buf); + if (ethhdr->proto != htons(OPENVPN_ETH_P_8021Q)) + { + /* reject untagged frame */ + if (c->options.vlan_accept == VLAN_ONLY_TAGGED) + { + msg(D_VLAN_DEBUG, + "dropping frame without vlan-tag (proto/len 0x%04x)", + ntohs(ethhdr->proto)); + goto drop; + } + + /* untagged frame is accepted and associated with the global VID */ + msg(D_VLAN_DEBUG, + "assuming pvid for frame without vlan-tag, pvid: %u (proto/len 0x%04x)", + c->options.vlan_pvid, ntohs(ethhdr->proto)); + + return c->options.vlan_pvid; + } + + /* tagged frame */ + if (BLEN(buf) < sizeof(*vlanhdr)) + { + goto drop; + } + + vlanhdr = (const struct openvpn_8021qhdr *)BPTR(buf); + vid = vlanhdr_get_vid(vlanhdr); + + switch (c->options.vlan_accept) + { + case VLAN_ONLY_UNTAGGED_OR_PRIORITY: + /* VLAN-tagged frame: drop packet */ + if (vid != 0) + { + msg(D_VLAN_DEBUG, "dropping frame with vlan-tag, vid: %u (proto/len 0x%04x)", + vid, ntohs(vlanhdr->proto)); + goto drop; + } + + /* vid == 0 means prio-tagged packet: don't drop and fall-through */ + case VLAN_ONLY_TAGGED: + case VLAN_ALL: + /* tagged frame can be accepted: extract vid and strip encapsulation */ + + /* in case of prio-tagged frame (vid == 0), assume the sender + * knows what he is doing and forward the packet as it is, so to + * keep the priority information intact. + */ + if (vid == 0) + { + /* return the global VID for priority-tagged frames */ + return c->options.vlan_pvid; + } + + /* here we have a proper VLAN tagged frame: perform decapsulation + * and return embedded VID + */ + msg(D_VLAN_DEBUG, + "removing vlan-tag from frame: vid: %u, wrapped proto/len: 0x%04x", + vid, ntohs(vlanhdr->proto)); + + /* save inner protocol to be restored later after decapsulation */ + uint16_t proto = vlanhdr->proto; + /* move the buffer head forward to adjust the headroom to a + * non-tagged frame + */ + buf_advance(buf, SIZE_ETH_TO_8021Q_HDR); + /* move the content of the 802.1q header to the new head, so that + * src/dst addresses are copied over + */ + ethhdr = memmove(BPTR(buf), vlanhdr, sizeof(*ethhdr)); + /* restore the inner protocol value */ + ethhdr->proto = proto; + + return vid; + } + +drop: + buf->len = 0; + return -1; +} + +/* + * vlan_encapsulate - add 802.1q header and set the context related VID + * + * Assumes vlan_accept == VLAN_ONLY_TAGGED + * + * @param c The current context. + * @param buf The ethernet frame to encapsulate. + */ +void +vlan_encapsulate(const struct context *c, struct buffer *buf) +{ + const struct openvpn_ethhdr *ethhdr; + struct openvpn_8021qhdr *vlanhdr; + + if (BLEN(buf) < sizeof(*ethhdr)) + { + goto drop; + } + + ethhdr = (const struct openvpn_ethhdr *)BPTR(buf); + if (ethhdr->proto == htons(OPENVPN_ETH_P_8021Q)) + { + /* Priority-tagged frame. (VLAN-tagged frames have been dropped before + * getting to this point) + */ + + /* Frame too small for header type? */ + if (BLEN(buf) < sizeof(*vlanhdr)) + { + goto drop; + } + + vlanhdr = (struct openvpn_8021qhdr *)BPTR(buf); + + /* sanity check: ensure this packet is really just prio-tagged */ + uint16_t vid = vlanhdr_get_vid(vlanhdr); + if (vid != 0) + { + goto drop; + } + } + else + { + /* Untagged frame. */ + + /* Not enough head room for VLAN tag? */ + if (buf_reverse_capacity(buf) < SIZE_ETH_TO_8021Q_HDR) + { + goto drop; + } + + vlanhdr = (struct openvpn_8021qhdr *)buf_prepend(buf, + SIZE_ETH_TO_8021Q_HDR); + + /* Initialise VLAN/802.1q header. + * Move the Eth header so to keep dst/src addresses the same and then + * assign the other fields. + * + * Also, save the inner protocol first, so that it can be restored later + * after the memmove() + */ + uint16_t proto = ethhdr->proto; + memmove(vlanhdr, ethhdr, sizeof(*ethhdr)); + vlanhdr->tpid = htons(OPENVPN_ETH_P_8021Q); + vlanhdr->pcp_cfi_vid = 0; + vlanhdr->proto = proto; + } + + /* set the VID corresponding to the current context (client) */ + vlanhdr_set_vid(vlanhdr, c->options.vlan_pvid); + + msg(D_VLAN_DEBUG, "tagging frame: vid %u (wrapping proto/len: %04x)", + c->options.vlan_pvid, vlanhdr->proto); + return; + +drop: + /* Drop the frame. */ + buf->len = 0; +} + +/* + * vlan_is_tagged - check if a packet is VLAN-tagged + * + * Checks whether ethernet frame is VLAN-tagged. + * + * @param buf The ethernet frame. + * @return Returns true if the frame is VLAN-tagged, false otherwise. + */ +bool +vlan_is_tagged(const struct buffer *buf) +{ + const struct openvpn_8021qhdr *vlanhdr; + uint16_t vid; + + if (BLEN(buf) < sizeof(struct openvpn_8021qhdr)) + { + /* frame too small to be VLAN-tagged */ + return false; + } + + vlanhdr = (const struct openvpn_8021qhdr *)BPTR(buf); + + if (ntohs(vlanhdr->tpid) != OPENVPN_ETH_P_8021Q) + { + /* non tagged frame */ + return false; + } + + vid = vlanhdr_get_vid(vlanhdr); + if (vid == 0) + { + /* no vid: piority tagged only */ + return false; + } + + return true; +} + +void +vlan_process_outgoing_tun(struct multi_context *m, struct multi_instance *mi) +{ + if (!m->top.options.vlan_tagging) + { + return; + } + + if (m->top.options.vlan_accept == VLAN_ONLY_UNTAGGED_OR_PRIORITY) + { + /* Packets forwarded to the TAP devices aren't VLAN-tagged. Only packets + * matching the PVID configured globally are allowed to be received + */ + if (m->top.options.vlan_pvid != mi->context.options.vlan_pvid) + { + /* Packet is coming from the wrong VID, drop it. */ + mi->context.c2.to_tun.len = 0; + } + } + else if (m->top.options.vlan_accept == VLAN_ALL) + { + /* Packets either need to be VLAN-tagged or not, depending on the + * packet's originating VID and the port's native VID (PVID). */ + + if (m->top.options.vlan_pvid != mi->context.options.vlan_pvid) + { + /* Packets need to be VLAN-tagged, because the packet's VID does not + * match the port's PVID. */ + vlan_encapsulate(&mi->context, &mi->context.c2.to_tun); + } + } + else if (m->top.options.vlan_accept == VLAN_ONLY_TAGGED) + { + /* All packets on the port (the tap device) need to be VLAN-tagged. */ + vlan_encapsulate(&mi->context, &mi->context.c2.to_tun); + } +} diff --git a/src/openvpn/vlan.h b/src/openvpn/vlan.h new file mode 100644 index 0000000..ed25c1d --- /dev/null +++ b/src/openvpn/vlan.h @@ -0,0 +1,44 @@ +/* + * 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-2019 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2010 Fabian Knittel <fabian.knittel@lettink.de> + * + * 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; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef VLAN_H +#define VLAN_H + +#include "buffer.h" +#include "mroute.h" +#include "openvpn.h" + +struct multi_context; +struct multi_instance; + +int16_t +vlan_decapsulate(const struct context *c, struct buffer *buf); + +bool +vlan_is_tagged(const struct buffer *buf); + +void +vlan_process_outgoing_tun(struct multi_context *m, struct multi_instance *mi); + +#endif /* VLAN_H */ diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index f13807f..7e91316 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -22,7 +22,7 @@ */ /* - * Win32-specific OpenVPN code, targetted at the mingw + * Win32-specific OpenVPN code, targeted at the mingw * development environment. */ @@ -39,9 +39,9 @@ #include "buffer.h" #include "error.h" #include "mtu.h" +#include "run_command.h" #include "sig.h" #include "win32.h" -#include "misc.h" #include "openvpn-msg.h" #include "memdbg.h" @@ -1139,7 +1139,7 @@ openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in else { ret = OPENVPN_EXECVE_NOT_ALLOWED; - if (!exec_warn && (script_security < SSEC_SCRIPTS)) + if (!exec_warn && (script_security() < SSEC_SCRIPTS)) { msg(M_WARN, SCRIPT_SECURITY_WARNING); exec_warn = true; @@ -1267,7 +1267,6 @@ win_get_tempdir(void) static bool win_block_dns_service(bool add, int index, const HANDLE pipe) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); @@ -1281,11 +1280,8 @@ win_block_dns_service(bool add, int index, const HANDLE pipe) .iface = { .index = index, .name = "" } }; - if (!WriteFile(pipe, &data, sizeof(data), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &data, sizeof(data), &ack, "Block_DNS")) { - msg(M_WARN, "Block_DNS: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -1421,10 +1417,18 @@ win32_version_info(void) { return WIN_7; } - else + + if (!IsWindows8Point1OrGreater()) { return WIN_8; } + + if (!IsWindows10OrGreater()) + { + return WIN_8_1; + } + + return WIN_10; } bool @@ -1462,7 +1466,15 @@ win32_version_string(struct gc_arena *gc, bool add_name) break; case WIN_8: - buf_printf(&out, "6.2%s", add_name ? " (Windows 8 or greater)" : ""); + buf_printf(&out, "6.2%s", add_name ? " (Windows 8)" : ""); + break; + + case WIN_8_1: + buf_printf(&out, "6.3%s", add_name ? " (Windows 8.1)" : ""); + break; + + case WIN_10: + buf_printf(&out, "10.0%s", add_name ? " (Windows 10 or greater)" : ""); break; default: @@ -1476,4 +1488,25 @@ win32_version_string(struct gc_arena *gc, bool add_name) return (const char *)out.data; } +bool +send_msg_iservice(HANDLE pipe, const void *data, size_t size, + ack_message_t *ack, const char *context) +{ + struct gc_arena gc = gc_new(); + DWORD len; + bool ret = true; + + if (!WriteFile(pipe, data, size, &len, NULL) + || !ReadFile(pipe, ack, sizeof(*ack), &len, NULL)) + { + msg(M_WARN, "%s: could not talk to service: %s [%lu]", + context ? context : "Unknown", + strerror_win32(GetLastError(), &gc), GetLastError()); + ret = false; + } + + gc_free(&gc); + return ret; +} + #endif /* ifdef _WIN32 */ diff --git a/src/openvpn/win32.h b/src/openvpn/win32.h index 4b99a5e..da85ed4 100644 --- a/src/openvpn/win32.h +++ b/src/openvpn/win32.h @@ -25,7 +25,11 @@ #ifndef OPENVPN_WIN32_H #define OPENVPN_WIN32_H +#include <winioctl.h> + #include "mtu.h" +#include "openvpn-msg.h" +#include "argv.h" /* location of executables */ #define SYS_PATH_ENV_VAR_NAME "SystemRoot" /* environmental variable name that normally contains the system path */ @@ -35,7 +39,7 @@ #define WIN_NET_PATH_SUFFIX "\\system32\\net.exe" /* - * Win32-specific OpenVPN code, targetted at the mingw + * Win32-specific OpenVPN code, targeted at the mingw * development environment. */ @@ -65,7 +69,7 @@ struct security_attributes struct window_title { bool saved; - char old_window_title [256]; + char old_window_title[256]; }; struct rw_handle { @@ -294,10 +298,12 @@ bool win_wfp_block_dns(const NET_IFINDEX index, const HANDLE msg_channel); bool win_wfp_uninit(const NET_IFINDEX index, const HANDLE msg_channel); -#define WIN_XP 0 +#define WIN_XP 0 #define WIN_VISTA 1 -#define WIN_7 2 -#define WIN_8 3 +#define WIN_7 2 +#define WIN_8 3 +#define WIN_8_1 4 +#define WIN_10 5 int win32_version_info(void); @@ -307,5 +313,21 @@ int win32_version_info(void); */ const char *win32_version_string(struct gc_arena *gc, bool add_name); +/* + * Send the |size| bytes in buffer |data| to the interactive service |pipe| + * and read the result in |ack|. Returns false on communication error. + * The string in |context| is used to prefix error messages. + */ +bool send_msg_iservice(HANDLE pipe, const void *data, size_t size, + ack_message_t *ack, const char *context); + +/* + * Attempt to simulate fork/execve on Windows + */ +int +openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags); + +bool impersonate_as_system(); + #endif /* ifndef OPENVPN_WIN32_H */ #endif /* ifdef _WIN32 */ |