summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff.email>2021-11-29 20:51:58 +0100
committerJörg Frings-Fürst <debian@jff.email>2021-11-29 20:51:58 +0100
commit6e3e95a9da9458ddf0874b4bd1c8ce6b47fcef27 (patch)
tree6e3667709d99f857d90b9831426f6a32ee70d113
parentb29f419d68b26b75a44e3ac00748875f1003b900 (diff)
parentf2b3dda12a731c2e0971cb7889728edaf23f6cb0 (diff)
Merge branch 'upstream' into develop
-rw-r--r--.gitignore7
-rw-r--r--COPYING2
-rw-r--r--ChangeLog1375
-rw-r--r--Changes.rst764
-rw-r--r--INSTALL28
-rw-r--r--Makefile.am14
-rw-r--r--Makefile.in63
-rw-r--r--PORTS2
-rw-r--r--TODO.IPv66
-rw-r--r--aclocal.m448
-rw-r--r--build/Makefile.am2
-rw-r--r--build/Makefile.in14
-rw-r--r--build/msvc/Makefile.am2
-rw-r--r--build/msvc/Makefile.in14
-rw-r--r--build/msvc/msvc-generate/Makefile.am2
-rw-r--r--build/msvc/msvc-generate/Makefile.in14
-rw-r--r--build/msvc/msvc-generate/Makefile.mak9
-rw-r--r--build/msvc/msvc-generate/msvc-generate.vcxproj91
-rwxr-xr-xcompile6
-rw-r--r--config-msvc.h39
-rw-r--r--config.h.in36
-rwxr-xr-xconfigure708
-rw-r--r--configure.ac225
-rw-r--r--contrib/vcpkg-ports/openssl/portfile.cmake25
-rw-r--r--contrib/vcpkg-ports/openssl/unix/CMakeLists.txt280
-rw-r--r--contrib/vcpkg-ports/openssl/unix/portfile.cmake49
-rw-r--r--contrib/vcpkg-ports/openssl/unix/remove-deps.cmake7
-rw-r--r--contrib/vcpkg-ports/openssl/unix/vcpkg-cmake-wrapper.cmake18
-rw-r--r--contrib/vcpkg-ports/openssl/usage4
-rw-r--r--contrib/vcpkg-ports/openssl/uwp/EnableUWPSupport.patch170
-rw-r--r--contrib/vcpkg-ports/openssl/uwp/make-openssl.bat16
-rw-r--r--contrib/vcpkg-ports/openssl/uwp/portfile.cmake156
-rw-r--r--contrib/vcpkg-ports/openssl/vcpkg.json7
-rw-r--r--contrib/vcpkg-ports/openssl/windows/portfile.cmake174
-rw-r--r--contrib/vcpkg-ports/openssl/windows/vcpkg-cmake-wrapper.cmake10
-rw-r--r--contrib/vcpkg-ports/pkcs11-helper/0001-nmake-openssl-1.1.1-support.patch88
-rw-r--r--contrib/vcpkg-ports/pkcs11-helper/CONTROL4
-rw-r--r--contrib/vcpkg-ports/pkcs11-helper/pkcs11-helper-001-RFC7512.patch686
-rw-r--r--contrib/vcpkg-ports/pkcs11-helper/portfile.cmake35
-rw-r--r--contrib/vcpkg-triplets/arm64-windows-ovpn.cmake7
-rw-r--r--contrib/vcpkg-triplets/x64-windows-ovpn.cmake7
-rw-r--r--contrib/vcpkg-triplets/x86-windows-ovpn.cmake7
-rwxr-xr-xdepcomp2
-rw-r--r--distro/Makefile.am2
-rw-r--r--distro/Makefile.in14
-rw-r--r--distro/systemd/Makefile.am2
-rw-r--r--distro/systemd/Makefile.in14
-rw-r--r--doc/Makefile.am83
-rw-r--r--doc/Makefile.in441
-rw-r--r--doc/doxygen/Makefile.am21
-rw-r--r--doc/doxygen/Makefile.in (renamed from vendor/Makefile.in)63
-rw-r--r--doc/doxygen/openvpn.doxyfile.in279
-rw-r--r--doc/gui-notes.txt369
-rw-r--r--doc/man-sections/advanced-options.rst110
-rw-r--r--doc/man-sections/client-options.rst369
-rw-r--r--doc/man-sections/connection-profiles.rst75
-rw-r--r--doc/man-sections/encryption-options.rst135
-rw-r--r--doc/man-sections/examples.rst240
-rw-r--r--doc/man-sections/generic-options.rst445
-rw-r--r--doc/man-sections/inline-files.rst25
-rw-r--r--doc/man-sections/link-options.rst409
-rw-r--r--doc/man-sections/log-options.rst73
-rw-r--r--doc/man-sections/management-options.rst135
-rw-r--r--doc/man-sections/network-config.rst10
-rw-r--r--doc/man-sections/pkcs11-options.rst80
-rw-r--r--doc/man-sections/plugin-options.rst57
-rw-r--r--doc/man-sections/protocol-options.rst281
-rw-r--r--doc/man-sections/proxy-options.rst65
-rw-r--r--doc/man-sections/renegotiation.rst52
-rw-r--r--doc/man-sections/script-options.rst841
-rw-r--r--doc/man-sections/server-options.rst812
-rw-r--r--doc/man-sections/signals.rst30
-rw-r--r--doc/man-sections/tls-options.rst668
-rw-r--r--doc/man-sections/unsupported-options.rst32
-rw-r--r--doc/man-sections/virtual-routing-and-forwarding.rst78
-rw-r--r--doc/man-sections/vpn-network-options.rst556
-rw-r--r--doc/man-sections/windows-options.rst244
-rw-r--r--doc/management-notes.txt229
-rw-r--r--doc/openvpn-examples.5374
-rw-r--r--doc/openvpn-examples.5.html582
-rw-r--r--doc/openvpn-examples.5.rst17
-rw-r--r--doc/openvpn.813521
-rw-r--r--doc/openvpn.8.html5899
-rw-r--r--doc/openvpn.8.rst170
-rw-r--r--include/Makefile.am2
-rw-r--r--include/Makefile.in18
-rw-r--r--include/openvpn-msg.h20
-rw-r--r--include/openvpn-plugin.h80
-rw-r--r--include/openvpn-plugin.h.in76
-rwxr-xr-xinstall-sh13
-rw-r--r--m4/pkg.m42
-rwxr-xr-xmissing2
-rw-r--r--msvc-build.bat6
-rw-r--r--msvc-dev.bat2
-rw-r--r--msvc-env.bat6
-rw-r--r--openvpn.sln73
-rw-r--r--sample/Makefile.am5
-rw-r--r--sample/Makefile.in18
-rw-r--r--sample/sample-config-files/client.conf4
-rw-r--r--sample/sample-config-files/loopback-client208
-rw-r--r--sample/sample-config-files/loopback-server1
-rw-r--r--sample/sample-config-files/server.conf2
-rw-r--r--sample/sample-config-files/static-home.conf75
-rw-r--r--sample/sample-config-files/static-office.conf72
-rw-r--r--sample/sample-config-files/tls-home.conf12
-rw-r--r--sample/sample-config-files/tls-office.conf3
-rwxr-xr-xsample/sample-keys/gen-sample-keys.sh4
-rw-r--r--sample/sample-keys/openssl.cnf4
-rw-r--r--sample/sample-plugins/Makefile585
-rw-r--r--sample/sample-plugins/Makefile.am34
-rw-r--r--sample/sample-plugins/Makefile.in585
-rw-r--r--sample/sample-plugins/Makefile.plugins37
-rw-r--r--sample/sample-plugins/README43
-rw-r--r--sample/sample-plugins/client-connect/README38
-rw-r--r--sample/sample-plugins/client-connect/sample-client-connect.c612
-rw-r--r--sample/sample-plugins/defer/README16
-rwxr-xr-xsample/sample-plugins/defer/build15
-rw-r--r--sample/sample-plugins/defer/simple.c400
-rwxr-xr-xsample/sample-plugins/keying-material-exporter-demo/build15
-rw-r--r--sample/sample-plugins/keying-material-exporter-demo/keyingmaterialexporter.c16
-rwxr-xr-xsample/sample-plugins/log/build15
-rw-r--r--sample/sample-plugins/log/log.c11
-rw-r--r--sample/sample-plugins/log/log_v3.c17
-rw-r--r--sample/sample-plugins/simple/README16
-rw-r--r--sample/sample-plugins/simple/base64.c2
-rwxr-xr-xsample/sample-plugins/simple/build15
-rw-r--r--sample/sample-plugins/simple/simple.c7
-rwxr-xr-xsample/sample-windows/sample.ovpn2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.in16
-rw-r--r--src/compat/Debug.props21
-rw-r--r--src/compat/Makefile.am8
-rw-r--r--src/compat/Makefile.in28
-rw-r--r--src/compat/PropertySheet.props55
-rw-r--r--src/compat/Release.props25
-rw-r--r--src/compat/compat-gettimeofday.c2
-rw-r--r--src/compat/compat-lz4.c2187
-rw-r--r--src/compat/compat-lz4.h737
-rw-r--r--src/compat/compat-strsep.c61
-rw-r--r--src/compat/compat-versionhelpers.h10
-rw-r--r--src/compat/compat.h5
-rw-r--r--src/compat/compat.vcxproj140
-rw-r--r--src/compat/compat.vcxproj.filters6
-rw-r--r--src/openvpn/Makefile.am25
-rw-r--r--src/openvpn/Makefile.in251
-rw-r--r--src/openvpn/argv.c538
-rw-r--r--src/openvpn/argv.h9
-rw-r--r--src/openvpn/auth_token.c410
-rw-r--r--src/openvpn/auth_token.h132
-rw-r--r--src/openvpn/base64.h4
-rw-r--r--src/openvpn/basic.h2
-rw-r--r--src/openvpn/block_dns.c5
-rw-r--r--src/openvpn/block_dns.h6
-rw-r--r--src/openvpn/buffer.c132
-rw-r--r--src/openvpn/buffer.h61
-rw-r--r--src/openvpn/circ_list.h2
-rw-r--r--src/openvpn/clinat.c2
-rw-r--r--src/openvpn/clinat.h2
-rw-r--r--src/openvpn/common.h10
-rw-r--r--src/openvpn/comp-lz4.c9
-rw-r--r--src/openvpn/comp-lz4.h4
-rw-r--r--src/openvpn/comp.c4
-rw-r--r--src/openvpn/comp.h18
-rw-r--r--src/openvpn/compstub.c2
-rw-r--r--src/openvpn/console.c4
-rw-r--r--src/openvpn/console.h12
-rw-r--r--src/openvpn/console_builtin.c107
-rw-r--r--src/openvpn/console_systemd.c3
-rw-r--r--src/openvpn/crypto.c348
-rw-r--r--src/openvpn/crypto.h106
-rw-r--r--src/openvpn/crypto_backend.h51
-rw-r--r--src/openvpn/crypto_mbedtls.c177
-rw-r--r--src/openvpn/crypto_mbedtls.h9
-rw-r--r--src/openvpn/crypto_openssl.c291
-rw-r--r--src/openvpn/crypto_openssl.h25
-rw-r--r--src/openvpn/cryptoapi.c531
-rw-r--r--src/openvpn/dhcp.c53
-rw-r--r--src/openvpn/dhcp.h2
-rw-r--r--src/openvpn/env_set.c459
-rw-r--r--src/openvpn/env_set.h123
-rw-r--r--src/openvpn/errlevel.h7
-rw-r--r--src/openvpn/error.c48
-rw-r--r--src/openvpn/error.h6
-rw-r--r--src/openvpn/event.c12
-rw-r--r--src/openvpn/event.h2
-rw-r--r--src/openvpn/fdmisc.c2
-rw-r--r--src/openvpn/fdmisc.h2
-rw-r--r--src/openvpn/forward-inline.h341
-rw-r--r--src/openvpn/forward.c614
-rw-r--r--src/openvpn/forward.h202
-rw-r--r--src/openvpn/fragment.c5
-rw-r--r--src/openvpn/fragment.h2
-rw-r--r--src/openvpn/gremlin.c3
-rw-r--r--src/openvpn/gremlin.h2
-rw-r--r--src/openvpn/helper.c45
-rw-r--r--src/openvpn/helper.h2
-rw-r--r--src/openvpn/httpdigest.c2
-rw-r--r--src/openvpn/httpdigest.h2
-rw-r--r--src/openvpn/init.c633
-rw-r--r--src/openvpn/init.h12
-rw-r--r--src/openvpn/integer.h12
-rw-r--r--src/openvpn/interval.c2
-rw-r--r--src/openvpn/interval.h2
-rw-r--r--src/openvpn/list.c22
-rw-r--r--src/openvpn/list.h9
-rw-r--r--src/openvpn/lladdr.c32
-rw-r--r--src/openvpn/lladdr.h3
-rw-r--r--src/openvpn/lzo.c10
-rw-r--r--src/openvpn/lzo.h10
-rw-r--r--src/openvpn/manage.c260
-rw-r--r--src/openvpn/manage.h55
-rw-r--r--src/openvpn/mbuf.c2
-rw-r--r--src/openvpn/mbuf.h6
-rw-r--r--src/openvpn/memdbg.h6
-rw-r--r--src/openvpn/misc.c995
-rw-r--r--src/openvpn/misc.h209
-rw-r--r--src/openvpn/mroute.c78
-rw-r--r--src/openvpn/mroute.h36
-rw-r--r--src/openvpn/mss.c6
-rw-r--r--src/openvpn/mss.h2
-rw-r--r--src/openvpn/mstats.c2
-rw-r--r--src/openvpn/mstats.h2
-rw-r--r--src/openvpn/mtcp.c27
-rw-r--r--src/openvpn/mtcp.h5
-rw-r--r--src/openvpn/mtu.c10
-rw-r--r--src/openvpn/mtu.h4
-rw-r--r--src/openvpn/mudp.c14
-rw-r--r--src/openvpn/mudp.h7
-rw-r--r--src/openvpn/multi.c1477
-rw-r--r--src/openvpn/multi.h60
-rw-r--r--src/openvpn/networking.h305
-rw-r--r--src/openvpn/networking_iproute2.c407
-rw-r--r--src/openvpn/networking_iproute2.h37
-rw-r--r--src/openvpn/networking_sitnl.c1339
-rw-r--r--src/openvpn/networking_sitnl.h28
-rw-r--r--src/openvpn/ntlm.c4
-rw-r--r--src/openvpn/occ-inline.h95
-rw-r--r--src/openvpn/occ.c15
-rw-r--r--src/openvpn/occ.h70
-rw-r--r--src/openvpn/openssl_compat.h88
-rw-r--r--src/openvpn/openvpn.c35
-rw-r--r--src/openvpn/openvpn.h65
-rw-r--r--src/openvpn/openvpn.manifest33
-rw-r--r--src/openvpn/openvpn.vcxproj245
-rw-r--r--src/openvpn/openvpn.vcxproj.filters50
-rw-r--r--src/openvpn/openvpn_win32_resources.rc2
-rw-r--r--src/openvpn/options.c1550
-rw-r--r--src/openvpn/options.h126
-rw-r--r--src/openvpn/otime.c27
-rw-r--r--src/openvpn/otime.h3
-rw-r--r--src/openvpn/packet_id.c24
-rw-r--r--src/openvpn/packet_id.h9
-rw-r--r--src/openvpn/perf.c2
-rw-r--r--src/openvpn/perf.h2
-rw-r--r--src/openvpn/pf-inline.h63
-rw-r--r--src/openvpn/pf.c37
-rw-r--r--src/openvpn/pf.h47
-rw-r--r--src/openvpn/ping-inline.h64
-rw-r--r--src/openvpn/ping.c9
-rw-r--r--src/openvpn/ping.h46
-rw-r--r--src/openvpn/pkcs11.c2
-rw-r--r--src/openvpn/pkcs11.h2
-rw-r--r--src/openvpn/pkcs11_backend.h4
-rw-r--r--src/openvpn/pkcs11_mbedtls.c91
-rw-r--r--src/openvpn/pkcs11_openssl.c4
-rw-r--r--src/openvpn/platform.c151
-rw-r--r--src/openvpn/platform.h20
-rw-r--r--src/openvpn/plugin.c47
-rw-r--r--src/openvpn/plugin.h25
-rw-r--r--src/openvpn/pool.c401
-rw-r--r--src/openvpn/pool.h32
-rw-r--r--src/openvpn/proto.c96
-rw-r--r--src/openvpn/proto.h69
-rw-r--r--src/openvpn/proxy.c12
-rw-r--r--src/openvpn/proxy.h2
-rw-r--r--src/openvpn/ps.c35
-rw-r--r--src/openvpn/ps.h2
-rw-r--r--src/openvpn/push.c410
-rw-r--r--src/openvpn/push.h27
-rw-r--r--src/openvpn/pushlist.h7
-rw-r--r--src/openvpn/reliable.c45
-rw-r--r--src/openvpn/reliable.h7
-rw-r--r--src/openvpn/ring_buffer.h125
-rw-r--r--src/openvpn/route.c721
-rw-r--r--src/openvpn/route.h36
-rw-r--r--src/openvpn/run_command.c288
-rw-r--r--src/openvpn/run_command.h67
-rw-r--r--src/openvpn/schedule.c5
-rw-r--r--src/openvpn/schedule.h5
-rw-r--r--src/openvpn/session_id.c11
-rw-r--r--src/openvpn/session_id.h5
-rw-r--r--src/openvpn/shaper.c6
-rw-r--r--src/openvpn/shaper.h10
-rw-r--r--src/openvpn/sig.c15
-rw-r--r--src/openvpn/sig.h5
-rw-r--r--src/openvpn/socket.c131
-rw-r--r--src/openvpn/socket.h73
-rw-r--r--src/openvpn/socks.c11
-rw-r--r--src/openvpn/socks.h2
-rw-r--r--src/openvpn/ssl.c1735
-rw-r--r--src/openvpn/ssl.h116
-rw-r--r--src/openvpn/ssl_backend.h87
-rw-r--r--src/openvpn/ssl_common.h128
-rw-r--r--src/openvpn/ssl_mbedtls.c379
-rw-r--r--src/openvpn/ssl_mbedtls.h57
-rw-r--r--src/openvpn/ssl_ncp.c342
-rw-r--r--src/openvpn/ssl_ncp.h118
-rw-r--r--src/openvpn/ssl_openssl.c831
-rw-r--r--src/openvpn/ssl_openssl.h15
-rw-r--r--src/openvpn/ssl_verify.c580
-rw-r--r--src/openvpn/ssl_verify.h23
-rw-r--r--src/openvpn/ssl_verify_backend.h7
-rw-r--r--src/openvpn/ssl_verify_mbedtls.c13
-rw-r--r--src/openvpn/ssl_verify_mbedtls.h4
-rw-r--r--src/openvpn/ssl_verify_openssl.c43
-rw-r--r--src/openvpn/ssl_verify_openssl.h4
-rw-r--r--src/openvpn/status.c15
-rw-r--r--src/openvpn/status.h4
-rw-r--r--src/openvpn/syshead.h82
-rw-r--r--src/openvpn/tls_crypt.c484
-rw-r--r--src/openvpn/tls_crypt.h117
-rw-r--r--src/openvpn/tun.c3467
-rw-r--r--src/openvpn/tun.h236
-rw-r--r--src/openvpn/vlan.c333
-rw-r--r--src/openvpn/vlan.h44
-rw-r--r--src/openvpn/win32.c55
-rw-r--r--src/openvpn/win32.h34
-rw-r--r--src/openvpnmsica/Makefile.am56
-rw-r--r--src/openvpnmsica/Makefile.in865
-rw-r--r--src/openvpnmsica/dllmain.c198
-rw-r--r--src/openvpnmsica/msica_arg.c139
-rw-r--r--src/openvpnmsica/msica_arg.h112
-rw-r--r--src/openvpnmsica/msiex.c265
-rw-r--r--src/openvpnmsica/msiex.h112
-rw-r--r--src/openvpnmsica/openvpnmsica-Debug.props14
-rw-r--r--src/openvpnmsica/openvpnmsica-Release.props14
-rw-r--r--src/openvpnmsica/openvpnmsica.c1297
-rw-r--r--src/openvpnmsica/openvpnmsica.h166
-rw-r--r--src/openvpnmsica/openvpnmsica.props18
-rw-r--r--src/openvpnmsica/openvpnmsica.vcxproj160
-rw-r--r--src/openvpnmsica/openvpnmsica.vcxproj.filters62
-rw-r--r--src/openvpnmsica/openvpnmsica_resources.rc62
-rw-r--r--src/openvpnserv/Makefile.am5
-rw-r--r--src/openvpnserv/Makefile.in17
-rw-r--r--src/openvpnserv/automatic.c25
-rw-r--r--src/openvpnserv/common.c36
-rw-r--r--src/openvpnserv/interactive.c395
-rw-r--r--src/openvpnserv/openvpnserv.vcxproj186
-rw-r--r--src/openvpnserv/openvpnserv.vcxproj.filters25
-rw-r--r--src/openvpnserv/service.c4
-rw-r--r--src/openvpnserv/service.h11
-rw-r--r--src/openvpnserv/validate.c13
-rw-r--r--src/openvpnserv/validate.h2
-rw-r--r--src/plugins/Makefile.am2
-rw-r--r--src/plugins/Makefile.in14
-rw-r--r--src/plugins/auth-pam/Makefile.in12
-rw-r--r--src/plugins/auth-pam/README.auth-pam54
-rw-r--r--src/plugins/auth-pam/auth-pam.c253
-rw-r--r--src/plugins/auth-pam/utils.c2
-rw-r--r--src/plugins/auth-pam/utils.h8
-rw-r--r--src/plugins/down-root/Makefile.in12
-rw-r--r--src/plugins/down-root/down-root.c2
-rw-r--r--src/tapctl/Makefile.am51
-rw-r--r--src/tapctl/Makefile.in831
-rw-r--r--src/tapctl/basic.h66
-rw-r--r--src/tapctl/error.c36
-rw-r--r--src/tapctl/error.h97
-rw-r--r--src/tapctl/main.c444
-rw-r--r--src/tapctl/tap.c1367
-rw-r--r--src/tapctl/tap.h181
-rw-r--r--src/tapctl/tapctl.exe.manifest10
-rw-r--r--src/tapctl/tapctl.props18
-rw-r--r--src/tapctl/tapctl.vcxproj163
-rw-r--r--src/tapctl/tapctl.vcxproj.filters49
-rw-r--r--src/tapctl/tapctl_resources.rc64
-rw-r--r--tests/Makefile.am8
-rw-r--r--tests/Makefile.in20
-rwxr-xr-xtests/t_client.sh3
-rwxr-xr-xtests/t_client.sh.in3
-rwxr-xr-xtests/t_lpback.sh51
-rwxr-xr-xtests/t_net.sh171
-rw-r--r--tests/unit_tests/Makefile.am5
-rw-r--r--tests/unit_tests/Makefile.in18
-rw-r--r--tests/unit_tests/engine-key/Makefile.am29
-rw-r--r--tests/unit_tests/engine-key/Makefile.in815
-rwxr-xr-xtests/unit_tests/engine-key/check_engine_keys.sh36
-rw-r--r--tests/unit_tests/engine-key/libtestengine.c116
-rw-r--r--tests/unit_tests/engine-key/openssl.cnf.in12
-rw-r--r--tests/unit_tests/example_test/Makefile.in12
-rw-r--r--tests/unit_tests/example_test/test.c18
-rw-r--r--tests/unit_tests/example_test/test2.c6
-rw-r--r--tests/unit_tests/openvpn/Makefile.am99
-rw-r--r--tests/unit_tests/openvpn/Makefile.in1042
-rw-r--r--tests/unit_tests/openvpn/mock_get_random.c36
-rw-r--r--tests/unit_tests/openvpn/mock_msg.c2
-rw-r--r--tests/unit_tests/openvpn/mock_msg.h2
-rw-r--r--tests/unit_tests/openvpn/test_argv.c123
-rw-r--r--tests/unit_tests/openvpn/test_auth_token.c397
-rw-r--r--tests/unit_tests/openvpn/test_buffer.c53
-rw-r--r--tests/unit_tests/openvpn/test_crypto.c158
-rw-r--r--tests/unit_tests/openvpn/test_ncp.c241
-rw-r--r--tests/unit_tests/openvpn/test_networking.c253
-rw-r--r--tests/unit_tests/openvpn/test_packet_id.c43
-rw-r--r--tests/unit_tests/openvpn/test_tls_crypt.c454
-rw-r--r--tests/unit_tests/plugins/Makefile.in12
-rw-r--r--tests/unit_tests/plugins/auth-pam/Makefile.in12
-rw-r--r--vendor/Makefile.am22
-rw-r--r--version.m46
408 files changed, 59142 insertions, 19175 deletions
diff --git a/.gitignore b/.gitignore
index 932b519..178076e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,7 +34,6 @@ config.sub
configure
configure.h
depcomp
-doxygen/
stamp-h1
install-sh
missing
@@ -50,7 +49,13 @@ version.sh
msvc-env-local.bat
config-msvc-local.h
config-msvc-version.h
+doc/openvpn-examples.5
+doc/openvpn-examples.5.html
+doc/openvpn.8
doc/openvpn.8.html
+/doc/doxygen/html/
+/doc/doxygen/latex/
+/doc/doxygen/openvpn.doxyfile
distro/systemd/*.service
sample/sample-keys/sample-ca/
vendor/cmocka_build
diff --git a/COPYING b/COPYING
index 9c21c17..9044a39 100644
--- a/COPYING
+++ b/COPYING
@@ -1,6 +1,6 @@
OpenVPN (TM) -- An Open Source VPN daemon
-Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
This distribution contains multiple components, some
of which fall under different licenses. By using OpenVPN
diff --git a/ChangeLog b/ChangeLog
index b0b0dd7..b0ee674 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,304 +1,278 @@
OpenVPN Change Log
-Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
-2020.04.16 -- Version 2.4.9
-Antonio Quartulli (1):
- socks: use the right function when printing struct openvpn_sockaddr
+2021.10.04 -- Version 2.5.4
-Arne Schwabe (3):
- Fetch OpenSSL versions via source/old links
- Fix OpenSSL error stack handling of tls_ctx_add_extra_certs
- Fix OpenSSL 1.1.1 not using auto elliptic curve selection
+Antonio Quartulli (3):
+ route.c: pass the right parameter to IN6_IS_ADDR_UNSPECIFIED
+ configure: search also for rst2{man, html}.py
+ networking: add networking API net_addr_ll_set() and use it on Linux
-Lev Stipakov (4):
- Fix broken fragmentation logic when using NCP
- Fix building with --enable-async-push in FreeBSD
- Fix broken async push with NCP is used
- Fix illegal client float (CVE-2020-11810)
+Arne Schwabe (1):
+ Move examples into openvpn-examples(5) man page
-Maxim Plotnikov (1):
- OpenSSL: Fix --crl-verify not loading multiple CRLs in one file
+David Korczynski (1):
+ Fix argv leaks in add_route() and add_route_ipv6()
-Santtu Lakkala (1):
- Fix OpenSSL private key passphrase notices
+David Sommerseth (2):
+ doc: Use generic rules for man/html generation
+ man: Clarify IV_HWADDR
-Selva Nair (7):
- Swap the order of checks for validating interactive service user
- Move querying username/password from management interface to a function
- When auth-user-pass file has no password query the management interface (if available).
- Fix possibly uninitialized return value in GetOpenvpnSettings()
- Fix possible access of uninitialized pipe handles
- Skip expired certificates in Windows certificate store
- Allow unicode search string in --cryptoapicert option
+Gert Doering (1):
+ Add error reporting to get_console_input_win32().
-Tom van Leeuwen (1):
- mbedTLS: Make sure TLS session survives move
+Lev Stipakov (3):
+ Fix console prompts with redirected log
+ Add building man page on Windows
+ GitHub Actions: remove Ubuntu 16.04 environment
-WGH (1):
- docs: Add reference to X509_LOOKUP_hash_dir(3)
+Max Fillinger (1):
+ Update Fox e-mail address in copyright notices
+Selva Nair (1):
+ Minor doc correction: tls-crypt-v2 key generation
-2019.10.30 -- Version 2.4.8
-Antonio Quartulli (1):
- mbedtls: fix segfault by calling mbedtls_cipher_free() in cipher_ctx_free()
-Arne Schwabe (1):
- Remove -no-cpp-precomp flag from Darwin builds
+2021.06.17 -- Version 2.5.3
-David Sommerseth (3):
- cleanup: Remove RPM openvpn.spec build approach
- docs: Update INSTALL
- build: Package missing mock_msg.h
+Arne Schwabe (3):
+ Add missing free_key_ctx for auth_token
+ Add github actions
+ Implement auth-token-user
-Gert Doering (4):
- repair windows builds (2.4)
- Increase listen() backlog queue to 32
- Force combinationation of --socks-proxy and --proto UDP to use IPv4.
- Fix IPv6 routes on tap interfaces on OpenSolaris/OpenIndiana
+David Sommerseth (1):
+ Update copyrights
-Gisle Vanem (1):
- Wrong FILETYPE in .rc files
+Lev Stipakov (8):
+ openvpnmsica: properly schedule reboot in the end of installation
+ msvc: add ARM64 configuration
+ msvc: standalone building
+ contrib/vcpkg-ports: add pkcs11-helper port
+ vcpkg-ports: restore trailing whitespaces in .patch files
+ GitHub actions: add MSVC build
+ crypto_openssl.c: disable explicit initialization on Windows (CVE-2121-3606)
+ contrib/vcpkg-ports: add openssl port with --no-autoload-config option set (CVE-2121-3606)
-Hilko Bengen (1):
- Do not set pkcs11-helper 'safe fork mode'
+Matthias Andree (1):
+ Fix SIGSEGV (NULL deref) receiving push "echo"
-Ilya Shipitsin (2):
- travis-ci: add "linux-ppc64le" to build matrix, change trusty image to xenial, update osx to xcode9.4 and modernize brew management
- travis-ci: fix osx builds
+Max Fillinger (1):
+ Fix build with mbedtls w/o SSL renegotiation support
-Kyle Evans (1):
- tests/t_lpback.sh: Switch sed(1) to POSIX-compatible regex.
+Selva Nair (2):
+ Improve documentation of AUTH_PENDING related directives
+ Apply the connect-retry backoff to only one side of a connection
-Lev Stipakov (1):
- Fix various compiler warnings
-Matthias Andree (1):
- Fix regression, reinstate LibreSSL support.
+2021.04.20 -- Version 2.5.2
-Michal Soltys (1):
- man: correct the description of --capath and --crl-verify regarding CRLs
+Arne Schwabe (10):
+ Avoid generating unecessary mbed debug messages
+ Restore also ping related options on a reconnect
+ Cleanup print_details and add signature/ED certificate print
+ Always disable TLS renegotiations
+ Also restore/save route-gateway options on SIGUSR1 reconnects
+ Move context_auth from context_2 to tls_multi and name it multi_state
+ Fix condition to generate session keys
+ Move auth_token_state from multi to key_state
+ Ensure auth-token is only sent on a fully authenticated session
+ Ensure key state is authenticated before sending push reply
-Mykola Baibuz (1):
- Fix typo in NTLM proxy debug message
+Gert Doering (2):
+ Fix potential NULL ptr crash if compiled with DMALLOC
+
+Max Fillinger (2):
+ In init_ssl, open the correct CRL path pre-chroot
+ Abort if CRL file can't be stat-ed in ssl_init
Richard Bonhomme (1):
- Ignore --pull-filter for --mode server
+ Do not print Diffie Hellman parameters file to log file
-Rosen Penev (1):
- openssl: Fix compilation without deprecated OpenSSL 1.1 APIs
+Simon Rozman (1):
+ openvpnserv: Cache last error before it is overridden
-Selva Nair (3):
- Better error message when script fails due to script-security setting
- Correct the return value of cryptoapi RSA signature callbacks
- Handle PSS padding in cryptoapicert
+Vladislav Grishenko (1):
+ Fix IPv4 default gateway with multiple route tables
-Steffan Karger (1):
- cmocka: use relative paths
-Thomas Quinot (1):
- Fix documentation of tls-verify script argument
+2021.02.24 -- Version 2.5.1
+Arne Schwabe (5):
+ Fix auth-token not being updated if auth-nocache is set
+ Remove auth_user_pass.wait_for_push variable
+ Fix port-share option with TLS-Crypt v2
+ Zero initialise msghdr prior to calling sendmesg
+ Fix tls-auth mismatch OCC message when tls-cryptv2 is used.
-2019.02.18 -- Version 2.4.7
-Adam Ciarcin?ski (1):
- Fix subnet topology on NetBSD (2.4).
+David Sommerseth (1):
+ build: Fix missing install of man page in certain environments
-Antonio Quartulli (3):
- add support for %lu in argv_printf and prevent ASSERT
- buffer_list: add functions documentation
- ifconfig-ipv6(-push): allow using hostnames
+Domagoj Pensa (3):
+ Fix too early argv freeing when registering DNS
+ Remove 1 second delay before running netsh
+ Skip DHCP renew with Wintun adapter
-Arne Schwabe (7):
- Properly free tuntap struct on android when emulating persist-tun
- Add OpenSSL compat definition for RSA_meth_set_sign
- Add support for tls-ciphersuites for TLS 1.3
- Add better support for showing TLS 1.3 ciphersuites in --show-tls
- Use right function to set TLS1.3 restrictions in show-tls
- Add message explaining early TLS client hello failure
- Fallback to password authentication when auth-token fails
+Gert Doering (6):
+ Change travis build scripts to use https when fetching prerequisites.
+ Fix line number reporting on config file errors after <inline> segments
+ Clarify --block-ipv6 intent and direction.
+ Document common uses of 'echo' directive, re-enable logging for 'echo'.
+ Make OPENVPN_PLUGIN_ENABLE_PF failures FATAL
+ clean up / rewrite sample-plugins/defer/simple.c
+
+Greg Cox (5):
+ Fix naming error in sample-plugins/defer/simple.c
+ Documentation fixes around openvpn_plugin_func_v3 in openvpn-plugin.h.in
+ Update openvpn_plugin_func_v2 to _v3 in sample-plugins/defer/simple.c
+ More explicit versioning compatibility in sample-plugins/defer/simple.c
+ Explain structver usage in sample defer plugin.
-Christian Ehrhardt (1):
- systemd: extend CapabilityBoundingSet for auth_pam
+Richard Bonhomme (1):
+ Man page sections corrections
+
+Selva Nair (1):
+ Quote the domain name argument passed to the wmic command
+
+Steffan Karger (2):
+ tls-crypt-v2: fix server memory leak
+ tls-crypt-v2: also preload tls-crypt-v2 keys (if --persist-key)
+
+
+2020.10.27 -- Version 2.5.0
+ (no changes relative to v2.5_rc3)
+
+2020.10.15 -- Version 2.5_rc3
+Arne Schwabe (2):
+ Allow 'none' cipher being specified in --data-ciphers
+ Add function for common env setting of verify user/pass calls
David Sommerseth (1):
- plugin: Export base64 encode and decode functions
+ compat/lz4: Update to v1.9.2
-Gert Doering (3):
- Add %d, %u and %lu tests to test_argv unit tests.
- Fix combination of --dev tap and --topology subnet across multiple platforms.
- Add 'printing of port number' to mroute_addr_print_ex() for v4-mapped v6.
+Gert Doering (2):
+ Fix redirecting of IPv4 default gateway if connecting over IPv6.
+ Avoid passing NULL to argv_printf_cat() in temp_file error case.
-Gert van Dijk (1):
- Minor reliability layer documentation fixes
+Jan Seeger (1):
+ Added 'route_ipv6_metric_NN' environment variable for IPv6 route metric.
-James Bekkema (1):
- Resolves small IV_GUI_VER typo in the documentation.
+Richard Bonhomme (1):
+ Improve error msg when all TAP adapters are in use 'or disabled'
-Jonathan K. Bullard (1):
- Clarify and expand management interface documentation
+Steffan Karger (1):
+ networking_iproute2: fix memory leak in net_iface_mtu_set()
-Lev Stipakov (5):
- Refactor NCP-negotiable options handling
- init.c: refine functions names and description
- interactive.c: fix usage of potentially uninitialized variable
- options.c: fix broken unary minus usage
- Remove extra token after #endif
+Vladislav Grishenko (2):
+ Selectively reformat too long lines
+ Speedup TCP remote hosts connections
-Richard van den Berg via Openvpn-devel (1):
- Fix error message when using RHEL init script
-Samy Mahmoudi (1):
- man: correct a --redirection-gateway option flag
+2020.09.30 -- Version 2.5_rc2
-Selva Nair (7):
- Replace M_DEBUG with D_LOW as the former is too verbose
- Correct the declaration of handle in 'struct openvpn_plugin_args_open_return'
- Bump version of openvpn plugin argument structs to 5
- Move get system directory to a separate function
- Enable dhcp on tap adapter using interactive service
- Pass the hash without the DigestInfo header to NCryptSignHash()
- White-list pull-filter and script-security in interactive service
+Lev Stipakov (1):
+ Alias ADAPTER_DOMAIN_SUFFIX to DOMAIN
-Simon Rozman (2):
- Add Interactive Service developer documentation
- Detect TAP interfaces with root-enumerated hardware ID
+Selva Nair (2):
+ Set DNS Domain using iservice
+ Improve documentation of --username-as-common-name
-Steffan Karger (7):
- man: add security considerations to --compress section
- mbedtls: print warning if random personalisation fails
- Fix memory leak after sighup
- travis: add OpenSSL 1.1 Windows build
- Fix --disable-crypto build
- Don't print OCC warnings about 'key-method', 'keydir' and 'tls-auth'
- buffer_list_aggregate_separator(): simplify code
+Simon Rozman (4):
+ netsh: Specify interfaces by index rather than name
+ netsh: Clear existing IPv6 DNS servers before configuring new ones
+ netsh: Delete WINS servers on TUN close
+ openvpnmsica: Simplify find_adapters() to void return
+Vladislav Grishenko (1):
+ Fix update_time() and openvpn_gettimeofday() coexistence
-2018.04.19 -- Version 2.4.6
-David Sommerseth (1):
- management: Warn if TCP port is used without password
-Gert Doering (2):
- Correct version in ChangeLog - should be 2.4.5, was mistyped as 2.4.4
- Fix potential double-free() in Interactive Service (CVE-2018-9336)
+2020.09.21 -- Version 2.5_rc1
-Gert van Dijk (1):
- manpage: improve description of --status and --status-version
+David Sommerseth (4):
+ man: Add missing --server-ipv6
+ man: Improve --remote entry
+ sample-plugins: Partially autotoolize the sample-plugins build
+ build: Fix make distclean/distcheck
-Joost Rijneveld (1):
- Make return code external tls key match docs
+Gert Doering (10):
+ Fix handling of 'route remote_host' for IPv6 transport case.
+ Replace 'echo -n' with 'printf' in tests/t_lpback.sh
+ Fix description of --client-disconnect calling convention in manpage.
+ Handle NULL returns from calloc() in sample plugins.
+ Fix --show-gateway for IPv6 on NetBSD/i386.
+ socks.c: fix alen for DOMAIN type addresses, bump up buffer sizes
+ Fix netbits setting (in TAP mode) for IPv6 on Windows.
+ If IPv6 pool specification sets pool start to ::0 address, increment.
+ Add demo plugin that excercises "CLIENT_CONNECT" and "CLIENT_CONNECT_V2" paths
+ Fix combination of --dev tap and --topology subnet across multiple platforms.
-Selva Nair (3):
- Delete the IPv6 route to the "connected" network on tun close
- Management: warn about password only when the option is in use
- Avoid overflow in wakeup time computation
+Lev Stipakov (1):
+ msvc: better support for 32bit architecture
-Simon Matter (1):
- Add missing #ifdef SSL_OP_NO_TLSv1_1/2
+Selva Nair (2):
+ Add a remark on dropping privileges when --mlock is used
+ Allow --dhcp-option in config file when windows-driver is wintun
-Steffan Karger (1):
- Check for more data in control channel
+Vladislav Grishenko (1):
+ Fix fatal error at switching remotes (#629)
-2018.02.28 -- Version 2.4.5
-Antonio Quartulli (4):
- reload HTTP proxy credentials when moving to the next connection profile
- Allow learning iroutes with network made up of all 0s (only if netbits < 8)
- mbedtls: fix typ0 in comment
- manpage: fix simple typ0
+2020.09.10 -- Version 2.5_beta4
-Arne Schwabe (2):
- Treat dhcp-option DNS6 and DNS identical
- show the right string for key-direction
+Gert Doering (3):
+ Document that --push-remove is generally more suitable than --push-reset
+ Fix error detection / abort in --inetd corner case.
+ Fix TUNSETGROUP compatibility with very old Linux systems.
-Bertrand Bonnefoy-Claudet (1):
- Fix typo in error message: "optione" -> "option"
+Lev Stipakov (1):
+ openvpnmsica: make adapter renaming non-fatal
-David Sommerseth (8):
- lz4: Fix confused version check
- lz4: Fix broken builds when pkg-config is not present but system library is
- Remove references to keychain-mcd in Changes.rst
- lz4: Rebase compat-lz4 against upstream v1.7.5
- systemd: Add and ship README.systemd
- Update copyright to include 2018 plus company name change
- man: Add .TQ groff support macro
- man: Reword --management to prefer unix sockets over TCP
+Selva Nair (1):
+ In tap.c use DiInstallDevice to install the driver on a new adapter
-Emmanuel Deloget (1):
- OpenSSL: check EVP_PKEY key types before returning the pkey
+Vladislav Grishenko (1):
+ Fix best gateway selection over netlink
-Gert Doering (2):
- Remove warning on pushed tun-ipv6 option.
- Fix removal of on-link prefix on windows with netsh
-Ilya Shipitsin (2):
- travis-ci: add brew cache, remove ccache
- travis-ci: modify openssl build script to support openssl-1.1.0
+2020.08.31 -- Version 2.5_beta3
-James Bottomley (1):
- autoconf: Fix engine checks for openssl 1.1
+Arne Schwabe (1):
+ Fix client NCP OCC fallback when server and client cipher are identical
-Jeremie Courreges-Anglas (2):
- Cast time_t to long long in order to print it.
- Fix build with LibreSSL
+2020.08.26 -- Version 2.5_beta2
-Selva Nair (14):
- Check whether in pull_mode before warning about previous connection blocks
- Avoid illegal memory access when malformed data is read from the pipe
- Fix missing check for return value of malloc'd buffer
- Return NULL if GetAdaptersInfo fails
- Use RSA_meth_free instead of free
- Bring cryptoapi.c upto speed with openssl 1.1
- Add SSL_CTX_get_max_proto_version() not in openssl 1.0
- TLS v1.2 support for cryptoapicert -- RSA only
- Refactor get_interface_metric to return metric and auto flag separately
- Ensure strings read from registry are null-terminated
- Make most registry values optional
- Use lowest metric interface when multiple interfaces match a route
- Adapt to RegGetValue brokenness in Windows 7
- Fix format spec errors in Windows builds
+Arne Schwabe (1):
+ Fix client's poor man NCP fallback
-Simon Rozman (11):
- Local functions are not supported in MSVC. Bummer.
- Mixing wide and regular strings in concatenations is not allowed in MSVC.
- RtlIpv6AddressToStringW() and RtlIpv4AddressToStringW() require mstcpip.h
- Simplify iphlpapi.dll API calls
- Fix local #include to use quoted form
- Document ">PASSWORD:Auth-Token" real-time message
- Fix typo in "verb" command examples
- Uniform swprintf() across MinGW and MSVC compilers
- MSVC meta files added to .gitignore list
- openvpnserv: Add support for multi-instances
- Document missing OpenVPN states
+Eric Thorpe (1):
+ Fixes a bug in management_callback_send_cc_message, should be strlen instead of sizeof
-Steffan Karger (21):
- make struct key * argument of init_key_ctx const
- buffer_list_aggregate_separator(): add unit tests
- Add --tls-cert-profile option.
- Use P_DATA_V2 for server->client packets too
- Fix memory leak in buffer unit tests
- buffer_list_aggregate_separator(): update list size after aggregating
- buffer_list_aggregate_separator(): don't exceed max_len
- buffer_list_aggregate_separator(): prevent 0-byte malloc
- Fix types around buffer_list_push(_data)
- ssl_openssl: fix compiler warning by removing getbio() wrapper
- travis: use clang's -fsanitize=address to catch more bugs
- Fix --tls-version-min and --tls-version-max for OpenSSL 1.1+
- Add support for TLS 1.3 in --tls-version-{min, max}
- Plug memory leak if push is interrupted
- Fix format errors when cross-compiling for Windows
- Log pre-handshake packet drops using D_MULTI_DROPPED
- Enable stricter compiler warnings by default
- Get rid of ax_check_compile_flag.m4
- mbedtls: don't use API deprecated in mbed 2.7
- Warn if tls-version-max < tls-version-min
- Don't throw fatal errors from create_temp_file()
+Gert Doering (2):
+ Fix stack overflow in OpenSolaris NEXTADDR()
+ Workaround FreeBSD 12+ race condition on tun/tap open with IPv6.
-hashiz (1):
- Fix '--bind ipv6only'
+Lev Stipakov (1):
+ tun.c: enable using wintun driver under SYSTEM
+
+Magnus Kroken (2):
+ doc: fix typos in cipher-negotiation.rst
+ Changes.rst: fix mistyped option names
+Selva Nair (1):
+ Improve the documentation for --dhcp-option
+
+
+2020.08.12 -- Version 2.5_beta1
-2017.09.25 -- Version 2.4.4
-Antonio Quartulli (23):
+Adam Ciarcin?ski (1):
+ Fix subnet topology on NetBSD.
+
+Antonio Quartulli (113):
+ attempt to add IPv6 route even when no IPv6 address was configured
+ fix redirect-gateway behaviour when an IPv4 default route does not exist
+ CRL: use time_t instead of struct timespec to store last mtime
+ ignore remote-random-hostname if a numeric host is provided
+ Ignore auth-nocache for auth-user-pass if auth-token is pushed
crypto: correct typ0 in error message
use M_ERRNO instead of explicitly printing errno
don't print errno twice
@@ -322,80 +296,321 @@ Antonio Quartulli (23):
fix a couple of typ0s in comments and strings
fragment.c: simplify boolean expression
tcp-server: ensure AF family is propagated to child context
-
-Arne Schwabe (2):
+ Remove ENABLE_CRYPTO
+ Remove option to disable crypto engine
+ Remove ENABLE_PUSH_PEER_INFO
+ Remove SSL_LIB_VER_STR
+ Remove MD5SUM
+ reload HTTP proxy credentials when moving to the next connection profile
+ Allow learning iroutes with network made up of all 0s (only if netbits < 8)
+ mbedtls: fix typ0 in comment
+ manpage: fix simple typ0
+ pool: restyle ipv4/ipv6 members to improve readability
+ pool: convert pool 'type' to enum
+ tun: ensure gc and argv are properly handled
+ tun: always pass a valid tt pointer
+ tun: get rid of tt->did_ifconfig member
+ tun: ensure interface can be configured with IPv6 only
+ add support for %lu in argv_printf and prevent ASSERT
+ windows: properly configure TAP driver when no IPv4 is configured
+ socket: make stream_buf_* functions static
+ crypto: always reload tls-auth/crypt key contexts
+ make tls-auth and tls-crypt per-connection-block options
+ pf: restyle pf_c2c/addr_test() to make them 'struct context' agnostic
+ merge *-inline.h files with their main header
+ ensure function declarations are compiled with their definitions
+ buffer_list: add functions documentation
+ ifconfig-ipv6(-push): allow using hostnames
+ tls-crypt: properly cast time_t to uint64_t
+ implement platform generic networking API
+ implement networking API for iproute2
+ introduce sitnl: Simplified Interface To NetLink
+ tun.c: use new networking API to handle tun interface on Linux
+ travis.yml: add test for iproute2 net implementation
+ route.c: use new networking API to handle routing table on Linux
+ unit tests: implement test for sitnl
+ t_net.sh: make bash dep explicit and run only if SITNL is compiled
+ t_net.sh: properly perform sudo check and print test steps
+ route.c: fix windows build by removing mismatching function parameter
+ t_net.sh: fixes for the networking test script
+ route.c: use sitnl to implement get_default_gateway_ipv6()
+ networking/best_gw: remove useless prefixlen parameter
+ sitnl: harden strncpy() by forcing arguments to have the same length
+ mbedtls: fix segfault by calling mbedtls_cipher_free() in cipher_ctx_free()
+ networking: extend API for better memory management
+ tun.c: undo_ifconfig_ipv4/6 remove useless gc argument
+ networking_sitnl.c: uncrustify file
+ route.c: simplify ifdef logic
+ t_net.sh: wait for NO-CARRIER bit to settle before starting test
+ t_net.sh: execute sleep after checking exit code of previous command
+ maddr: create helper function to populate maddr object from eth_addr
+ VLAN: add basic VLAN tagging support
+ maddr: export VLAN ID from client context to maddr object
+ VLAN: filter multicast and client-to-client unicast traffic
+ is_ipv_X: add support for parsing IP header inside a 802.1q frame
+ VLAN: implement support for forwarding only pre-tagged VLAN packets
+ VLAN: allow forwarding tagged and untagged packets on the server TAP device
+ VLAN: add documentation to manpage
+ socks: use the right function when printing struct openvpn_sockaddr
+ add -Wno-stringop-truncation to CFLAGS on linux
+ get rid of 'broadcast' argument when configuring the tun device
+ auth_token_kt: ensure key_type object is initialized
+ auth.c: make cast explicit in the crypto API
+ travis: compile with -Werror on Linux
+ travis: fix CFLAGS assignment error and add -Werror only when compiling on Linux for Linux
+ sitnl: fix failure reporting by keeping error negative
+ sitnl: fix TUN/TAP confusion in error messages
+ sitnl: fix ignoring EEXIST when sending a netlink command
+ t_net.sh: use dummy interface instead of tun
+ remove bogus file check on --genkey argument
+ t_net.sh: assign MAC address directly during interface creation
+ convert *_inline attributes to bool
+ options: fix inlining auth-gen-token-secret file
+ tls-crypt-v2: fix testing of inline key
+ get rid of INLINE_FILE_TAG constant
+ pool: prevent IPv6 pools to be larger than 2^16 addresses
+ pool: allow to configure an IPv6-only ifconfig-pool
+ allow usage of --server-ipv6 even when no --server is specified
+ pool: add support for ifconfig-pool-persist with IPv6 only
+ route: warn on IPv4 routes installation when no IPv4 is configured
+ options: enable IPv4 redirection logic only if really required
+ ipv6-pool: get rid of size constraint
+ pool: remove useless 'options.h' include
+ multi: skip IPv4 logic in multi_select_virtual_addr() if no pool is configured
+ multi.c: use mi->cc_config instead of config variable
+ options: don't leak inline'd key material in logfile
+ t_net.sh: drop hard dependency on t_client.rc
+ travis: don't run t_net.sh test
+
+Arne Schwabe (124):
Set tls-cipher restriction before loading certificates
Print ec bit details, refuse management-external-key if key is not RSA
+ Replace buffer backed strings for management_android_control with simple stack variables
+ Treat dhcp-option DNS6 and DNS identical
+ show the right string for key-direction
+ Add MTU to Android IFCONFIG6 control command
+ Properly free tuntap struct on android when emulating persist-tun
+ Add OpenSSL compat definition for RSA_meth_set_sign
+ Skip error about ioctl(SIOCGIFCONF) failed on Android
+ Factor out convert_tls_list_to_openssl method
+ Remove AUTO_USERID feature
+ Remove MANAGMENT_EXTERNAL_KEY, MANAGMENT_IN_EXTRA, ENABLE_CLIENT_CR
+ Add support for tls-ciphersuites for TLS 1.3
+ Add better support for showing TLS 1.3 ciphersuites in --show-tls
+ Use right function to set TLS1.3 restrictions in show-tls
+ Refuse mbed TLS external key with non RSA certificates
+ Add message explaining early TLS client hello failure
+ Add tls-crypt-v2 to the list of supported inline options
+ Implement block-ipv6
+ Fallback to password authentication when auth-token fails
+ Fix loading inline tls-crypt-v2 keys with mbed TLS
+ Refactor tls_crypt_v2_write_server_key_file into crypto.c
+ Add send_control_channel_string_dowork variant
+ Rename tls_crypt_v2_read_keyfile into generic pem_read_key_file
+ Fix poll.h logic in syshead.h
+ Write key to stdout if filename is not given
+ Implement --genkey type keyfile syntax and migrate tls-crypt-v2
+ Add generate_ephemeral_key that allows a random ephermal key
+ Remove -no-cpp-precomp flag from Darwin builds
+ Fix check if iface name is set
+ Adjust Android code after sitnl patch merge
+ Rewrite auth-token-gen to be based on HMAC based tokens
+ Implement a permanent session id in auth-token
+ Sent indication that a session is expired to clients
+ Implement unit tests for auth-gen-token
+ Make tls_version_max return the actual maximum version
+ Add support for OpenSSL TLS 1.3 when using management-external-key
+ Document tls-ciphersuites also in --help output
+ Only announce IV_NCP=2 when we are willing to support these ciphers
+ Add strsep compat function
+ Implement dynamic NCP negotiation
+ Warn about insecure ciphers also in init_key_type
+ Move NCP related function into a seperate file and add unit tests
+ Normalise ncp-ciphers option and restrict it to 127 bytes
+ Fetch OpenSSL versions via source/old links
+ Fix OpenSSL error stack handling of tls_ctx_add_extra_certs
+ Fix off-by-one in tls-crypt-v2 client wrapping with custom metadata
+ Fix OpenSSL 1.1.1 not using auto elliptic curve selection
+ Refactor counting number of element in a : delimited list into function
+ Minor style change to improve code style
+ Another round of uncrustify code cleanup.
+ Fix tls_ctx_client/server_new leaving error on OpenSSL error stack
+ Add tls-crypt-v2 test writing metadata
+ Use crypto library functions for const time memcmp when possible
+ Fix session id in env missing first byte
+ Document reneweal mechanic of auth-token in manual
+ Fix session id and initial timestamp not being preserved
+ Do not write extra 0 byte for --gen-key with auth-token/tls-crypt-v2
+ Refuse server mode on Android
+ Add .git-blame-ignore-revs with reformat commits
+ Make cipher_kt_name always return normalised cipher name
+ Make cipher_kt_get also accept OpenVPN config cipher name
+ Implement parsing and sending INFO and INFO_PRE control messages
+ Implement support for signalling IV_SSO to server
+ Implement sending response to challenge via CR_RESPONSE
+ Implement sending AUTH_PENDING challenges to clients
+ Implement forwarding client CR_RESPONSE messages to management
+ Add unit test for cipher name translations
+ Make compression asymmetric by default and add warnings
+ Reformat files using uncrustify
+ Remove parameter config from multi_client_connect_mda
+ Remove push_reply_deferred variable
+ Remove did_open_context, defined and connection_established_flag
+ merge key_state->authenticated and key_state->auth_deferred
+ Simplify multi_connection_established.
+ Deprecate ncp-disable and add improved ncp to Changes.rst
+ Make key_state->authenticated more state machine like
+ Extract process_incoming_push_reply from process_incoming_push_msg
+ Removed unused definition
+ Code cleanup: remove superflous variable
+ Move protocol option negotiation from push_prepare to new function
+ Generate data channel keys after connect options have been parsed
+ Cleanup: Remove special case code for old poor man's NCP.
+ Allow changing fallback cipher from ccd files/client-connect
+ client-connect: Change cas_context from int to enum
+ client-connect: Move adding inotify watch into its own function
+ reformat multi_client_generate_tls_keys according to uncrustify
+ client-connect: Add CC_RET_DEFERRED and cope with deferred client-connect
+ Remove CAS_PARTIAL state
+ client-connect: Use inotify for the deferred client-connect status file
+ client-connect: Implement deferred connect support for plugin API v2
+ Drop support for OpenSSL 1.0.1
+ Require AEAD support in the crypto library
+ Remove key-method 1
+ Remove ENABLE_OCC #define
+ Implement tls-groups option to specify eliptic curves/groups
+ Avoid sending --cipher to clients not supporting NCP
+ Indicate that a client is in pull mode in IV_PROTO
+ Deprecate --inetd
+ Include utun device number in utun error messages
+ Simplify calling logic of check_connection_established_dowork
+ Avoid sending push request after receving push reply
+ Rename ncp-ciphers to data-ciphers
+ Add a note that ncp-ciphers is replaced by data-ciphers
+ client-connect: Add documentation for the deferred client connect feature
+ Rework NCP compability logic and drop BF-CBC support by default
+ Document different behaviour of dynamic cipher negotiation
+ Minor cleanup in push.c
+ Clean up a number of leftover C89 initialisations in ssl.c
+ Remove buf argument from link_socket_set_outgoing_addr
+ Remove a number of check/do_work wrapper calls from coarse_timers
+ Split pf_check_reload check and check timer in process_coarse_timers
+ Rename check_ping_restart_dowork to trigger_ping_timeout_signal
+ Eliminate check_fragment function
+ Eliminate check_incoming_control_channel wrapper function
+ Eliminate check_tls wrapper function
+ Merge check_coarse_timers and check_coarse_timers_dowork
+ Skip existing interfaces on opening the first available utun on macOS
+ Move parsing IV_PROTO to separate function
+ Remove S_OP_NORMAL key state.
+ Document comp-lzo no and compress being incompatible
+ Refactor/Reformat tls_pre_decrypt
+ Cleanup tls_pre_decrypt_lite and tls_pre_encrypt
+ Improve sections about older OpenVPN clients in cipher-negotiation.rst
+
+Bertrand Bonnefoy-Claudet (1):
+ Fix typo in error message: "optione" -> "option"
+
+Christian Ehrhardt (1):
+ systemd: extend CapabilityBoundingSet for auth_pam
+
+Christian Hesse (7):
+ man: fix formatting for alternative option
+ systemd: Use automake tools to install unit files
+ systemd: Do not race on RuntimeDirectory
+ systemd: Add more security feature for systemd units
+ Clean up plugin path handling
+ plugin: Remove GNUism in openvpn-plugin.h generation
+ fix typo in notification message
+
+Christopher Schenk (3):
+ Set the correct mtu on windows based systems
+ Log a note if someone wants to set a MTU below 1280 on IPv6
+ Unified success messages for setting mtu
Conrad Hoffmann (2):
Use provided env vars in up/down script.
Document down-root plugin usage in client.down
-David Sommerseth (11):
+David Sommerseth (64):
+ docs: Further enhance the documentation related to SWEET32
+ man: Remove references to no longer present IV_RGI6 peer-info
+ build: Ensure Changes.rst is shipped and installed as a doc file
+ management: >REMOTE operation would overwrite ce change indicator
+ management: Remove a redundant #ifdef block
+ git: Merge .gitignore files into a single file
+ systemd: Move the READY=1 signalling to an earlier point
+ dev-tools: Simple tool which automates rebasing LZ4 compat library
+ dev-tools: lz4-rebaser tool carried a typo
+ plugin: Improve the handling of default plug-in directory
+ cleanup: Remove faulty env processing functions
+ auth-token: Ensure tokens are always wiped on de-auth
+ docs: Fixed man-page warnings discoverd by rpmlint
+ Make --cipher/--auth none more explicit on the risks
+ Require minimum OpenSSL 1.0.1
+ Fix broken ./configure on systems without openssl.pc
+ plugin: Fix documentation typo for type_mask
+ plugin: Export secure_memzero() to plug-ins
+ crypto: Enable SHA256 fingerprint checking in --verify-hash
+ copyright: Update GPLv2 license texts
+ dev-tools: Script generating the source releases in an automated fashion
+ auth-token with auth-nocache fix broke --disable-crypto builds
doc: The CRL processing is not a deprecated feature
cleanup: Move write_pid() to where it is being used
contrib: Remove keychain-mcd code
cleanup: Move init_random_seed() to where it is being used
- sample-plugins: fix ASN1_STRING_to_UTF8 return value checks
Highlight deprecated features
Use consistent version references
docs: Replace all PolarSSL references to mbed TLS
systemd: Ensure systemd shuts down OpenVPN in a proper way
systemd: Enable systemd's auto-restart feature for server profiles
lz4: Move towards a newer LZ4 API
-
-Emmanuel Deloget (3):
- OpenSSL: remove pre-1.1 function from the OpenSSL compat interface
- OpenSSL: remove EVP_CIPHER_CTX_new() from the compat layer
- OpenSSL: remove EVP_CIPHER_CTX_free() from the compat layer
-
-Gert van Dijk (1):
- Warn that DH config option is only meaningful in a tls-server context
-
-Ilya Shipitsin (3):
- travis-ci: add 3 missing patches from master to release/2.4
- travis-ci: update openssl to 1.0.2l, update mbedtls to 2.5.1
- travis-ci: update pkcs11-helper to 1.22
-
-Richard Bonhomme (1):
- man: Corrections to doc/openvpn.8
-
-Steffan Karger (17):
- Fix typo in extract_x509_extension() debug message
- Move adjust_power_of_2() to integer.h
- Undo cipher push in client options state if cipher is rejected
- Remove strerror_ts()
- Move openvpn_sleep() to manage.c
- fixup: also change missed openvpn_sleep() occurrences
- Always use default keysize for NCP'd ciphers
- Move create_temp_file() out of #ifdef ENABLE_CRYPTO
- Deprecate --keysize
- Deprecate --no-replay
- Move run_up_down() to init.c
- tls-crypt: introduce tls_crypt_kt()
- crypto: create function to initialize encrypt and decrypt key
- Add coverity static analysis to Travis CI config
- tls-crypt: don't leak memory for incorrect tls-crypt messages
- travis: reorder matrix to speed up build
- Fix bounds check in read_key()
-
-Szilárd Pfeiffer (1):
- OpenSSL: Always set SSL_OP_CIPHER_SERVER_PREFERENCE flag
-
-Thomas Veerman via Openvpn-devel (1):
- Fix socks_proxy_port pointing to invalid data
-
-
-2017.06.21 -- Version 2.4.3
-Antonio Quartulli (1):
- Ignore auth-nocache for auth-user-pass if auth-token is pushed
-
-David Sommerseth (3):
- crypto: Enable SHA256 fingerprint checking in --verify-hash
- copyright: Update GPLv2 license texts
- auth-token with auth-nocache fix broke --disable-crypto builds
-
-Emmanuel Deloget (8):
+ lz4: Fix confused version check
+ lz4: Fix broken builds when pkg-config is not present but system library is
+ Remove references to keychain-mcd in Changes.rst
+ lz4: Rebase compat-lz4 against upstream v1.7.5
+ systemd: Add and ship README.systemd
+ Update copyright to include 2018 plus company name change
+ man: Add .TQ groff support macro
+ man: Reword --management to prefer unix sockets over TCP
+ management: Warn if TCP port is used without password
+ plugin: Export base64 encode and decode functions
+ build: Fix build warnings related to get_random()
+ build: Fix another compile warning in console_systemd.c
+ cleanup: Remove RPM openvpn.spec build approach
+ docs: Update INSTALL
+ build: Package missing mock_msg.h
+ auth-token: Fix building with --disable-server
+ auth-token: Fix compiler complaints with --disable-management
+ Improve the comments related to auth-token-hmac patches
+ Documented all the argv related code with minor refactoring
+ build: Remove --disable-server from ./configure
+ options: Fix failing inline tls-auth/crypt with persist-key
+ options: Restore --tls-crypt-v2 inline file capability
+ doc/man: convert openvpn.8 to split-up .rst files
+ doc/man: Mark compression options as deprecated
+ doc/man: Adopt compression documentation
+ doc/man: Documentation for --bind-dev / VRFs on Linux
+ doc/man: Add misssing renegotiation.rst to Makefile.am
+ Remove --no-iv
+ doc/man: Do not install man *.rst files
+ travis: Fix make distcheck failure
+ Remove --ifconfig-pool-linear
+ Remove --client-cert-not-required
+
+Domagoj Pensa (2):
+ Fix linking issues on MinGW
+ Skip DNS address validation
+
+Emmanuel Deloget (20):
+ OpenSSL: check for the SSL reason, not the full error
+ OpenSSL: don't use direct access to the internal of X509_STORE_CTX
+ OpenSSL: don't use direct access to the internal of SSL_CTX
+ OpenSSL: don't use direct access to the internal of X509_STORE
+ OpenSSL: don't use direct access to the internal of X509_OBJECT
+ OpenSSL: don't use direct access to the internal of RSA_METHOD
+ OpenSSL: SSLeay symbols are no longer available in OpenSSL 1.1
+ OpenSSL: use EVP_CipherInit_ex() instead of EVP_CipherInit()
OpenSSL: don't use direct access to the internal of X509
OpenSSL: don't use direct access to the internal of EVP_PKEY
OpenSSL: don't use direct access to the internal of RSA
@@ -404,14 +619,87 @@ Emmanuel Deloget (8):
OpenSSL: don't use direct access to the internal of EVP_MD_CTX
OpenSSL: don't use direct access to the internal of EVP_CIPHER_CTX
OpenSSL: don't use direct access to the internal of HMAC_CTX
+ OpenSSL: remove pre-1.1 function from the OpenSSL compat interface
+ OpenSSL: remove EVP_CIPHER_CTX_new() from the compat layer
+ OpenSSL: remove EVP_CIPHER_CTX_free() from the compat layer
+ OpenSSL: check EVP_PKEY key types before returning the pkey
-Gert Doering (6):
+Eric Thorpe (1):
+ Fix Building Using MSVC
+
+Fabian Knittel (7):
+ client-connect: Split multi_connection_established into separate functions
+ client-connect: Refactor multi_client_connect_source_ccd
+ client-connect: Move multi_client_connect_setenv into early_setup
+ client-connect: Refactor to use return values instead of modifying a passed-in flag
+ client-connect: Refactor client-connect handling to calling a bunch of hooks in a loop
+ client-connect: Add deferred support to the client-connect script handler
+ client-connect: Add deferred support to the client-connect v1 plugin handler
+
+Gert Doering (50):
+ Remove IV_RGI6=1 peer-info signalling.
+ Add openssl_compat.h to openvpn_SOURCES
+ Fix '--dev null'
+ Fix installation of IPv6 host route to VPN server when using iservice.
+ Make ENABLE_OCC no longer depend on !ENABLE_SMALL
Fix NCP behaviour on TLS reconnect.
Remove erroneous limitation on max number of args for --plugin
+ proxy.c refactoring: remove always-NULL gc parameter
Fix edge case with clients failing to set up cipher on empty PUSH_REPLY.
Fix potential 1-byte overread in TCP option parsing.
Fix remotely-triggerable ASSERT() on malformed IPv6 packet.
Update Changes.rst with relevant info for 2.4.3 release.
+ Remove warning on pushed tun-ipv6 option.
+ Fix removal of on-link prefix on windows with netsh
+ Fix potential double-free() in Interactive Service (CVE-2018-9336)
+ Add %d, %u and %lu tests to test_argv unit tests.
+ Extend push-remove to also handle 'ifconfig'.
+ Print lzo_init() return code in case of errors
+ Uncrustify sample-plugin sources according to code style
+ uncrustify openvpnserv/ sources
+ uncrustify openvpn/ sources
+ Add 'printing of port number' to mroute_addr_print_ex() for v4-mapped v6.
+ Stop complaining about IPv6 routes without gateway address.
+ Copy one byte less in strncpynt()
+ Remove cmocka submodule, rely on system-wide installation instead.
+ Increase listen() backlog queue to 32
+ repair tap mode on OpenSolaris/OpenIndiana
+ Fix IPv6 routes on tap interfaces on OpenSolaris/OpenIndiana
+ OpenSolaris/OpenIllumos: use /bin/bash if available for test scripts.
+ Force combinationation of --socks-proxy and --proto UDP to use IPv4.
+ Uncrustify the tests/unit_tests/ part of our tree.
+ Change client side of t_lpback.sh configs to use inline material.
+ Simplify pool size handling, fix possible array overrun on pool reading.
+ Change timestamps in file-based logging to ISO 8601 time format.
+ Depreciation warning for --topology net30 on servers with IPv4 pools.
+ Convert plugin/auth-pam.c from stderr logging to plugin_log().
+ Add c1ff8f247f91c88a2df5502eeedf42857f9a6831 (engine, pool, SSO) to .git-blame-ignore-revs
+ Linux: do not change --txqueuelen OS default if not configured.
+ Fix 'engine' unit test on FreeBSD (specifically 'not GNU make')
+ t_client.sh: correctly report all failed instances in summary
+ Remove --writepid file on program exit.
+ Handle connecting clients without NCP or OCC without crashing.
+ Add deferred authentication support to plugin-auth-pam
+ Separate handling of non-deferred return values for client-connect-scripts.
+ Repair --inetd
+ Fix sequence of events for async plugin v1 handler.
+ Abort client-connect handler loop after first handler sets 'disable'.
+ Add depreciation notice for --ncp-disable to protocol-options.rst
+ Changes.rst updates in preparation to 2.5_beta1
+ Preparing release 2.5_beta1
+
+Gert van Dijk (7):
+ Warn that DH config option is only meaningful in a tls-server context
+ Add generated openvpn.doxyfile to .gitignore
+ manpage: improve description of --status and --status-version
+ Add negotiated cipher to status file format 2 and 3
+ Minor reliability layer documentation fixes
+ Make second parameter to reliable_send_purge() const
+ Remove unneeded newline in debug message in reliable.c
+
+Gisle Vanem (2):
+ Crash in options.c
+ Wrong FILETYPE in .rc files
Guido Vranken (6):
refactor my_strupr
@@ -421,125 +709,303 @@ Guido Vranken (6):
Fix a null-pointer dereference in establish_http_proxy_passthru()
Prevent two kinds of stack buffer OOB reads and a crash for invalid input data
-Jérémie Courrèges-Anglas (2):
- Fix an unaligned access on OpenBSD/sparc64
- Missing include for socket-flags TCP_NODELAY on OpenBSD
+Heiko Hund (3):
+ re-implement argv_printf_*()
+ argv: do fewer memory re-allocations
+ Add gc_arena to struct argv to save allocations
-Matthias Andree (1):
- Make openvpn-plugin.h self-contained again.
+Hilko Bengen (1):
+ Do not set pkcs11-helper 'safe fork mode'
-Selva Nair (1):
- Pass correct buffer size to GetModuleFileNameW()
+Hristo Venev (1):
+ Fix extract_x509_field_ssl for external objects, v2
-Steffan Karger (11):
- Log the negotiated (NCP) cipher
- Avoid a 1 byte overcopy in x509_get_subject (ssl_verify_openssl.c)
- Skip tls-crypt unit tests if required crypto mode not supported
- openssl: fix overflow check for long --tls-cipher option
- Add a DSA test key/cert pair to sample-keys
- Fix mbedtls fingerprint calculation
- mbedtls: fix --x509-track post-authentication remote DoS (CVE-2017-7522)
- mbedtls: require C-string compatible types for --x509-username-field
- Fix remote-triggerable memory leaks (CVE-2017-7521)
- Restrict --x509-alt-username extension types
- Fix potential double-free in --x509-alt-username (CVE-2017-7521)
+Ilya Shipitsin (18):
+ Resolve several travis-ci issues
+ github: Add PR template with contributor related information
+ travis-ci: add 'make distcheck' to test scenario, V2
+ travis-ci: remove unused files
+ v4, travis-ci: add 2 mingw "build only" configurations
+ travis-ci: added gcc and clang openssl-1.1.0 builds
+ travis-ci: update openssl to 1.0.2l, update mbedtls to 2.5.1
+ travis-ci: update pkcs11-helper to 1.22
+ travis-ci: add brew cache, remove ccache
+ travis-ci: modify openssl build script to support openssl-1.1.0
+ travis-ci: cleanup, refactor, upgrade ssl libraries
+ travis-ci: add "linux-ppc64le" to build matrix
+ travis-ci: change trusty image to xenial
+ travis-ci: update osx to xcode9.4 and modernize brew management
+ configure.ac: fix compile-time error in argv_testdriver
+ travis-ci: fix osx builds
+ travis-ci: update components versions
+ travis-ci: add arm64, s390x builds.
-Steven McDonald (1):
- Fix gateway detection with OpenBSD routing domains
+James Bekkema (2):
+ Resolves small IV_GUI_VER typo in the documentation.
+ Adds support for setting the default IPv6 gateway for routes using the route-ipv6-gateway option.
+James Bottomley (7):
+ autoconf: Fix engine checks for openssl 1.1
+ openssl: add engine method for loading the key
+ crypto_openssl: add initialization to pick up local configuration
+ crypto_openssl: add include for openssl/conf.h
+ Add unit tests for engine keys
+ Fix make distcheck for new engine key unit test
+ engine-key tests: make check_engine_keys.sh work with --enable-small
-2017.05.11 -- Version 2.4.2
-David Sommerseth (5):
- auth-token: Ensure tokens are always wiped on de-auth
- docs: Fixed man-page warnings discoverd by rpmlint
- Make --cipher/--auth none more explicit on the risks
- plugin: Fix documentation typo for type_mask
- plugin: Export secure_memzero() to plug-ins
+Jan Just Keijser (1):
+ Added support for DHCP option 119 (dns search suffix list) for Windows.
-Hristo Venev (1):
- Fix extract_x509_field_ssl for external objects, v2
+Jeremie Courreges-Anglas (5):
+ Cast time_t to long long in order to print it.
+ Print time_t as long long and suseconds_t as long
+ Cast and print another suseconds_t as long
+ Use long long to format time_t-related environment variables
+ Fix build with LibreSSL
-Selva Nair (1):
- In auth-pam plugin clear the password after use
+Jeremy Evans (1):
+ Switch assertion failure to returning false
-Steffan Karger (10):
- cleanup: merge packet_id_alloc_outgoing() into packet_id_write()
- Don't run packet_id unit tests for --disable-crypto builds
- Fix Changes.rst layout
- Fix memory leak in x509_verify_cert_ku()
- mbedtls: correctly check return value in pkcs11_certificate_dn()
- Restore pre-NCP frame parameters for new sessions
- Always clear username/password from memory on error
- Document tls-crypt security considerations in man page
- Don't assert out on receiving too-large control packets (CVE-2017-7478)
- Drop packets instead of assert out if packet id rolls over (CVE-2017-7479)
+Jonathan K. Bullard (1):
+ Clarify and expand management interface documentation
-ValdikSS (1):
- Set a low interface metric for tap adapter when block-outside-dns is in use
+Jonathan Tooker (1):
+ Fix various spelling mistakes
-2017.03.21 -- Version 2.4.1
-Antonio Quartulli (4):
- attempt to add IPv6 route even when no IPv6 address was configured
- fix redirect-gateway behaviour when an IPv4 default route does not exist
- CRL: use time_t instead of struct timespec to store last mtime
- ignore remote-random-hostname if a numeric host is provided
+Joost Rijneveld (1):
+ Make return code external tls key match docs
-Christian Hesse (7):
- man: fix formatting for alternative option
- systemd: Use automake tools to install unit files
- systemd: Do not race on RuntimeDirectory
- systemd: Add more security feature for systemd units
- Clean up plugin path handling
- plugin: Remove GNUism in openvpn-plugin.h generation
- fix typo in notification message
+Jérémie Courrèges-Anglas (2):
+ Fix an unaligned access on OpenBSD/sparc64
+ Missing include for socket-flags TCP_NODELAY on OpenBSD
-David Sommerseth (6):
- management: >REMOTE operation would overwrite ce change indicator
- management: Remove a redundant #ifdef block
- git: Merge .gitignore files into a single file
- systemd: Move the READY=1 signalling to an earlier point
- plugin: Improve the handling of default plug-in directory
- cleanup: Remove faulty env processing functions
+Kyle Evans (1):
+ tests/t_lpback.sh: Switch sed(1) to POSIX-compatible regex.
-Emmanuel Deloget (8):
- OpenSSL: check for the SSL reason, not the full error
- OpenSSL: don't use direct access to the internal of X509_STORE_CTX
- OpenSSL: don't use direct access to the internal of SSL_CTX
- OpenSSL: don't use direct access to the internal of X509_STORE
- OpenSSL: don't use direct access to the internal of X509_OBJECT
- OpenSSL: don't use direct access to the internal of RSA_METHOD
- OpenSSL: SSLeay symbols are no longer available in OpenSSL 1.1
- OpenSSL: use EVP_CipherInit_ex() instead of EVP_CipherInit()
+Lev Stipakov (46):
+ win: support for Visual Studio 2017
+ Refactor NCP-negotiable options handling
+ init.c: refine functions names and description
+ openvpnserv: clarify return values type
+ crypto.h: remove unused function declaration
+ interactive.c: fix usage of potentially uninitialized variable
+ options.c: fix broken unary minus usage
+ Introduce openvpn_swprintf() with nul termination guarantee
+ Wrap openvpn_swprintf into Windows define
+ test_tls_crypt.c: fix global-buffer-overflow found by AddressSanitizer
+ crypto_openssl.c: fix heap-buffer-overflow found by AddressSanitizer
+ Fix various compiler warnings
+ Fix broken fragment/mssfix with NCP
+ crypto.c: fix Visual Studio build
+ tun.h: change tun_set() return value type to void
+ tun.h: remove TUN_PASS_BUFFER define
+ tapctl: add optional 'hardware id' parameter
+ vcxproj: add missing source files
+ push.c: fix Visual Studio build
+ Visual Studio: make it easier to build with VS
+ msvc: OpenSSL 1.1.x support
+ travis: add Visual Studio build
+ Visual Studio: upgrade project files to VS2019
+ wintun: add --windows-driver config option
+ wintun: implement opening wintun device
+ travis: bump MSVC to 2019
+ travis: bump clang version
+ wintun: ring buffers based I/O
+ wintun: interactive service support
+ wintun: set adapter properties via interactive service
+ wintun: clear adapter settings on tun close
+ tun.c: refactor open_tun() implementation
+ tun.c: do not add/remove on-link IPv4 route on tun open/close
+ options.c: do not force route delay when not using DHCP
+ configure.ac: simplify AC_CHECK_FUNCS statements
+ cryptoapi.c: fix run-time check failure in msvc debugger
+ interactive.c: remove unused function
+ tun.c: fix 'use after free' error
+ Fix building with --enable-async-push in FreeBSD
+ Fix broken async push with NCP is used
+ Fix illegal client float (CVE-2020-11810)
+ msvc: fix various level2 warnings
+ tap.c: fix adapter renaming
+ Improve Windows version detection with manifest
+ wintun: remove SYSTEM elevation hack
+ Fix compilation with --disable-lzo and --disable-lz4
-Eric Thorpe (1):
- Fix Building Using MSVC
+Matthias Andree (3):
+ Make openvpn-plugin.h self-contained again.
+ Merge Makefile.am's AUTOMAKE_OPTIONS into configure.ac's AM_INIT_AUTOMAKE.
+ Fix stack buffer overruns in NEXTADDR() macro:
-Gert Doering (4):
- Add openssl_compat.h to openvpn_SOURCES
- Fix '--dev null'
- Fix installation of IPv6 host route to VPN server when using iservice.
- Make ENABLE_OCC no longer depend on !ENABLE_SMALL
+Maxim Plotnikov (1):
+ OpenSSL: Fix --crl-verify not loading multiple CRLs in one file
-Gisle Vanem (1):
- Crash in options.c
+Maximilian Wilhelm (1):
+ Add --bind-dev option.
-Ilya Shipitsin (2):
- Resolve several travis-ci issues
- travis-ci: remove unused files
+Michal Soltys (1):
+ man: correct the description of --capath and --crl-verify regarding CRLs
+
+Mykola Baibuz (1):
+ Fix typo in NTLM proxy debug message
Olivier Wahrenberger (1):
Fix building with LibreSSL 2.5.1 by cleaning a hack.
-Selva Nair (4):
+Richard Bonhomme (3):
+ man: Corrections to doc/openvpn.8
+ Ignore --pull-filter for --mode server
+ doc/man: Update --txqueuelen default setting (Now OS default)
+
+Richard van den Berg via Openvpn-devel (1):
+ Fix error message when using RHEL init script
+
+Rosen Penev (2):
+ Remove wrong poll.h include
+ openssl: Fix compilation without deprecated OpenSSL 1.1 APIs
+
+Samy Mahmoudi (1):
+ man: correct a --redirection-gateway option flag
+
+Santtu Lakkala (1):
+ Fix OpenSSL private key passphrase notices
+
+Selva Nair (55):
Fix push options digest update
Always release dhcp address in close_tun() on Windows.
Add a check for -Wl, --wrap support in linker
Fix user's group membership check in interactive service to work with domains
+ In auth-pam plugin clear the password after use
+ Pass correct buffer size to GetModuleFileNameW()
+ Check whether in pull_mode before warning about previous connection blocks
+ Avoid illegal memory access when malformed data is read from the pipe
+ Fix missing check for return value of malloc'd buffer
+ Return NULL if GetAdaptersInfo fails
+ Use RSA_meth_free instead of free
+ Bring cryptoapi.c upto speed with openssl 1.1
+ Add SSL_CTX_get_max_proto_version() not in openssl 1.0
+ TLS v1.2 support for cryptoapicert -- RSA only
+ Refactor ssl_openssl.c in prep for external EC key support
+ Refactor get_interface_metric to return metric and auto flag separately
+ Add management client version
+ Prompt for signature using '>PK_SIGN' if the client supports it
+ Allow external EC key through --management-external-key
+ Ensure strings read from registry are null-terminated
+ Make most registry values optional
+ Use lowest metric interface when multiple interfaces match a route
+ Move code to free cd to a function CAPI_DATA_free()
+ Disable external ec key support when building with libressl
+ Adapt to RegGetValue brokenness in Windows 7
+ Fix format spec errors in Windows builds
+ Move setting private key to a function in prep for EC support
+ Support EC certificates with cryptoapicert
+ Delete the IPv6 route to the "connected" network on tun close
+ Management: warn about password only when the option is in use
+ Avoid overflow in wakeup time computation
+ Replace M_DEBUG with D_LOW as the former is too verbose
+ Correct the declaration of handle in 'struct openvpn_plugin_args_open_return'
+ Parse static challenge response in auth-pam plugin
+ Bump version of openvpn plugin argument structs to 5
+ Accept empty password and/or response in auth-pam plugin
+ Pass the hash without the DigestInfo header to NCryptSignHash()
+ Move get system directory to a separate function
+ Enable dhcp on tap adapter using interactive service
+ Refactor sending commands to interactive service
+ Declare Windows version of openvpn_execve() before use
+ White-list pull-filter and script-security in interactive service
+ Move OpenSSL vs CNG signature digest type mapping to a function
+ Handle PSS padding in cryptoapicert
+ Better error message when script fails due to script-security setting
+ Correct the return value of cryptoapi RSA signature callbacks
+ Fix ACL_CHECK_ADD_COMPILE_FLAGS to work with clang
+ Swap the order of checks for validating interactive service user
+ Skip expired certificates in Windows certificate store
+ Allow unicode search string in --cryptoapicert option
+ Fix possibly uninitialized return value in GetOpenvpnSettings()
+ Fix possible access of uninitialized pipe handles
+ Move querying username/password from management to a function
+ When auth-user-pass file has no password query the management interface (if available).
+ Persist management-query-remote and proxy prompts
-Simon Matter (1):
+Simon Matter (2):
Fix segfault when using crypto lib without AES-256-CTR or SHA256
+ Add per session pseudo-random jitter to --reneg-sec intervals
-Steffan Karger (8):
+Simon Rozman (67):
+ Local functions are not supported in MSVC. Bummer.
+ Mixing wide and regular strings in concatenations is not allowed in MSVC.
+ RtlIpv6AddressToStringW() and RtlIpv4AddressToStringW() require mstcpip.h
+ Simplify iphlpapi.dll API calls
+ Fix local #include to use quoted form
+ Document ">PASSWORD:Auth-Token" real-time message
+ Fix typo in "verb" command examples
+ Uniform swprintf() across MinGW and MSVC compilers
+ MSVC meta files added to .gitignore list
+ openvpnserv: Review MSVC down-casting warnings
+ openvpnserv: Add support for multi-instances
+ Document missing OpenVPN states
+ Add Interactive Service developer documentation
+ Change quoted to angled form when #including external .h files
+ Signed/unsigned warnings of MSVC resolved
+ Reference msvc-generate from compat to assure correct build order
+ msvc: Move common project settings to reusable property sheets
+ msvc: Unify Unicode/MultiByte string setting across all cfg|plat
+ Introduce tapctl.exe utility and openvpnmsica.dll MSI CA
+ Set output name to libopenvpnmsica.dll in MSVC builds too
+ Prevent __stdcall name mangling of MSVC
+ Define _WIN32_WINNT=_WIN32_WINNT_VISTA in MSVC
+ Add MSI custom action for reliable Windows 10 detection
+ Detect TAP interfaces with root-enumerated hardware ID
+ Change C++ to C comments
+ Make MSI custom action debug pop-up more informative
+ Delete TAP interface before the TAP driver is uninstalled
+ Add detection of active VPN connections for MSI packages
+ Add a MSI custom actions to close and relaunch OpenVPN GUI
+ Make DriverCertification MSI property public
+ Extend FindSystemInfo custom action to detect OpenVPNService state
+ Uncrustify tapctl and openvpnmsica
+ Strip _stdcall suffixes (@nn) for 32-bit builds
+ Detect missing TAP driver and bail out gracefully
+ Disambiguate thread local storage references from TLS
+ Add NULL checks
+ Add user manual and developer notes URL for tapctl.exe
+ Refactor OpenVPNService state detection code
+ Add developer notes URL for openvpnmsica.dll
+ Limit tapctl.exe and openvpnmsica.dll to TAP-Windows6 adapters only
+ msvc: Add vlan.c/h
+ tun.c: make Windows device lookup functions more general
+ tun.c: upgrade get_device_guid() to return the Windows driver type
+ tun.c: make wintun_register_ring_buffer() non-fatal on failures
+ wintun: register ring buffers when iterating adapters
+ wintun: add support for --dev-node
+ tun.c: reword the at_least_one_tap_win() error
+ wintun: stop sending TAP-Windows6 ioctls to NDIS device
+ wintun: refactor code to use enum driver type
+ tun.c: refactor driver detection and make it case-insensitive
+ tun.c: uncrustify
+ wintun: check for conflicting options
+ openvpnmsica: Remove required Windows driver certification detection
+ openvpnmsica: Fix TAPInterface.DisplayName field interpretation
+ tapctl: Update documentation
+ wintun: upgrade error message in case of ring registration failure
+ tun.c: reorder IPv6 ifconfig on Windows
+ tapctl: Add functions for enabling/disabling adapters
+ openvpnmsica: Revise MSI custom actions interop
+ openvpnmsica: Simplify static function names
+ openvpnmsica, tapctl: "interface" => "adapter"
+ openvpnmsica: "TAP" => "TUN/TAP"
+ openvpnmsica: Extend to support arbitrary HWID network adapters
+ openvpnmsica, tapctl: Revise default hardware ID management
+ openvpnmsica: Merge FindTUNTAPAdapters into FindSystemInfo
+ tapctl: Support multiple hardware IDs
+ tun.c: revise the IPv4 ifconfig flow on Windows
+
+Stefan Strogin (1):
+ Use correct ifdefs for LibreSSL support
+
+Steffan Karger (122):
+ Document that RSA_SIGN can also request TLS 1.2 signatures
+ man: encourage user to read on about --tls-crypt
+ Textual fixes for Changes.rst
+ Remove deprecated --no-iv option
More broadly enforce Allman style and braces-around-conditionals
Use SHA256 for the internal digest, instead of MD5
OpenSSL: 1.1 fallout - fix configure on old autoconf
@@ -548,23 +1014,146 @@ Steffan Karger (8):
Fix non-C99-compliant builds: don't use const size_t as array length
Deprecate --ns-cert-type
Be less picky about keyUsage extensions
+ cleanup: merge packet_id_alloc_outgoing() into packet_id_write()
+ Don't run packet_id unit tests for --disable-crypto builds
+ Fix Changes.rst layout
+ Fix memory leak in x509_verify_cert_ku()
+ mbedtls: correctly check return value in pkcs11_certificate_dn()
+ Restore pre-NCP frame parameters for new sessions
+ Always clear username/password from memory on error
+ Document tls-crypt security considerations in man page
+ Don't assert out on receiving too-large control packets (CVE-2017-7478)
+ Drop packets instead of assert out if packet id rolls over (CVE-2017-7479)
+ Log the negotiated (NCP) cipher
+ Avoid a 1 byte overcopy in x509_get_subject (ssl_verify_openssl.c)
+ Skip tls-crypt unit tests if required crypto mode not supported
+ openssl: fix overflow check for long --tls-cipher option
+ Add a DSA test key/cert pair to sample-keys
+ Fix mbedtls fingerprint calculation
+ mbedtls: fix --x509-track post-authentication remote DoS (CVE-2017-7522)
+ mbedtls: require C-string compatible types for --x509-username-field
+ Fix remote-triggerable memory leaks (CVE-2017-7521)
+ Restrict --x509-alt-username extension types
+ Fix potential double-free in --x509-alt-username (CVE-2017-7521)
+ Fix typo in extract_x509_extension() debug message
+ init_key_ctx: key and iv arguments can (now) be const
+ Move adjust_power_of_2() to integer.h
+ Undo cipher push in client options state if cipher is rejected
+ Remove strerror_ts()
+ Move openvpn_sleep() to manage.c
+ fixup: also change missed openvpn_sleep() occurrences
+ Always use default keysize for NCP'd ciphers
+ Move create_temp_file() out of #ifdef ENABLE_CRYPTO
+ sample-plugins: fix ASN1_STRING_to_UTF8 return value checks
+ Deprecate --keysize
+ Move run_up_down() to init.c
+ tls-crypt: introduce tls_crypt_kt()
+ crypto: create function to initialize encrypt and decrypt key
+ Add coverity static analysis to Travis CI config
+ tls-crypt: don't leak memory for incorrect tls-crypt messages
+ travis: reorder matrix to speed up build
+ Fix bounds check in read_key()
+ buffer_list_aggregate_separator(): add unit tests
+ doxygen: add make target and use relative paths
+ Simplify and inline clear_buf()
+ Add --tls-cert-profile option.
+ pf: clean up temporary files if plugin init fails
+ pf: reject client if PF plugin is configured, but init fails
+ Don't throw fatal errors from create_temp_file()
+ create_temp_file/gen_path: prevent memory leak if gc == NULL
+ Use P_DATA_V2 for server->client packets too
+ Fix memory leak in buffer unit tests
+ travis: use clang's -fsanitize=address to catch more bugs
+ Don't throw fatal errors from verify_cert_export_cert()
+ buffer_list_aggregate_separator(): update list size after aggregating
+ buffer_list_aggregate_separator(): don't exceed max_len
+ buffer_list_aggregate_separator(): prevent 0-byte malloc
+ Fix types around buffer_list_push(_data)
+ ssl_openssl: fix compiler warning by removing getbio() wrapper
+ Fix --tls-version-min and --tls-version-max for OpenSSL 1.1+
+ Add support for TLS 1.3 in --tls-version-{min, max}
+ tls_ctx_set_tls_versions: move verify_flags to where it is used
+ Plug memory leak if push is interrupted
+ Log pre-handshake packet drops using D_MULTI_DROPPED
+ Enable stricter compiler warnings by default
+ reliable: remove reliable_unique_retry()
+ Get rid of ax_check_compile_flag.m4
+ mbedtls: don't use API deprecated in mbed 2.7
+ Warn if tls-version-max < tls-version-min
+ Check for more data in control channel
+ Move env helper functions into their own module/file
+ man: add security considerations to --compress section
+ openssl: don't use deprecated SSLEAY/SSLeay symbols
+ openssl: add missing #include statements
+ Move file-related functions from misc.c to platform.c
+ Move execve/run_script helper functions to run_command.c
+ Add crypto_pem_{encode,decode}()
+ Introduce buffer_write_file()
+ mbedtls: print warning if random personalisation fails
+ Fix memory leak after sighup
+ Remove unused void_ptr_hash_function and void_ptr_compare_function
+ Do not load certificate from tls_ctx_use_external_private_key()
+ mbedtls: make external signing code generic
+ mbedtls: remove dependency on mbedtls pkcs11 module
+ Fix memory leak in SSL_CTX_use_certificate
+ travis: add OpenSSL 1.1 Windows build
+ Fix use-after-free in tls_ctx_use_management_external_key
+ Simplify --genkey option syntax
+ Don't print OCC warnings about 'key-method', 'keydir' and 'tls-auth'
+ Add support for CHACHA20-POLY1305 in the data channel
+ List ChaCha20-Poly1305 as stream cipher
+ mbedtls: don't print unsupported ciphers in insecure cipher list
+ Fix mbedtls unit tests
+ buffer_list_aggregate_separator(): simplify code
+ tls-crypt-v2: add specification to doc/
+ tls-crypt-v2: generate tls-crypt-v2 keys
+ tls-crypt-v2: add unwrap_client_key
+ tls-crypt-v2: add P_CONTROL_HARD_RESET_CLIENT_V3 opcode
+ tls-crypt-v2: implement tls-crypt-v2 handshake
+ tls-crypt-v2: add script hook to verify metadata
+ tls-crypt-v2: clarify --tls-crypt-v2-genkey man page section
+ tls-crypt-v2: fix client reconnect bug
+ Remove deprecated --compat-x509-names and --no-name-remapping
+ Extend tls-crypt-v2 unit tests
+ Fix tls-auth/crypt in connection blocks with --persist-key
+ cmocka: use relative paths
+ tests: remove dependency on base64
+ configure.ac: add lzo CFLAGS/LIBS to the test flags
+ Update sample configs to use modern cipher, remove static key examples
+ mbedtls: add RFC 5705 keying material exporter support
+ Move keying material exporter check from syshead.h to configure.ac
+ Make openvpn --version exit with exit code 0
+ Gently push users towards --data-ciphers in --show-ciphers output
+Steven McDonald (1):
+ Fix gateway detection with OpenBSD routing domains
-2016.12.26 -- Version 2.4.0
-David Sommerseth (5):
- dev-tools: Added script for updating copyright years in files
- Update copyrights
- docs: Further enhance the documentation related to SWEET32
- man: Remove references to no longer present IV_RGI6 peer-info
- build: Ensure Changes.rst is shipped and installed as a doc file
+Szilárd Pfeiffer (1):
+ OpenSSL: Always set SSL_OP_CIPHER_SERVER_PREFERENCE flag
-Gert Doering (1):
- Remove IV_RGI6=1 peer-info signalling.
+Thomas Quinot (1):
+ Fix documentation of tls-verify script argument
-Steffan Karger (3):
- Document that RSA_SIGN can also request TLS 1.2 signatures
- man: encourage user to read on about --tls-crypt
- Textual fixes for Changes.rst
+Thomas Veerman via Openvpn-devel (1):
+ Fix socks_proxy_port pointing to invalid data
+
+Tom van Leeuwen (1):
+ mbedTLS: Make sure TLS session survives move
+
+ValdikSS (1):
+ Set a low interface metric for tap adapter when block-outside-dns is in use
+
+Vladislav Grishenko (1):
+ Log serial number of revoked certificate
+
+WGH (1):
+ docs: Add reference to X509_LOOKUP_hash_dir(3)
+
+hashiz (1):
+ Fix '--bind ipv6only'
+
+tincanteksup (1):
+ Correct error message for --tls-crypt-v2-genkey client
2016.12.16 -- Version 2.4_rc2
diff --git a/Changes.rst b/Changes.rst
index fee48e2..ba5ee1a 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -1,3 +1,480 @@
+Overview of changes in 2.5.4
+============================
+Bugfixes
+--------
+- fix prompting for password on windows console if stderr redirection
+ is in use - this breaks 2.5.x on Win11/ARM, and might also break
+ on Win11/adm64 when released.
+
+- fix setting MAC address on TAP adapters (--lladdr) to use sitnl
+ (was overlooked, and still used "ifconfig" calls)
+
+- various improvements for man page building (rst2man/rst2html etc)
+
+- minor bugfix with IN6_IS_ADDR_UNSPECIFIED() use (breaks build on
+ at least one platform strictly checking this)
+
+- fix minor memory leak under certain conditions in add_route() and
+ add_route_ipv6()
+
+User-visible Changes
+--------------------
+- documentation improvements
+
+- copyright updates where needed
+
+- better error reporting when win32 console access fails
+
+New features
+------------
+- also build man page on Windows builds
+
+
+Overview of changes in 2.5.3
+============================
+Bugfixes
+--------
+- CVE-2121-3606
+ see https://community.openvpn.net/openvpn/wiki/SecurityAnnouncements
+
+ OpenVPN windows builds could possibly load OpenSSL Config files from
+ world writeable locations, thus posing a security risk to OpenVPN.
+
+ As a fix, disable OpenSSL config loading completely on Windows.
+
+- disable connect-retry backoff for p2p (--secret) instances
+ (Trac #1010, #1384)
+
+- fix build with mbedtls w/o SSL renegotiation support
+
+- Fix SIGSEGV (NULL deref) receiving push "echo" (Trac #1409)
+
+- MSI installers: properly schedule reboot in the end of installation
+
+- fix small memory leak in free_key_ctx for auth_token
+
+
+User-visible Changes
+--------------------
+- update copyright messages in files and --version output
+
+New features
+------------
+- add --auth-token-user option (for --auth-token deployments without
+ --auth-user-pass in client config)
+
+- improve MSVC building for Windows
+
+- official MSI installers will now contain arm64 drivers and binaries
+ (x86, amd64, arm64)
+
+
+Overview of changes in 2.5.2
+============================
+
+Bugfixes
+--------
+- CVE-2020-15078
+ see https://community.openvpn.net/openvpn/wiki/SecurityAnnouncements
+
+ This bug allows - under very specific circumstances - to trick a
+ server using delayed authentication (plugin or management) into
+ returning a PUSH_REPLY before the AUTH_FAILED message, which can
+ possibly be used to gather information about a VPN setup.
+
+ In combination with "--auth-gen-token" or an user-specific token auth
+ solution it can be possible to get access to a VPN with an
+ otherwise-invalid account.
+
+- restore pushed "ping" settings correctly on a SIGUSR1 restart
+
+- avoid generating unecessary mbed debug messages - this is actually
+ a workaround for an mbedTLS 2.25 bug when using Curve25519 and Curve448
+ ED curves - mbedTLS crashes on preparing debug infos that we do not
+ actually need unless running with "--verb 8"
+
+- do not print inlined (<dh>...</dh>) Diffie Hellman parameters to log file
+
+- fix Linux/SITNL default route lookup in case of multiple routing tables
+ with more than one default route present (always use "main table" for now)
+
+- Fix CRL file handling in combination with chroot
+
+User-visible Changes
+--------------------
+
+- OpenVPN will now refuse to start if CRL file is not present at startup
+ time. At "reload time" absense of the CRL file is still OK (and the
+ in memory copy is used) but at startup it is now considered an error.
+
+
+New features
+------------
+- printing of the TLS ciphers negotiated has been extended, especially
+ displaying TLS 1.3 and EC certificates more correctly.
+
+
+Overview of changes in 2.5.1
+============================
+
+New features
+------------
+- "echo msg" support, to enable the server to pushed messages that are
+ then displayed by the client-side GUI. See doc/gui-notes.txt and
+ doc/management-notes.txt.
+
+ Supported by the Windows GUI shipped in 2.5.1, not yet supported by
+ Tunnelblick and the Android GUI.
+
+User-visible Changes
+--------------------
+- make OPENVPN_PLUGIN_ENABLE_PF plugin failures FATAL - if a plugin offers
+ to set the "openvpn packet filter", and returns a failure when requested
+ to, OpenVPN 2.5.0 would crash trying to clean up not-yet-initialized
+ structure members. Since PF is going away in 2.6.0, this is just turning
+ the crash into a well-defined program abort, and no further effort has
+ been spent in rewriting the PF plugin error handling (see trac #1377).
+
+Documentation
+-------------
+- rework sample-plugins/defer/simple.c - this is an extensive rewrite
+ of the plugin to bring code quality to acceptable standards and add
+ documentation on the various plugin API aspects. Since it's just
+ example code, filed under "Documentation", not under "Bugfix".
+
+- various man page improvements.
+
+- clarify ``--block-ipv6`` intent and direction
+
+Bugfixes
+--------
+- fix installation of openvpn.8 manpage on systems without docutils.
+
+- Windows: fix DNS search list setup for domains with "-" chars.
+
+- Fix tls-auth mismatch OCC message when tls-cryptv2 is used.
+
+- Windows: Skip DHCP renew with Wintun adapter (Wintun does not support
+ DHCP, so this was just causing an - harmless - error and needless delay).
+
+- Windows: Remove 1 second delay before running netsh - speeds up
+ interface init for wintun setups not using the interactive service.
+
+- Windows: Fix too early argv freeing when registering DNS - this would
+ cause a client side crash on Windows if ``register-dns`` is used,
+ and the interactive service is not used.
+
+- Android: Zero initialise msghdr prior to calling sendmesg.
+
+- Fix line number reporting on config file errors after <inline> segments
+ (see Trac #1325).
+
+- Fix port-share option with TLS-Crypt v2.
+
+- tls-crypt-v2: also preload tls-crypt-v2 keys (if --persist-key), otherwise
+ dropping privs on the server would fail.
+
+- tls-crypt-v2: fix server memory leak (about 600 bytes per connecting
+ client with tls-crypt-v2)
+
+- rework handling of server-pushed ``--auth-token`` in combination with
+ ``--auth-nocache`` on reconnection / TLS renegotiation events. This
+ used to "forget" to update new incoming token after a reconnection event
+ (leading to failure to reauth some time later) and now works in all
+ tested cases.
+
+
+Overview of changes in 2.5.0
+============================
+
+New features
+------------
+Client-specific tls-crypt keys (``--tls-crypt-v2``)
+ ``tls-crypt-v2`` adds the ability to supply each client with a unique
+ tls-crypt key. This allows large organisations and VPN providers to profit
+ from the same DoS and TLS stack protection that small deployments can
+ already achieve using ``tls-auth`` or ``tls-crypt``.
+
+ChaCha20-Poly1305 cipher support
+ Added support for using the ChaCha20-Poly1305 cipher in the OpenVPN data
+ channel.
+
+Improved Data channel cipher negotiation
+ The option ``ncp-ciphers`` has been renamed to ``data-ciphers``.
+ The old name is still accepted. The change in name signals that
+ ``data-ciphers`` is the preferred way to configure data channel
+ ciphers and the data prefix is chosen to avoid the ambiguity that
+ exists with ``--cipher`` for the data cipher and ``tls-cipher``
+ for the TLS ciphers.
+
+ OpenVPN clients will now signal all supported ciphers from the
+ ``data-ciphers`` option to the server via ``IV_CIPHERS``. OpenVPN
+ servers will select the first common cipher from the ``data-ciphers``
+ list instead of blindly pushing the first cipher of the list. This
+ allows to use a configuration like
+ ``data-ciphers ChaCha20-Poly1305:AES-256-GCM`` on the server that
+ prefers ChaCha20-Poly1305 but uses it only if the client supports it.
+
+ See the data channel negotiation section in the manual for more details.
+
+Removal of BF-CBC support in default configuration:
+ By default OpenVPN 2.5 will only accept AES-256-GCM and AES-128-GCM as
+ data ciphers. OpenVPN 2.4 allows AES-256-GCM,AES-128-GCM and BF-CBC when
+ no --cipher and --ncp-ciphers options are present. Accepting BF-CBC can be
+ enabled by adding
+
+ data-ciphers AES-256-GCM:AES-128-GCM:BF-CBC
+
+ and when you need to support very old peers also
+
+ data-ciphers-fallback BF-CBC
+
+ To offer backwards compatibility with older configs an *explicit*
+
+ cipher BF-CBC
+
+ in the configuration will be automatically translated into adding BF-CBC
+ to the data-ciphers option and setting data-ciphers-fallback to BF-CBC
+ (as in the example commands above). We strongly recommend to switching
+ away from BF-CBC to a more secure cipher.
+
+Asynchronous (deferred) authentication support for auth-pam plugin.
+ See src/plugins/auth-pam/README.auth-pam for details.
+
+Deferred client-connect
+ The ``--client-connect`` option and the connect plugin API allow
+ asynchronous/deferred return of the configuration file in the same way
+ as the auth-plugin.
+
+Faster connection setup
+ A client will signal in the ``IV_PROTO`` variable that it is in pull
+ mode. This allows the server to push the configuration options to
+ the client without waiting for a ``PULL_REQUEST`` message. The feature
+ is automatically enabled if both client and server support it and
+ significantly reduces the connection setup time by avoiding one
+ extra packet round-trip and 1s of internal event delays.
+
+Netlink support
+ On Linux, if configured without ``--enable-iproute2``, configuring IP
+ addresses and adding/removing routes is now done via the netlink(3)
+ kernel interface. This is much faster than calling ``ifconfig`` or
+ ``route`` and also enables OpenVPN to run with less privileges.
+
+ If configured with --enable-iproute2, the ``ip`` command is used
+ (as in 2.4). Support for ``ifconfig`` and ``route`` is gone.
+
+Wintun support
+ On Windows, OpenVPN can now use ``wintun`` devices. They are faster
+ than the traditional ``tap9`` tun/tap devices, but do not provide
+ ``--dev tap`` mode - so the official installers contain both. To use
+ a wintun device, add ``--windows-driver wintun`` to your config
+ (and use of the interactive service is required as wintun needs
+ SYSTEM privileges to enable access).
+
+IPv6-only operation
+ It is now possible to have only IPv6 addresses inside the VPN tunnel,
+ and IPv6-only address pools (2.4 always required IPv4 config/pools
+ and IPv6 was the "optional extra").
+
+Improved Windows 10 detection
+ Correctly log OS on Windows 10 now.
+
+Linux VRF support
+ Using the new ``--bind-dev`` option, the OpenVPN outside socket can
+ now be put into a Linux VRF. See the "Virtual Routing and Forwarding"
+ documentation in the man page.
+
+TLS 1.3 support
+ TLS 1.3 support has been added to OpenVPN. Currently, this requires
+ OpenSSL 1.1.1+.
+ The options ``--tls-ciphersuites`` and ``--tls-groups`` have been
+ added to fine tune TLS protocol options. Most of the improvements
+ were also backported to OpenVPN 2.4 as part of the maintainance
+ releases.
+
+Support setting DHCP search domain
+ A new option ``--dhcp-option DOMAIN-SEARCH my.example.com`` has been
+ defined, and Windows support for it is implemented (tun/tap only, no
+ wintun support yet). Other platforms need to support this via ``--up``
+ script (Linux) or GUI (OSX/Tunnelblick).
+
+per-client changing of ``--data-ciphers`` or ``data-ciphers-fallback``
+ from client-connect script/dir (NOTE: this only changes preference of
+ ciphers for NCP, but can not override what the client announces as
+ "willing to accept")
+
+Handle setting of tun/tap interface MTU on Windows
+ If IPv6 is in use, MTU must be >= 1280 (Windows enforces IETF requirements)
+
+Add support for OpenSSL engines to access private key material (like TPM).
+
+HMAC based auth-token support
+ The ``--auth-gen-token`` support has been improved and now generates HMAC
+ based user token. If the optional ``--auth-gen-token-secret`` option is
+ used clients will be able to seamlessly reconnect to a different server
+ using the same secret file or to the same server after a server restart.
+
+Improved support for pending authentication
+ The protocol has been enhanced to be able to signal that
+ the authentication should use a secondary authentication
+ via web (like SAML) or a two factor authentication without
+ disconnecting the OpenVPN session with AUTH_FAILED. The
+ session will instead be stay in a authenticated state and
+ wait for the second factor authentication to complete.
+
+ This feature currently requires usage of the managent interface
+ on both client and server side. See the `management-notes.txt`
+ ``client-pending-auth`` and ``cr-response`` commands for more
+ details.
+
+VLAN support
+ OpenVPN servers in TAP mode can now use 802.1q tagged VLANs
+ on the TAP interface to separate clients into different groups
+ that can then be handled differently (different subnets / DHCP,
+ firewall zones, ...) further down the network. See the new
+ options ``--vlan-tagging``, ``--vlan-accept``, ``--vlan-pvid``.
+
+ 802.1q tagging on the client side TAP interface is not handled
+ today (= tags are just forwarded transparently to the server).
+
+Support building of .msi installers for Windows
+
+Allow unicode search string in ``--cryptoapicert`` option (Windows)
+
+Support IPv4 configs with /31 netmasks now
+ (By no longer trying to configure ``broadcast x.x.x.x'' in
+ ifconfig calls, /31 support "just works")
+
+New option ``--block-ipv6`` to reject all IPv6 packets (ICMPv6)
+ this is useful if the VPN service has no IPv6, but the clients
+ might have (LAN), to avoid client connections to IPv6-enabled
+ servers leaking "around" the IPv4-only VPN.
+
+``--ifconfig-ipv6`` and ``--ifconfig-ipv6-push`` will now accept
+ hostnames and do a DNS lookup to get the IPv6 address to use
+
+
+Deprecated features
+-------------------
+For an up-to-date list of all deprecated options, see this wiki page:
+https://community.openvpn.net/openvpn/wiki/DeprecatedOptions
+
+- ``ncp-disable`` has been deprecated
+ With the improved and matured data channel cipher negotiation, the use
+ of ``ncp-disable`` should not be necessary anymore.
+
+- ``inetd`` has been deprecated
+ This is a very limited and not-well-tested way to run OpenVPN, on TCP
+ and TAP mode only, which complicates the code quite a bit for little gain.
+ To be removed in OpenVPN 2.6 (unless users protest).
+
+- ``no-iv`` has been removed
+ This option was made into a NOOP option with OpenVPN 2.4. This has now
+ been completely removed.
+
+- ``--client-cert-not-required`` has been removed
+ This option will now cause server configurations to not start. Use
+ ``--verify-client-cert none`` instead.
+
+- ``--ifconfig-pool-linear`` has been removed
+ This option is removed. Use ``--topology p2p`` or ``--topology subnet``
+ instead.
+
+- ``--compress xxx`` is considered risky and is warned against, see below.
+
+- ``--key-method 1`` has been removed
+
+
+User-visible Changes
+--------------------
+- If multiple connect handlers are used (client-connect, ccd, connect
+ plugin) and one of the handler succeeds but a subsequent fails, the
+ client-disconnect-script is now called immediately. Previously it
+ was called, when the VPN session was terminated.
+
+- Support for building with OpenSSL 1.0.1 has been removed. The minimum
+ supported OpenSSL version is now 1.0.2.
+
+- The GET_CONFIG management state is omitted if the server pushes
+ the client configuration almost immediately as result of the
+ faster connection setup feature.
+
+- ``--compress`` is nowadays considered risky, because attacks exist
+ leveraging compression-inside-crypto to reveal plaintext (VORACLE). So
+ by default, ``--compress xxx`` will now accept incoming compressed
+ packets (for compatibility with peers that have not been upgraded yet),
+ but will not use compression outgoing packets. This can be controlled with
+ the new option ``--allow-compression yes|no|asym``.
+
+- Stop changing ``--txlen`` aways from OS defaults unless explicitly specified
+ in config file. OS defaults nowadays are actually larger then what we used
+ to configure, so our defaults sometimes caused packet drops = bad performance.
+
+- remove ``--writepid`` pid file on exit now
+
+- plugin-auth-pam now logs via OpenVPN logging method, no longer to stderr
+ (this means you'll have log messages in syslog or openvpn log file now)
+
+- use ISO 8601 time format for file based logging now (YYYY-MM-DD hh:mm:dd)
+ (syslog is not affected, nor is ``--machine-readable-output``)
+
+- ``--clr-verify`` now loads all CRLs if more than one CRL is in the same
+ file (OpenSSL backend only, mbedTLS always did that)
+
+- when ``--auth-user-pass file`` has no password, and the management interface
+ is active, query management interface (instead of trying console query,
+ which does not work on windows)
+
+- skip expired certificates in Windows certificate store (``--cryptoapicert``)
+
+- ``--socks-proxy`` + ``--proto udp*`` will now allways use IPv4, even if
+ IPv6 is requested and available. Our SOCKS code does not handle IPv6+UDP,
+ and before that change it would just fail in non-obvious ways.
+
+- TCP listen() backlog queue is now set to 32 - this helps TCP servers that
+ receive lots of "invalid" connects by TCP port scanners
+
+- do no longer print OCC warnings ("option mismatch") about ``key-method``,
+ ``keydir``, ``tls-auth`` and ``cipher`` - these are either gone now, or
+ negotiated, and the warnings do not serve a useful purpose.
+
+- ``dhcp-option DNS`` and ``dhcp-option DNS6`` are now treated identically
+ (= both accept an IPv4 or IPv6 address for the nameserver)
+
+
+Maintainer-visible changes
+--------------------------
+- the man page is now in maintained in .rst format, so building the openvpn.8
+ manpage from a git checkout now requires python-docutils (if this is missing,
+ the manpage will not be built - which is not considered an error generally,
+ but for package builders or ``make distcheck`` it is). Release tarballs
+ contain the openvpn.8 file, so unless some .rst is changed, doc-utils are
+ not needed for building.
+
+- OCC support can no longer be disabled
+
+- AEAD support is now required in the crypto library
+
+- ``--disable-server`` has been removed from configure (so it is no longer
+ possible to build a client-/p2p-only OpenVPN binary) - the saving in code
+ size no longer outweighs the extra maintenance effort.
+
+- ``--enable-iproute2`` will disable netlink(3) support, so maybe remove
+ that from package building configs (see above)
+
+- support building with MSVC 2019
+
+- cmocka based unit tests are now only run if cmocka is installed externally
+ (2.4 used to ship a local git submodule which was painful to maintain)
+
+- ``--disable-crypto`` configure option has been removed. OpenVPN is now always
+ built with crypto support, which makes the code much easier to maintain.
+ This does not affect ``--cipher none`` to do a tunnel without encryption.
+
+- ``--disable-multi`` configure option has been removed
+
+
+
Overview of changes in 2.4
==========================
@@ -10,7 +487,7 @@ Seamless client IP/port floating
the new format. When a data packet arrives, the server identifies peer
by peer-id. If peer's ip/port has changed, server assumes that
client has floated, verifies HMAC and updates ip/port in internal structs.
- This allows the connection to be immediatly restored, instead of requiring
+ This allows the connection to be immediately restored, instead of requiring
a TLS handshake before the server accepts packets from the new client
ip/port.
@@ -207,7 +684,7 @@ User-visible Changes
of a field get _$N appended to it's field name, starting at N=1. For the
example above, that would result in e.g. X509_0_OU=one, X509_0_OU_1=two.
Note that this breaks setups that rely on the fact that OpenVPN would
- previously (incorrectly) only export the last occurence of a field.
+ previously (incorrectly) only export the last occurrence of a field.
- ``proto udp`` and ``proto tcp`` now use both IPv4 and IPv6. The new
options ``proto udp4`` and ``proto tcp4`` use IPv4 only.
@@ -321,190 +798,8 @@ Maintainer-visible changes
i386/i686 builds on RHEL5.
-Version 2.4.9
-=============
-This is primarily a maintenance release with minor bugfixes and improvements.
-
-New features
-------------
-- Allow unicode search string in --cryptoapicert option (Windows)
-
-User visible changes
---------------------
-- Skip expired certificates in Windows certificate store (Windows) (trac #966)
-
-- OpenSSL: Fix --crl-verify not loading multiple CRLs in one file (trac #623)
-
-- When using "--auth-user-pass file" with just a username and no password
- in the file, OpenVPN now queries the management interface (if active)
- for the credentials. Previously it would query the console for the
- password, and fail if no console available (normal case on Windows)
- (trac #757)
-
-- Swap the order of checks for validating interactive service user
- (Windows: check config location before querying domain controller for
- group membership, which can be slow)
-
-
-Bug fixes
----------
-- fix condition where a client's session could "float" to a new IP address
- that is not authorized ("fix illegal client float").
-
- This can be used to disrupt service to a freshly connected client (no
- session keys negotiated yet). It can not be used to inject or steal
- VPN traffic. CVE-2020-11810, trac #1272).
-
-- fix combination of async push (deferred auth) and NCP (trac #1259)
-
-- Fix OpenSSL 1.1.1 not using auto elliptic curve selection (trac #1228)
-
-- Fix OpenSSL error stack handling of tls_ctx_add_extra_certs
-
-- mbedTLS: Make sure TLS session survives move (trac #880)
-
-- Fix OpenSSL private key passphrase notices
-
-- Fix building with --enable-async-push in FreeBSD (trac #1256)
-
-- Fix broken fragmentation logic when using NCP (trac #1140)
-
-
-
-Version 2.4.8
-=============
-This is primarily a maintenance release with minor bugfixes and improvements.
-
-New features
-------------
-- Support compiling with OpenSSL 1.1 without deprecated APIs
-
-- handle PSS padding in cryptoapicert (necessary for TLS >= 1.2)
-
-
-User visible changes
---------------------
-- do not abort when hitting the combination of "--pull-filter" and
- "--mode server" (this got hit when starting OpenVPN servers using
- the windows GUI which installs a pull-filter to force ip-win32)
-
-- increase listen() backlog queue to 32 (improve response behaviour
- on openvpn servers using TCP that get portscanned)
-
-- fix and enhance documentation (INSTALL, man page, ...)
-
-
-Bug fixes
----------
-- the combination "IPv6 and proto UDP and SOCKS proxy" did not work - as
- a workaround, force IPv4 in this case until a full implementation for
- IPv6-UDP-SOCKS can be made.
-
-- fix IPv6 routes on tap interfaces on OpenSolaris/OpenIndiana
-
-- fix building with LibreSSL
-
-- do not set pkcs11-helper 'safe fork mode' (should fix PIN querying in
- systemd environments)
-
-- repair windows builds
-
-- repair Darwin builds (remove -no-cpp-precomp flag)
-
-
-
-Version 2.4.7
-=============
-This is primarily a maintenance release with minor bugfixes and improvements.
-
-New features
-------------
-- ifconfig-ipv6(-push): allow using hostnames (in place of IPv6 addresses)
-
-- new option: --ciphersuites to select TLS 1.3 cipher suites
- (--cipher selects TLS 1.2 and earlier ciphers)
-
-- enable dhcp on tap adapter using interactive service
- (previously this required a privileged netsh.exe call from OpenVPN)
-
-- clarify and expand management interface documentation
-
-- add Interactive Service developer documentation
-
-
-User visible changes
---------------------
-- add message explaining early TLS client hello failure (if TLS 1.0
- only clients try to connect to TLS 1.3 capable servers)
-
-- --show-tls will now display TLS 1.3 and TLS 1.2 ciphers in separate
- lists (if built with OpenSSL 1.1.1+)
-
-- don't print OCC warnings about 'key-method', 'keydir' and 'tls-auth'
- (unnecessary warnings, and will cause spurious warnings with tls-crypt-v2)
-
-- bump version of openvpn plugin argument structs to 5
-
-- plugin: Export base64 encode and decode functions
-
-- man: add security considerations to --compress section
-
-
-Bug fixes
----------
-- print port numbers (again) for incoming IPv4 connections received on
- a dual-stacked IPv6 socket. This got lost at some point during
- rewrite of the dual-stack code and proper printing of IPv4 addresses.
-
-- fallback to password authentication when auth-token fails
-
-- fix combination of --dev tap and --topology subnet across multiple
- platforms (BSDs, MacOS, and Solaris).
-
-- fix Windows CryptoAPI usage for TLS 1.2 signatures
-
-- fix option handling in combination with NCP negotiation and OCC
- (--opt-verify failure on reconnect if NCP modified options and server
- verified "original" vs. "modified" options)
-
-- mbedtls: print warning if random personalisation fails
-
-- fix subnet topology on NetBSD (2.4).
-
-
-
-Version 2.4.6
-=============
-This is primarily a maintenance release with minor bugfixes and improvements,
-and one security relevant fix for the Windows Interactive Service.
-
-User visible changes
---------------------
-- warn if the management interface is configured with a TCP port and
- no password is set (because it might be possible to interfere with
- OpenVPN operation by tricking other programs into connecting to the
- management interface and inject unwanted commands)
-
-Bug fixes
----------
-- CVE-2018-9336: fix potential double-free() in the Interactive Service
- (Windows) on malformed input.
-
-- avoid possible integer overflow in wakeup computation (trac #922)
-
-- improve handling of incoming packet bursts for control channel data
-
-- fix compilation with older OpenSSL versions that were broken in 2.4.5
-
-- Windows + interactive Service: delete the IPv6 route to the "connected"
- network on tun close
-
-
Version 2.4.5
=============
-This is primarily a maintenance release, with further improved OpenSSL 1.1
-integration, several minor bug fixes and other minor improvements.
-
New features
------------
@@ -514,99 +809,6 @@ New features
elliptic curve certificates. The default will be changed to the 'preferred'
profile in the future, which requires SHA2+, RSA-2048+ and any curve.
-- make CryptoAPI support (Windows) compatible with OpenSSL 1.1 builds
-
-- TLS v1.2 support for cryptoapicert (on Windows) -- RSA only
-
-- openvpnserv: Add support for multi-instances (to support multiple
- parallel OpenVPN installations, like EduVPN and regular OpenVPN)
-
-- Use P_DATA_V2 for server->client packets too (better packet alignment)
-
-- improve management interface documentation
-
-- rework registry key handling for OpenVPN service, notably making most
- registry values optional, falling back to reasonable defaults
-
-- accept IPv6 address for pushed "dhcp-option DNS ..."
- (make OpenVPN 2 option compatible with OpenVPN 3 iOS and Android clients)
-
-
-Bug fixes
----------
-- Fix --tls-version-min and --tls-version-max for OpenSSL 1.1+
-
-- Fix lots of compiler warnings (format string, type casts, ...)
-
-- Fix --redirect-gateway route installation on Windows systems that have
- multiple interfaces into the same network (e.g. Wifi and wired LAN).
-
-- Fix IPv6 interface route cleanup on Windows
-
-- reload HTTP proxy credentials when moving to the next connection profile
-
-- Fix build with LibreSSL (multiple times)
-
-- Remove non-useful warning on pushed tun-ipv6 option.
-
-- fix building with MSVC due to incompatible C constructs
-
-- autoconf: Fix engine checks for openssl 1.1
-
-- lz4: Rebase compat-lz4 against upstream v1.7.5
-
-- lz4: Fix broken builds when pkg-config is not present but system library is
-
-- Fix '--bind ipv6only'
-
-- Allow learning iroutes with network made up of all 0s
-
-
-Version 2.4.4
-=============
-This is primarily a maintenance release, with further improved OpenSSL 1.1
-integration, several minor bug fixes and other minor improvements.
-
-Bug fixes
----------
-- Fix issues when a pushed cipher via the Negotiable Crypto Parameters (NCP) is
- rejected by the remote side
-
-- Ignore ``--keysize`` when NCP have resulted in a changed cipher.
-
-- Configurations using ``--auth-nocache`` and the management interface to provide
- user credentials (like NetworkManager on Linux) on client side with servers
- implementing authentication tokens (for example, using ``--auth-gen-token``)
- will now behave correctly and not query the user for an, to them, unknown
- authentication token on renegotiations of the tunnel.
-
-- Fix bug causing invalid or corrupt SOCKS port number when changing the
- proxy via the management interface.
-
-- The man page should now have proper escaping of hyphens/minus characters
- and have seen some minor corrections.
-
-User-visible Changes
---------------------
-- Linux servers with systemd which uses the ``openvpn-server@.service`` unit
- file for server configurations will now utilize the automatic restart feature
- in systemd. If the OpenVPN server process dies unexpectedly, systemd will
- ensure the OpenVPN configuration will be restarted without any user interaction.
-
-Deprecated features
--------------------
-- ``--no-replay`` is deprecated and will be removed in OpenVPN 2.5.
-- ``--keysize`` is deprecated in OpenVPN 2.4 and will be removed in v2.6
-
-Security
---------
-- CVE-2017-12166: Fix bounds check for configurations using ``--key-method 1``.
- Before this fix, it could allow an attacker to send a malformed packet to
- trigger a stack overflow. This is considered to be a low risk issue, as
- ``--key-method 2`` has been the default since OpenVPN 2.0 (released on
- 2005-04-17). This option is already deprecated in v2.4 and will be
- completely removed in v2.5.
-
Version 2.4.3
=============
@@ -630,7 +832,7 @@ Security
- CVE-2017-7521: Fix post-authentication remote-triggerable memory leaks
A client could cause a server to leak a few bytes each time it connects to the
- server. That can eventuall cause the server to run out of memory, and thereby
+ server. That can eventually cause the server to run out of memory, and thereby
causing the server process to terminate. Discovered and reported to the
OpenVPN security team by Guido Vranken. (OpenSSL builds only.)
@@ -658,7 +860,7 @@ Security
are known.
- Fix null-pointer dereference when talking to a malicious http proxy
- that returns a malformed Proxy-Authenticate: headers for digest auth.
+ that returns a malformed ``Proxy-Authenticate:`` headers for digest auth.
- Fix overflow check for long ``--tls-cipher`` option
@@ -697,7 +899,7 @@ Bugfixes
- Fix TCP_NODELAY on OpenBSD
-- Remove erroneous limitation on max number of args for --plugin
+- Remove erroneous limitation on max number of args for ``--plugin``
- Fix NCP behaviour on TLS reconnect (Server would not send a proper
"cipher ..." message back to the client, leading to client and server
@@ -733,12 +935,10 @@ Version 2.4.1
- ``--remote-cert-ku`` now only requires the certificate to have at least the
bits set of one of the values in the supplied list, instead of requiring an
exact match to one of the values in the list.
-
- ``--remote-cert-tls`` now only requires that a keyUsage is present in the
certificate, and leaves the verification of the value up to the crypto
library, which has more information (i.e. the key exchange method in use)
to verify that the keyUsage is correct.
-
- ``--ns-cert-type`` is deprecated. Use ``--remote-cert-tls`` instead.
The nsCertType x509 extension is very old, and barely used.
``--remote-cert-tls`` uses the far more common keyUsage and extendedKeyUsage
diff --git a/INSTALL b/INSTALL
index a5936b3..9db5b64 100644
--- a/INSTALL
+++ b/INSTALL
@@ -71,12 +71,13 @@ REQUIRES:
(1) TUN and/or TAP driver to allow user-space programs to control
a virtual point-to-point IP or Ethernet device. See
TUN/TAP Driver Configuration section below for more info.
-
-OPTIONAL (but recommended):
- (1) OpenSSL library, necessary for encryption, version 0.9.8 or higher
+ (2) OpenSSL library, necessary for encryption, version 1.0.2 or higher
required, available from http://www.openssl.org/
- (2) mbed TLS library, an alternative for encryption, version 2.0 or higher
+ or
+ (3) mbed TLS library, an alternative for encryption, version 2.0 or higher
required, available from https://tls.mbed.org/
+
+OPTIONAL:
(3) LZO real-time compression library, required for link compression,
available from http://www.oberhumer.com/opensource/lzo/
OpenBSD users can use ports or packages to install lzo, but remember
@@ -145,7 +146,7 @@ make check (Run all tests below)
Test Crypto:
-./openvpn --genkey --secret key
+./openvpn --genkey secret key
./openvpn --test-crypto --secret key
Test SSL/TLS negotiations (runs for 2 minutes):
@@ -156,6 +157,20 @@ Test SSL/TLS negotiations (runs for 2 minutes):
For more thorough client-server tests you can configure your own, private test
environment. See tests/t_client.rc-sample for details.
+To do the C unit tests, you need to have the "cmocka" test framework
+installed on your system. More recent distributions already ship this
+as part of their packages/ports. If your system does not have it,
+you can install cmocka with these commands:
+
+ $ git clone https://git.cryptomilk.org/projects/cmocka.git
+ $ cd cmocka
+ $ mkdir build
+ $ cd build
+ $ cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug ..
+ $ make
+ $ sudo make install
+
+
*************************************************************************
OPTIONS for ./configure:
@@ -213,7 +228,6 @@ ENVIRONMENT for ./configure:
ROUTE full path to route utility
IPROUTE full path to ip utility
NETSTAT path to netstat utility
- MAN2HTML path to man2html utility
GIT path to git utility
SYSTEMD_ASK_PASSWORD
path to systemd-ask-password utility
@@ -221,6 +235,8 @@ ENVIRONMENT for ./configure:
Path of systemd unit directory [default=LIBDIR/systemd/system]
TMPFILES_DIR
Path of tmpfiles directory [default=LIBDIR/tmpfiles.d]
+ RST2MAN Path to rst2man utility
+ RST2HTML Path to rst2html utility
ENVIRONMENT variables adjusting parameters related to dependencies
diff --git a/Makefile.am b/Makefile.am
index f4ca50f..8092448 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,8 +5,8 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
-# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2010-2021 David Sommerseth <dazo@eurephia.org>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
@@ -23,9 +23,6 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
-# This option prevents autoreconf from overriding our COPYING and
-# INSTALL targets:
-AUTOMAKE_OPTIONS = foreign 1.9
ACLOCAL_AMFLAGS = -I m4
MAINTAINERCLEANFILES = \
@@ -46,14 +43,14 @@ EXTRA_DIST = \
contrib \
debug
-.PHONY: config-version.h
+.PHONY: config-version.h doxygen
if GIT_CHECKOUT
BUILT_SOURCES = \
config-version.h
endif
-SUBDIRS = build distro include src sample doc vendor tests
+SUBDIRS = build distro include src sample doc tests
dist_doc_DATA = \
README \
@@ -96,3 +93,6 @@ config-version.h:
else \
rm -f config-version.h.tmp; \
fi
+
+doxygen:
+ $(MAKE) -C doc/doxygen doxygen
diff --git a/Makefile.in b/Makefile.in
index 9dac135..7df8d07 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,8 +21,8 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
-# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2010-2021 David Sommerseth <dazo@eurephia.org>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
@@ -199,8 +199,8 @@ am__recursive_targets = \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
cscope distdir distdir-am dist dist-all distcheck
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
- $(LISP)config.h.in
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+ config.h.in
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
@@ -279,7 +279,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -293,6 +294,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -320,7 +322,6 @@ 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@
@@ -371,6 +372,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -434,6 +437,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -445,10 +449,6 @@ tmpfilesdir = @tmpfilesdir@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-
-# This option prevents autoreconf from overriding our COPYING and
-# INSTALL targets:
-AUTOMAKE_OPTIONS = foreign 1.9
ACLOCAL_AMFLAGS = -I m4
MAINTAINERCLEANFILES = \
config.log config.status \
@@ -471,7 +471,7 @@ EXTRA_DIST = \
@GIT_CHECKOUT_TRUE@BUILT_SOURCES = \
@GIT_CHECKOUT_TRUE@ config-version.h
-SUBDIRS = build distro include src sample doc vendor tests
+SUBDIRS = build distro include src sample doc tests
dist_doc_DATA = \
README \
README.IPv6 \
@@ -792,6 +792,10 @@ dist-xz: distdir
tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
$(am__post_remove_distdir)
+dist-zstd: distdir
+ tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+ $(am__post_remove_distdir)
+
dist-tarZ: distdir
@echo WARNING: "Support for distribution archives compressed with" \
"legacy program 'compress' is deprecated." >&2
@@ -834,6 +838,8 @@ distcheck: dist
eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
+ *.tar.zst*) \
+ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
@@ -1020,24 +1026,24 @@ uninstall-am: uninstall-dist_docDATA uninstall-rootDATA
am--refresh check check-am clean clean-cscope clean-generic \
clean-libtool cscope cscopelist-am ctags ctags-am dist \
dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
- dist-xz dist-zip distcheck distclean distclean-generic \
- distclean-hdr distclean-libtool distclean-tags distcleancheck \
- distdir distuninstallcheck dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dist_docDATA 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-rootDATA install-strip \
- installcheck installcheck-am installdirs installdirs-am \
- maintainer-clean maintainer-clean-generic mostlyclean \
- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags tags-am uninstall uninstall-am uninstall-dist_docDATA \
- uninstall-rootDATA
+ dist-xz dist-zip dist-zstd distcheck distclean \
+ distclean-generic distclean-hdr distclean-libtool \
+ distclean-tags distcleancheck distdir distuninstallcheck dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dist_docDATA 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-rootDATA install-strip installcheck installcheck-am \
+ installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-dist_docDATA uninstall-rootDATA
.PRECIOUS: Makefile
-.PHONY: config-version.h
+.PHONY: config-version.h doxygen
config-version.h:
@CONFIGURE_GIT_CHFILES="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-files --name-status -r --ignore-submodules --quiet -- || echo \"+\"`"; \
@@ -1053,6 +1059,9 @@ config-version.h:
rm -f config-version.h.tmp; \
fi
+doxygen:
+ $(MAKE) -C doc/doxygen doxygen
+
# 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/PORTS b/PORTS
index 1d8d5e0..d158e1f 100644
--- a/PORTS
+++ b/PORTS
@@ -1,5 +1,5 @@
OpenVPN
-Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
OpenVPN has been written to try to avoid features
that are not standardized well across different
diff --git a/TODO.IPv6 b/TODO.IPv6
index 24bf865..465eaa6 100644
--- a/TODO.IPv6
+++ b/TODO.IPv6
@@ -21,7 +21,7 @@ TODO for IPv6 payload support
4.) do "ifconfig tun0 inet6 unplumb" or "ifconfig tun0 destroy" for
Solaris, *BSD, ... at program termination time, to clean up leftovers
- (unless tunnel persistance is desired).
+ (unless tunnel persistence is desired).
For Solaris, only the "ipv6 tun0" is affected, for the *BSDs all tun0
stay around.
@@ -47,7 +47,7 @@ tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
4b.) verify this - on FreeBSD, tun0 is auto-destroyed if created by
opening /dev/tun (and lingers if created by "ifconfig tun0 create")
- -> use for persistant tunnels on not-linux?
+ -> use for persistent tunnels on not-linux?
* 2012-06-10 tun interface behaviour is documented in "man tun(4)"
@@ -201,7 +201,7 @@ TODO for IPv6 transport support
downstream.
- Still done by flags, seems clean enough.
- o implement comparison for mapped addesses: server in dual stack
+ o implement comparison for mapped addresses: server in dual stack
listening IPv6 must permit incoming streams from allowed IPv4 peer,
currently you need to pass eg: --remote ffff::1.2.3.4
- OpenVPN will compare all address of a remote
diff --git a/aclocal.m4 b/aclocal.m4
index 2cc1c34..ee51d3d 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.2 -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
-# Copyright (C) 2002-2018 Free Software Foundation, Inc.
+# Copyright (C) 2002-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -35,7 +35,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.16.1], [],
+m4_if([$1], [1.16.2], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.2])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -110,7 +110,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -141,7 +141,7 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -332,7 +332,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -371,7 +371,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
done
if test $am_rc -ne 0; then
AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
- for automatic dependency tracking. Try re-running configure with the
+ for automatic dependency tracking. If GNU make was not used, consider
+ re-running the configure script with MAKE="gmake" (or whatever is
+ necessary). You can also try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).])
fi
@@ -398,7 +400,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -595,7 +597,7 @@ for _am_header in $config_headers :; do
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -616,7 +618,7 @@ if test x"${install_sh+set}" != xset; then
fi
AC_SUBST([install_sh])])
-# Copyright (C) 2003-2018 Free Software Foundation, Inc.
+# Copyright (C) 2003-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -637,7 +639,7 @@ AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -680,7 +682,7 @@ AC_SUBST([am__quote])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -719,7 +721,7 @@ fi
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -748,7 +750,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -795,7 +797,7 @@ AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -814,7 +816,7 @@ AC_DEFUN([AM_RUN_LOG],
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -895,7 +897,7 @@ AC_CONFIG_COMMANDS_PRE(
rm -f conftest.file
])
-# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+# Copyright (C) 2009-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -955,7 +957,7 @@ AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -983,7 +985,7 @@ fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2018 Free Software Foundation, Inc.
+# Copyright (C) 2006-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1002,7 +1004,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2018 Free Software Foundation, Inc.
+# Copyright (C) 2004-2020 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/build/Makefile.am b/build/Makefile.am
index b011141..46973b4 100644
--- a/build/Makefile.am
+++ b/build/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
#
MAINTAINERCLEANFILES = \
diff --git a/build/Makefile.in b/build/Makefile.in
index e2beb32..a3fb154 100644
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
#
VPATH = @srcdir@
am__is_gnu_make = { \
@@ -208,7 +208,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -222,6 +223,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -249,7 +251,6 @@ 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@
@@ -300,6 +301,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -363,6 +366,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/build/msvc/Makefile.am b/build/msvc/Makefile.am
index 3e9c3fe..820e312 100644
--- a/build/msvc/Makefile.am
+++ b/build/msvc/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
diff --git a/build/msvc/Makefile.in b/build/msvc/Makefile.in
index 7a6c8bd..7aa8bd1 100644
--- a/build/msvc/Makefile.in
+++ b/build/msvc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
VPATH = @srcdir@
@@ -209,7 +209,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -223,6 +224,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -250,7 +252,6 @@ 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@
@@ -301,6 +302,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -364,6 +367,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/build/msvc/msvc-generate/Makefile.am b/build/msvc/msvc-generate/Makefile.am
index 4ae850f..3f189cf 100644
--- a/build/msvc/msvc-generate/Makefile.am
+++ b/build/msvc/msvc-generate/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
diff --git a/build/msvc/msvc-generate/Makefile.in b/build/msvc/msvc-generate/Makefile.in
index ac41d06..80e9bca 100644
--- a/build/msvc/msvc-generate/Makefile.in
+++ b/build/msvc/msvc-generate/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -152,7 +152,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -166,6 +167,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -193,7 +195,6 @@ 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@
@@ -244,6 +245,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -307,6 +310,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/build/msvc/msvc-generate/Makefile.mak b/build/msvc/msvc-generate/Makefile.mak
index 59fc9f0..6da859e 100644
--- a/build/msvc/msvc-generate/Makefile.mak
+++ b/build/msvc/msvc-generate/Makefile.mak
@@ -11,7 +11,10 @@ OUTPUT_PLUGIN=$(SOURCEBASE)/include/openvpn-plugin.h
INPUT_PLUGIN_CONFIG=version.m4.in
OUTPUT_PLUGIN_CONFIG=version.m4
-all: $(OUTPUT_MSVC_VER) $(OUTPUT_PLUGIN)
+INPUT_MAN=$(SOURCEBASE)/doc/openvpn.8.rst
+OUTPUT_MAN=$(SOURCEBASE)/doc/openvpn.8.html
+
+all: $(OUTPUT_MSVC_VER) $(OUTPUT_PLUGIN) $(OUTPUT_MAN)
$(OUTPUT_MSVC_VER): $(INPUT_MSVC_VER) $(CONFIG)
cscript //nologo msvc-generate.js --config="$(CONFIG)" --input="$(INPUT_MSVC_VER)" --output="$(OUTPUT_MSVC_VER)"
@@ -22,7 +25,11 @@ $(OUTPUT_PLUGIN_CONFIG): $(INPUT_PLUGIN_CONFIG)
$(OUTPUT_PLUGIN): $(INPUT_PLUGIN) $(OUTPUT_PLUGIN_CONFIG)
cscript //nologo msvc-generate.js --config="$(OUTPUT_PLUGIN_CONFIG)" --input="$(INPUT_PLUGIN)" --output="$(OUTPUT_PLUGIN)"
+$(OUTPUT_MAN): $(INPUT_MAN)
+ -FOR /F %i IN ('where rst2html.py') DO python %i "$(INPUT_MAN)" "$(OUTPUT_MAN)"
+
clean:
-del "$(OUTPUT_MSVC_VER)"
-del "$(OUTPUT_PLUGIN)"
-del "$(OUTPUT_PLUGIN_CONFIG)"
+ -del "$(OUTPUT_MAN)"
diff --git a/build/msvc/msvc-generate/msvc-generate.vcxproj b/build/msvc/msvc-generate/msvc-generate.vcxproj
index 8b7ec22..dda8b05 100644
--- a/build/msvc/msvc-generate/msvc-generate.vcxproj
+++ b/build/msvc/msvc-generate/msvc-generate.vcxproj
@@ -1,35 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.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|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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>{8598C2C8-34C4-47A1-99B0-7C295A890615}</ProjectGuid>
<RootNamespace>msvc-generate</RootNamespace>
<Keyword>MakeFileProj</Keyword>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <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="..\..\..\src\compat\PropertySheet.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="..\..\..\src\compat\PropertySheet.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\..\src\compat\PropertySheet.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="..\..\..\src\compat\PropertySheet.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="..\..\..\src\compat\PropertySheet.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\..\src\compat\PropertySheet.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
@@ -37,25 +90,61 @@
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
+ <NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
+ <NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
<NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
+ <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
+ <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
<NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
+ <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
+ <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
<NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">config-msvc-version.h</NMakeOutput>
+ <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">config-msvc-version.h</NMakeOutput>
+ <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">config-msvc-version.h</NMakeOutput>
<NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
<NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ <NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ <NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
<NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+ <NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+ <NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
<NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+ <NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+ <NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
+ <NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
+ <NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">nmake -f Makefile.mak all</NMakeBuildCommandLine>
<NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
+ <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
+ <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">nmake -f Makefile.mak clean all</NMakeReBuildCommandLine>
<NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
+ <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
+ <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">nmake -f Makefile.mak clean</NMakeCleanCommandLine>
<NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">config-msvc-version.h</NMakeOutput>
+ <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|x64'">config-msvc-version.h</NMakeOutput>
+ <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">config-msvc-version.h</NMakeOutput>
<NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
<NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ <NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ <NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
<NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+ <NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+ <NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
<NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+ <NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+ <NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
</PropertyGroup>
<ItemDefinitionGroup>
</ItemDefinitionGroup>
diff --git a/compile b/compile
index 99e5052..23fcba0 100755
--- a/compile
+++ b/compile
@@ -3,7 +3,7 @@
scriptversion=2018-03-07.03; # UTC
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
@@ -53,7 +53,7 @@ func_file_conv ()
MINGW*)
file_conv=mingw
;;
- CYGWIN*)
+ CYGWIN* | MSYS*)
file_conv=cygwin
;;
*)
@@ -67,7 +67,7 @@ func_file_conv ()
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
- cygwin/*)
+ cygwin/* | msys/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
diff --git a/config-msvc.h b/config-msvc.h
index 0bb153d..f199bb2 100644
--- a/config-msvc.h
+++ b/config-msvc.h
@@ -4,7 +4,6 @@
#define ENABLE_DEF_AUTH 1
#define ENABLE_PF 1
-#define ENABLE_CRYPTO 1
#define ENABLE_CRYPTO_OPENSSL 1
#define ENABLE_DEBUG 1
#define ENABLE_EUREPHIA 1
@@ -76,6 +75,44 @@
#define HAVE_POLL 1
#define HAVE_OPENSSL_ENGINE 1
+/* hardcode usage of OpenSSL 1.1.x */
+#define HAVE_EVP_MD_CTX_RESET 1
+#define HAVE_EVP_MD_CTX_FREE 1
+#define HAVE_EVP_MD_CTX_NEW 1
+#define HAVE_HMAC_CTX_RESET 1
+#define HAVE_HMAC_CTX_FREE 1
+#define HAVE_HMAC_CTX_NEW 1
+#define HAVE_SSL_CTX_GET_DEFAULT_PASSWD_CB_USERDATA 1
+#define HAVE_SSL_CTX_GET_DEFAULT_PASSWD_CB 1
+#define HAVE_X509_GET0_PUBKEY 1
+#define HAVE_X509_STORE_GET0_OBJECTS 1
+#define HAVE_X509_OBJECT_FREE 1
+#define HAVE_X509_OBJECT_GET_TYPE 1
+#define HAVE_EVP_PKEY_GET0_RSA 1
+#define HAVE_EVP_PKEY_GET0_EC_KEY 1
+#define HAVE_EVP_PKEY_ID 1
+#define HAVE_EVP_PKEY_GET0_DSA 1
+#define HAVE_RSA_SET_FLAGS 1
+#define HAVE_RSA_GET0_KEY 1
+#define HAVE_RSA_SET0_KEY 1
+#define HAVE_RSA_BITS 1
+#define HAVE_DSA_GET0_PQG 1
+#define HAVE_DSA_BITS 1
+#define HAVE_RSA_METH_NEW 1
+#define HAVE_RSA_METH_FREE 1
+#define HAVE_RSA_METH_SET_PUB_ENC 1
+#define HAVE_RSA_METH_SET_PUB_DEC 1
+#define HAVE_RSA_METH_SET_PRIV_ENC 1
+#define HAVE_RSA_METH_SET_PRIV_DEC 1
+#define HAVE_RSA_METH_SET_INIT 1
+#define HAVE_RSA_METH_SET_SIGN 1
+#define HAVE_RSA_METH_SET_FINISH 1
+#define HAVE_RSA_METH_SET0_APP_DATA 1
+#define HAVE_RSA_METH_GET0_APP_DATA 1
+#define HAVE_EC_GROUP_ORDER_BITS 1
+#define OPENSSL_NO_EC 1
+#define HAVE_EVP_CIPHER_CTX_RESET 1
+#define HAVE_DIINSTALLDEVICE 1
#define PATH_SEPARATOR '\\'
#define PATH_SEPARATOR_STR "\\"
diff --git a/config.h.in b/config.h.in
index b8a48d5..dc4c2d5 100644
--- a/config.h.in
+++ b/config.h.in
@@ -21,15 +21,9 @@
/* Enable async push */
#undef ENABLE_ASYNC_PUSH
-/* Enable client capability only */
-#undef ENABLE_CLIENT_ONLY
-
/* Enable compression stub capability */
#undef ENABLE_COMP_STUB
-/* Enable crypto library */
-#undef ENABLE_CRYPTO
-
/* Use mbed TLS library */
#undef ENABLE_CRYPTO_MBEDTLS
@@ -81,6 +75,9 @@
/* SELinux support */
#undef ENABLE_SELINUX
+/* enable sitnl support */
+#undef ENABLE_SITNL
+
/* Enable smaller executable size */
#undef ENABLE_SMALL
@@ -99,9 +96,6 @@
/* Define to 1 if you have the `access' function. */
#undef HAVE_ACCESS
-/* Use crypto library */
-#undef HAVE_AEAD_CIPHER_MODES
-
/* Compiler supports anonymous unions */
#undef HAVE_ANONYMOUS_UNION_SUPPORT
@@ -220,9 +214,6 @@
/* Define to 1 if you have the <err.h> header file. */
#undef HAVE_ERR_H
-/* Define to 1 if you have the `EVP_aes_256_gcm' function. */
-#undef HAVE_EVP_AES_256_GCM
-
/* Define to 1 if you have the `EVP_CIPHER_CTX_reset' function. */
#undef HAVE_EVP_CIPHER_CTX_RESET
@@ -247,12 +238,12 @@
/* Define to 1 if you have the `EVP_PKEY_get0_RSA' function. */
#undef HAVE_EVP_PKEY_GET0_RSA
-/* Define to 1 if you have the `EVP_PKEY_id' function. */
-#undef HAVE_EVP_PKEY_ID
-
/* Define to 1 if you have the `execve' function. */
#undef HAVE_EXECVE
+/* Crypto library supports keying material exporter */
+#undef HAVE_EXPORT_KEYING_MATERIAL
+
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
@@ -430,9 +421,15 @@
/* OpenSSL engine support available */
#undef HAVE_OPENSSL_ENGINE
+/* Define to 1 if you have the `OpenSSL_version' function. */
+#undef HAVE_OPENSSL_VERSION
+
/* Define to 1 if you have the `poll' function. */
#undef HAVE_POLL
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
/* Define to 1 if you have the `putenv' function. */
#undef HAVE_PUTENV
@@ -551,6 +548,9 @@
/* Define to 1 if you have the `SSL_CTX_new' function. */
#undef HAVE_SSL_CTX_NEW
+/* Define to 1 if you have the `SSL_CTX_set1_groups' function. */
+#undef HAVE_SSL_CTX_SET1_GROUPS
+
/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */
#undef HAVE_SSL_CTX_SET_SECURITY_LEVEL
@@ -581,6 +581,9 @@
/* Define to 1 if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
+/* Define to 1 if you have the `strsep' function. */
+#undef HAVE_STRSEP
+
/* Define to 1 if you have the `syslog' function. */
#undef HAVE_SYSLOG
@@ -611,9 +614,6 @@
/* Define to 1 if you have the <sys/mman.h> header file. */
#undef HAVE_SYS_MMAN_H
-/* Define to 1 if you have the <sys/poll.h> header file. */
-#undef HAVE_SYS_POLL_H
-
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
diff --git a/configure b/configure
index eee8cbc..2fa8d28 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for OpenVPN 2.4.9.
+# Generated by GNU Autoconf 2.69 for OpenVPN 2.5.4.
#
# Report bugs to <openvpn-users@lists.sourceforge.net>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='OpenVPN'
PACKAGE_TARNAME='openvpn'
-PACKAGE_VERSION='2.4.9'
-PACKAGE_STRING='OpenVPN 2.4.9'
+PACKAGE_VERSION='2.5.4'
+PACKAGE_STRING='OpenVPN 2.5.4'
PACKAGE_BUGREPORT='openvpn-users@lists.sourceforge.net'
PACKAGE_URL=''
@@ -636,19 +636,21 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
-CMOCKA_INITIALIZED_FALSE
-CMOCKA_INITIALIZED_TRUE
-CMAKE
TEST_CFLAGS
TEST_LDFLAGS
+ENABLE_UNITTESTS
+ENABLE_UNITTESTS_FALSE
+ENABLE_UNITTESTS_TRUE
+CMOCKA_LIBS
+CMOCKA_CFLAGS
tmpfilesdir
systemdunitdir
sampledir
plugindir
+OPENSSL_ENGINE_FALSE
+OPENSSL_ENGINE_TRUE
HAVE_LD_WRAP_SUPPORT_FALSE
HAVE_LD_WRAP_SUPPORT_TRUE
-ENABLE_CRYPTO_FALSE
-ENABLE_CRYPTO_TRUE
ENABLE_PLUGIN_DOWN_ROOT_FALSE
ENABLE_PLUGIN_DOWN_ROOT_TRUE
ENABLE_PLUGIN_AUTH_PAM_FALSE
@@ -677,6 +679,8 @@ OPTIONAL_INOTIFY_LIBS
OPTIONAL_INOTIFY_CFLAGS
P11KIT_LIBS
P11KIT_CFLAGS
+HAVE_SITNL_FALSE
+HAVE_SITNL_TRUE
libsystemd_LIBS
libsystemd_CFLAGS
ENABLE_SYSTEMD_FALSE
@@ -717,11 +721,14 @@ LIBTOOL
OBJDUMP
DLLTOOL
AS
+HAVE_PYDOCUTILS_FALSE
+HAVE_PYDOCUTILS_TRUE
+RST2HTML
+RST2MAN
TMPFILES_DIR
SYSTEMD_UNIT_DIR
SYSTEMD_ASK_PASSWORD
GIT
-MAN2HTML
NETSTAT
IPROUTE
ROUTE
@@ -731,6 +738,8 @@ LN_S
PKG_CONFIG_LIBDIR
PKG_CONFIG_PATH
PKG_CONFIG
+TARGET_LINUX_FALSE
+TARGET_LINUX_TRUE
PLUGINDIR
EGREP
GREP
@@ -808,6 +817,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -836,10 +846,8 @@ enable_dependency_tracking
enable_lzo
enable_lz4
enable_comp_stub
-enable_crypto
enable_ofb_cfb
enable_x509_alt_username
-enable_server
enable_plugins
enable_management
enable_pkcs11
@@ -872,6 +880,7 @@ with_aix_soname
with_gnu_ld
with_sysroot
enable_libtool_lock
+enable_unit_tests
'
ac_precious_vars='build_alias
host_alias
@@ -890,11 +899,12 @@ IFCONFIG
ROUTE
IPROUTE
NETSTAT
-MAN2HTML
GIT
SYSTEMD_ASK_PASSWORD
SYSTEMD_UNIT_DIR
TMPFILES_DIR
+RST2MAN
+RST2HTML
LT_SYS_LIBRARY_PATH
TAP_CFLAGS
LIBPAM_CFLAGS
@@ -914,7 +924,9 @@ libsystemd_LIBS
P11KIT_CFLAGS
P11KIT_LIBS
OPTIONAL_INOTIFY_CFLAGS
-OPTIONAL_INOTIFY_LIBS'
+OPTIONAL_INOTIFY_LIBS
+CMOCKA_CFLAGS
+CMOCKA_LIBS'
# Initialize some variables set by options.
@@ -953,6 +965,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1205,6 +1218,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1342,7 +1364,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1455,7 +1477,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures OpenVPN 2.4.9 to adapt to many kinds of systems.
+\`configure' configures OpenVPN 2.5.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1495,6 +1517,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1525,7 +1548,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of OpenVPN 2.4.9:";;
+ short | recursive ) echo "Configuration of OpenVPN 2.5.4:";;
esac
cat <<\_ACEOF
@@ -1542,14 +1565,11 @@ Optional Features:
--disable-lzo disable LZO compression support [default=yes]
--disable-lz4 Disable LZ4 compression support
--enable-comp-stub Don't compile compression support but still allow limited interoperability with compression-enabled peers
- --disable-crypto disable crypto support [default=yes]
--disable-ofb-cfb disable support for OFB and CFB cipher modes
[default=yes]
--enable-x509-alt-username
enable the --x509-username-field feature
[default=no]
- --disable-server disable server support only (but retain client
- support) [default=yes]
--disable-plugins disable plug-in support [default=yes]
--disable-management disable management server support [default=yes]
--enable-pkcs11 enable pkcs11 support [default=no]
@@ -1581,7 +1601,7 @@ Optional Features:
--enable-strict-options enable strict options check between peers (debugging
option) [default=no]
--enable-selinux enable SELinux support [default=no]
- --enable-systemd enable systemd suppport [default=no]
+ --enable-systemd enable systemd support [default=no]
--enable-async-push enable async-push support for plugins providing
deferred authentication [default=no]
--enable-shared[=PKGS] build shared libraries [default=yes]
@@ -1589,6 +1609,7 @@ Optional Features:
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-unit-tests Disables building and running the unit tests suite
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1628,7 +1649,6 @@ Some influential environment variables:
ROUTE full path to route utility
IPROUTE full path to ip utility
NETSTAT path to netstat utility
- MAN2HTML path to man2html utility
GIT path to git utility
SYSTEMD_ASK_PASSWORD
path to systemd-ask-password utility
@@ -1636,6 +1656,8 @@ Some influential environment variables:
Path of systemd unit directory [default=LIBDIR/systemd/system]
TMPFILES_DIR
Path of tmpfiles directory [default=LIBDIR/tmpfiles.d]
+ RST2MAN path to rst2man utility
+ RST2HTML path to rst2html utility
LT_SYS_LIBRARY_PATH
User-defined run-time library search path.
TAP_CFLAGS C compiler flags for tap
@@ -1669,6 +1691,9 @@ Some influential environment variables:
C compiler flags for OPTIONAL_INOTIFY, overriding pkg-config
OPTIONAL_INOTIFY_LIBS
linker flags for OPTIONAL_INOTIFY, overriding pkg-config
+ CMOCKA_CFLAGS
+ C compiler flags for CMOCKA, overriding pkg-config
+ CMOCKA_LIBS linker flags for CMOCKA, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1736,7 +1761,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-OpenVPN configure 2.4.9
+OpenVPN configure 2.5.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2575,7 +2600,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by OpenVPN $as_me 2.4.9, which was
+It was created by OpenVPN $as_me 2.5.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2939,22 +2964,22 @@ if test -z "${htmldir}"; then
fi
-$as_echo "#define OPENVPN_VERSION_RESOURCE 2,4,9,0" >>confdefs.h
+$as_echo "#define OPENVPN_VERSION_RESOURCE 2,5,4,0" >>confdefs.h
OPENVPN_VERSION_MAJOR=2
-OPENVPN_VERSION_MINOR=4
+OPENVPN_VERSION_MINOR=5
-OPENVPN_VERSION_PATCH=.9
+OPENVPN_VERSION_PATCH=.4
$as_echo "#define OPENVPN_VERSION_MAJOR 2" >>confdefs.h
-$as_echo "#define OPENVPN_VERSION_MINOR 4" >>confdefs.h
+$as_echo "#define OPENVPN_VERSION_MINOR 5" >>confdefs.h
-$as_echo "#define OPENVPN_VERSION_PATCH \".9\"" >>confdefs.h
+$as_echo "#define OPENVPN_VERSION_PATCH \".4\"" >>confdefs.h
ac_aux_dir=
@@ -2992,6 +3017,8 @@ ac_config_headers="$ac_config_headers config.h include/openvpn-plugin.h"
+# This foreign option prevents autoreconf from overriding our COPYING and
+# INSTALL targets:
am__api_version='1.16'
# Find a good install program. We prefer a C program (faster),
@@ -3478,7 +3505,7 @@ fi
# Define the identity of the package.
PACKAGE='openvpn'
- VERSION='2.4.9'
+ VERSION='2.5.4'
cat >>confdefs.h <<_ACEOF
@@ -5167,15 +5194,6 @@ else
fi
-# Check whether --enable-crypto was given.
-if test "${enable_crypto+set}" = set; then :
- enableval=$enable_crypto;
-else
- enable_crypto="yes"
-
-fi
-
-
# Check whether --enable-ofb-cfb was given.
if test "${enable_ofb_cfb+set}" = set; then :
enableval=$enable_ofb_cfb;
@@ -5194,15 +5212,6 @@ else
fi
-# Check whether --enable-server was given.
-if test "${enable_server+set}" = set; then :
- enableval=$enable_server;
-else
- enable_server="yes"
-
-fi
-
-
# Check whether --enable-plugins was given.
if test "${enable_plugins+set}" = set; then :
enableval=$enable_plugins;
@@ -5457,16 +5466,33 @@ cat >>confdefs.h <<_ACEOF
#define TARGET_ALIAS "${host}"
_ACEOF
+ if false; then
+ TARGET_LINUX_TRUE=
+ TARGET_LINUX_FALSE='#'
+else
+ TARGET_LINUX_TRUE='#'
+ TARGET_LINUX_FALSE=
+fi
+
case "$host" in
*-*-linux*)
$as_echo "#define TARGET_LINUX 1" >>confdefs.h
+ if true; then
+ TARGET_LINUX_TRUE=
+ TARGET_LINUX_FALSE='#'
+else
+ TARGET_LINUX_TRUE='#'
+ TARGET_LINUX_FALSE=
+fi
+
cat >>confdefs.h <<_ACEOF
#define TARGET_PREFIX "L"
_ACEOF
+ have_sitnl="yes"
;;
*-*-solaris*)
@@ -5478,6 +5504,7 @@ cat >>confdefs.h <<_ACEOF
_ACEOF
CPPFLAGS="$CPPFLAGS -D_XPG4_2"
+ test -x /bin/bash && SHELL="/bin/bash"
;;
*-*-openbsd*)
@@ -5945,7 +5972,6 @@ fi
-
for ac_prog in ifconfig
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
@@ -6174,17 +6200,17 @@ fi
done
test -n "$NETSTAT" || NETSTAT="netstat"
# tests
-for ac_prog in man2html
+for ac_prog in git
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_MAN2HTML+:} false; then :
+if ${ac_cv_prog_GIT+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test -n "$MAN2HTML"; then
- ac_cv_prog_MAN2HTML="$MAN2HTML" # Let the user override the test.
+ if test -n "$GIT"; then
+ ac_cv_prog_GIT="$GIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
@@ -6193,7 +6219,7 @@ do
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_MAN2HTML="$ac_prog"
+ ac_cv_prog_GIT="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
@@ -6203,30 +6229,56 @@ IFS=$as_save_IFS
fi
fi
-MAN2HTML=$ac_cv_prog_MAN2HTML
-if test -n "$MAN2HTML"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAN2HTML" >&5
-$as_echo "$MAN2HTML" >&6; }
+GIT=$ac_cv_prog_GIT
+if test -n "$GIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GIT" >&5
+$as_echo "$GIT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
- test -n "$MAN2HTML" && break
+ test -n "$GIT" && break
done
+ # optional
-for ac_prog in git
+cat >>confdefs.h <<_ACEOF
+#define IFCONFIG_PATH "$IFCONFIG"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define IPROUTE_PATH "$IPROUTE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define ROUTE_PATH "$ROUTE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SYSTEMD_ASK_PASSWORD_PATH "$SYSTEMD_ASK_PASSWORD"
+_ACEOF
+
+
+#
+# man page generation - based on python-docutils
+#
+
+
+for ac_prog in rst2man rst2man.py
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_GIT+:} false; then :
+if ${ac_cv_prog_RST2MAN+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test -n "$GIT"; then
- ac_cv_prog_GIT="$GIT" # Let the user override the test.
+ if test -n "$RST2MAN"; then
+ ac_cv_prog_RST2MAN="$RST2MAN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
@@ -6235,7 +6287,7 @@ do
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_GIT="$ac_prog"
+ ac_cv_prog_RST2MAN="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
@@ -6245,38 +6297,68 @@ IFS=$as_save_IFS
fi
fi
-GIT=$ac_cv_prog_GIT
-if test -n "$GIT"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GIT" >&5
-$as_echo "$GIT" >&6; }
+RST2MAN=$ac_cv_prog_RST2MAN
+if test -n "$RST2MAN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RST2MAN" >&5
+$as_echo "$RST2MAN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
- test -n "$GIT" && break
+ test -n "$RST2MAN" && break
done
- # optional
-cat >>confdefs.h <<_ACEOF
-#define IFCONFIG_PATH "$IFCONFIG"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define IPROUTE_PATH "$IPROUTE"
-_ACEOF
+for ac_prog in rst2html rst2html.py
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RST2HTML+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RST2HTML"; then
+ ac_cv_prog_RST2HTML="$RST2HTML" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RST2HTML="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+fi
+fi
+RST2HTML=$ac_cv_prog_RST2HTML
+if test -n "$RST2HTML"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RST2HTML" >&5
+$as_echo "$RST2HTML" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
-cat >>confdefs.h <<_ACEOF
-#define ROUTE_PATH "$ROUTE"
-_ACEOF
+ test -n "$RST2HTML" && break
+done
-cat >>confdefs.h <<_ACEOF
-#define SYSTEMD_ASK_PASSWORD_PATH "$SYSTEMD_ASK_PASSWORD"
-_ACEOF
+ if test "${RST2MAN}" -a "${RST2HTML}"; then
+ HAVE_PYDOCUTILS_TRUE=
+ HAVE_PYDOCUTILS_FALSE='#'
+else
+ HAVE_PYDOCUTILS_TRUE='#'
+ HAVE_PYDOCUTILS_FALSE=
+fi
# Set -std=c99 unless user already specified a -std=
@@ -15000,7 +15082,7 @@ for ac_header in \
unistd.h signal.h libgen.h stropts.h \
syslog.h pwd.h grp.h \
sys/sockio.h sys/uio.h linux/sockios.h \
- linux/types.h sys/poll.h sys/epoll.h err.h \
+ linux/types.h poll.h sys/epoll.h err.h \
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -15600,7 +15682,7 @@ for ac_func in \
ctime memset vsnprintf strdup \
setsid chdir putenv getpeername unlink \
chsize ftruncate execve getpeereid umask basename dirname access \
- epoll_create \
+ epoll_create strsep \
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -16469,7 +16551,7 @@ $as_echo "yes" >&6; }
have_pkcs11_helper="yes"
fi
-if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
+if test "${with_crypto_library}" = "openssl"; then
@@ -16484,12 +16566,12 @@ if test -n "$OPENSSL_CFLAGS"; then
pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto >= 0.9.8, libssl >= 0.9.8\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libcrypto >= 0.9.8, libssl >= 0.9.8") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.2\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libcrypto >= 0.9.8, libssl >= 0.9.8" 2>/dev/null`
+ pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 1.0.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -16501,12 +16583,12 @@ if test -n "$OPENSSL_LIBS"; then
pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcrypto >= 0.9.8, libssl >= 0.9.8\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libcrypto >= 0.9.8, libssl >= 0.9.8") 2>&5
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.2\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "libcrypto >= 0.9.8, libssl >= 0.9.8" 2>/dev/null`
+ pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 1.0.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -16527,19 +16609,19 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
- OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcrypto >= 0.9.8, libssl >= 0.9.8" 2>&1`
+ OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl >= 1.0.2" 2>&1`
else
- OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcrypto >= 0.9.8, libssl >= 0.9.8" 2>&1`
+ OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl >= 1.0.2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$OPENSSL_PKG_ERRORS" >&5
- have_openssl="no" # Provide if-not-found to prevent erroring out
+ # If this fails, we will do another test next
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
- have_openssl="no" # Provide if-not-found to prevent erroring out
+ # If this fails, we will do another test next
else
OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS
@@ -16548,7 +16630,6 @@ else
$as_echo "yes" >&6; }
have_openssl="yes"
fi
-
OPENSSL_LIBS=${OPENSSL_LIBS:--lssl -lcrypto}
fi
@@ -16557,6 +16638,40 @@ fi
CFLAGS="${CFLAGS} ${OPENSSL_CFLAGS}"
LIBS="${LIBS} ${OPENSSL_LIBS}"
+ # If pkgconfig check failed or OPENSSL_CFLAGS/OPENSSL_LIBS env vars
+ # are used, check the version directly in the OpenSSL include file
+ if test "${have_openssl}" != "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking additionally if OpenSSL is available and version >= 1.0.2" >&5
+$as_echo_n "checking additionally if OpenSSL is available and version >= 1.0.2... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <openssl/opensslv.h>
+
+int
+main ()
+{
+
+/* Version encoding: MNNFFPPS - see opensslv.h for details */
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#error OpenSSL too old
+#endif
+
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+else
+ as_fn_error $? "OpenSSL version too old" "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+
for ac_func in SSL_CTX_new EVP_CIPHER_CTX_set_key_length
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -16610,22 +16725,19 @@ $as_echo "#define HAVE_OPENSSL_ENGINE 1" >>confdefs.h
fi
- have_crypto_aead_modes="yes"
- for ac_func in EVP_aes_256_gcm
-do :
- ac_fn_c_check_func "$LINENO" "EVP_aes_256_gcm" "ac_cv_func_EVP_aes_256_gcm"
+ ac_fn_c_check_func "$LINENO" "EVP_aes_256_gcm" "ac_cv_func_EVP_aes_256_gcm"
if test "x$ac_cv_func_EVP_aes_256_gcm" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_EVP_AES_256_GCM 1
-_ACEOF
else
- have_crypto_aead_modes="no"; break
+ as_fn_error $? "OpenSSL check for AES-256-GCM support failed" "$LINENO" 5
fi
-done
+ # All supported OpenSSL version (>= 1.0.2)
+ # have this feature
+ have_export_keying_material="yes"
+
for ac_func in \
HMAC_CTX_new \
HMAC_CTX_free \
@@ -16634,8 +16746,10 @@ done
EVP_MD_CTX_free \
EVP_MD_CTX_reset \
EVP_CIPHER_CTX_reset \
+ OpenSSL_version \
SSL_CTX_get_default_passwd_cb \
SSL_CTX_get_default_passwd_cb_userdata \
+ SSL_CTX_set1_groups \
SSL_CTX_set_security_level \
X509_get0_notBefore \
X509_get0_notAfter \
@@ -16643,7 +16757,6 @@ done
X509_STORE_get0_objects \
X509_OBJECT_free \
X509_OBJECT_get_type \
- EVP_PKEY_id \
EVP_PKEY_get0_RSA \
EVP_PKEY_get0_DSA \
EVP_PKEY_get0_EC_KEY \
@@ -16682,13 +16795,12 @@ done
CFLAGS="${saved_CFLAGS}"
LIBS="${saved_LIBS}"
- have_crypto="yes"
$as_echo "#define ENABLE_CRYPTO_OPENSSL 1" >>confdefs.h
CRYPTO_CFLAGS="${OPENSSL_CFLAGS}"
CRYPTO_LIBS="${OPENSSL_LIBS}"
-elif test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "mbedtls"; then
+elif test "${with_crypto_library}" = "mbedtls"; then
@@ -16775,49 +16887,6 @@ else
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- mbedtls_with_pkcs11="no"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-#include <mbedtls/config.h>
-
-int
-main ()
-{
-
-#ifndef MBEDTLS_PKCS11_C
-#error pkcs11 wrapper missing
-#endif
-
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- mbedtls_with_pkcs11="yes"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking mbedtls pkcs11 support" >&5
-$as_echo_n "checking mbedtls pkcs11 support... " >&6; }
- if test "${enable_pkcs11}" = "yes"; then
- if test "${mbedtls_with_pkcs11}" = "yes"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
-$as_echo "ok" >&6; }
- else
- as_fn_error $? "mbedtls has no pkcs11 wrapper compiled in" "$LINENO" 5
- fi
- else
- if test "${mbedtls_with_pkcs11}" != "yes"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
-$as_echo "ok" >&6; }
- else
- as_fn_error $? "mbed TLS compiled with PKCS11, while OpenVPN is not" "$LINENO" 5
- fi
- fi
-
- have_crypto_aead_modes="yes"
for ac_func in \
mbedtls_cipher_write_tag \
mbedtls_cipher_check_tag \
@@ -16831,21 +16900,30 @@ if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
_ACEOF
else
- have_crypto_aead_modes="no"; break
+ as_fn_error $? "mbed TLS check for AEAD support failed" "$LINENO" 5
fi
done
+ have_export_keying_material="yes"
+ ac_fn_c_check_func "$LINENO" "mbedtls_ssl_conf_export_keys_ext_cb" "ac_cv_func_mbedtls_ssl_conf_export_keys_ext_cb"
+if test "x$ac_cv_func_mbedtls_ssl_conf_export_keys_ext_cb" = xyes; then :
+
+else
+ have_export_keying_material="no"
+
+fi
+
+
CFLAGS="${saved_CFLAGS}"
LIBS="${saved_LIBS}"
- have_crypto="yes"
$as_echo "#define ENABLE_CRYPTO_MBEDTLS 1" >>confdefs.h
CRYPTO_CFLAGS="${MBEDTLS_CFLAGS}"
CRYPTO_LIBS="${MBEDTLS_LIBS}"
-elif test "${enable_crypto}" = "yes"; then
+else
as_fn_error $? "Invalid crypto library: ${with_crypto_library}" "$LINENO" 5
fi
@@ -17638,9 +17716,6 @@ fi
test "${ac_cv_header_sys_uio_h}" = "yes" &&
$as_echo "#define HAVE_IOVEC 1" >>confdefs.h
-test "${enable_server}" = "no" &&
-$as_echo "#define ENABLE_CLIENT_ONLY 1" >>confdefs.h
-
test "${enable_management}" = "yes" &&
$as_echo "#define ENABLE_MANAGEMENT 1" >>confdefs.h
@@ -17669,20 +17744,16 @@ test "${enable_strict_options}" = "yes" &&
$as_echo "#define ENABLE_STRICT_OPTIONS_CHECK 1" >>confdefs.h
-if test "${enable_crypto}" = "yes"; then
- test "${have_crypto}" != "yes" && as_fn_error $? "${with_crypto_library} crypto is required but missing" "$LINENO" 5
- test "${enable_crypto_ofb_cfb}" = "yes" &&
+test "${enable_crypto_ofb_cfb}" = "yes" &&
$as_echo "#define ENABLE_OFB_CFB_MODE 1" >>confdefs.h
- test "${have_crypto_aead_modes}" = "yes" &&
-$as_echo "#define HAVE_AEAD_CIPHER_MODES 1" >>confdefs.h
+if test "${have_export_keying_material}" = "yes"; then
- OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
- OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
-
-$as_echo "#define ENABLE_CRYPTO 1" >>confdefs.h
+$as_echo "#define HAVE_EXPORT_KEYING_MATERIAL 1" >>confdefs.h
fi
+OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
+OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
if test "${enable_plugins}" = "yes"; then
OPTIONAL_DL_LIBS="${DL_LIBS}"
@@ -17694,16 +17765,37 @@ else
enable_plugin_down_root="no"
fi
+ if false; then
+ HAVE_SITNL_TRUE=
+ HAVE_SITNL_FALSE='#'
+else
+ HAVE_SITNL_TRUE='#'
+ HAVE_SITNL_FALSE=
+fi
+
+
if test "${enable_iproute2}" = "yes"; then
test -z "${IPROUTE}" && as_fn_error $? "ip utility is required but missing" "$LINENO" 5
$as_echo "#define ENABLE_IPROUTE 1" >>confdefs.h
+else if test "${have_sitnl}" = "yes"; then
+
+$as_echo "#define ENABLE_SITNL 1" >>confdefs.h
+
+ if true; then
+ HAVE_SITNL_TRUE=
+ HAVE_SITNL_FALSE='#'
else
- if test "${WIN32}" != "yes"; then
- test -z "${ROUTE}" && as_fn_error $? "route utility is required but missing" "$LINENO" 5
- test -z "${IFCONFIG}" && as_fn_error $? "ifconfig utility is required but missing" "$LINENO" 5
- fi
+ HAVE_SITNL_TRUE='#'
+ HAVE_SITNL_FALSE=
+fi
+
+else if test "${WIN32}" != "yes" -a "${have_sitnl}" != "yes"; then
+ test -z "${ROUTE}" && as_fn_error $? "route utility is required but missing" "$LINENO" 5
+ test -z "${IFCONFIG}" && as_fn_error $? "ifconfig utility is required but missing" "$LINENO" 5
+fi
+fi
fi
if test "${enable_selinux}" = "yes"; then
@@ -17732,7 +17824,6 @@ fi
if test "${enable_pkcs11}" = "yes"; then
test "${have_pkcs11_helper}" != "yes" && as_fn_error $? "PKCS11 enabled but libpkcs11-helper is missing" "$LINENO" 5
- test "${enable_crypto}" != "yes" && as_fn_error $? "PKCS11 can be enabled only if crypto is enabled" "$LINENO" 5
OPTIONAL_PKCS11_HELPER_CFLAGS="${PKCS11_HELPER_CFLAGS}"
OPTIONAL_PKCS11_HELPER_LIBS="${PKCS11_HELPER_LIBS}"
@@ -17818,13 +17909,16 @@ _ACEOF
fi
fi
+# When testing a compiler option, we add -Werror to force
+# an error when the option is unsupported. This is not
+# required for gcc, but some compilers such as clang needs it.
old_cflags="$CFLAGS"
- CFLAGS="-Wno-unused-function $CFLAGS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler acceppts -Wno-unused-function" >&5
-$as_echo_n "checking whether the compiler acceppts -Wno-unused-function... " >&6; }
+ CFLAGS="-Wno-stringop-truncation -Werror $CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler accepts -Wno-stringop-truncation" >&5
+$as_echo_n "checking whether the compiler accepts -Wno-stringop-truncation... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -17838,7 +17932,7 @@ main ()
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+$as_echo "yes" >&6; }; CFLAGS="-Wno-stringop-truncation $old_cflags"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }; CFLAGS="$old_cflags"
@@ -17847,9 +17941,9 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
old_cflags="$CFLAGS"
- CFLAGS="-Wno-unused-parameter $CFLAGS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler acceppts -Wno-unused-parameter" >&5
-$as_echo_n "checking whether the compiler acceppts -Wno-unused-parameter... " >&6; }
+ CFLAGS="-Wno-unused-function -Werror $CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler accepts -Wno-unused-function" >&5
+$as_echo_n "checking whether the compiler accepts -Wno-unused-function... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -17863,7 +17957,7 @@ main ()
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+$as_echo "yes" >&6; }; CFLAGS="-Wno-unused-function $old_cflags"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }; CFLAGS="$old_cflags"
@@ -17872,9 +17966,9 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
old_cflags="$CFLAGS"
- CFLAGS="-Wall $CFLAGS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler acceppts -Wall" >&5
-$as_echo_n "checking whether the compiler acceppts -Wall... " >&6; }
+ CFLAGS="-Wno-unused-parameter -Werror $CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler accepts -Wno-unused-parameter" >&5
+$as_echo_n "checking whether the compiler accepts -Wno-unused-parameter... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -17888,7 +17982,32 @@ main ()
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+$as_echo "yes" >&6; }; CFLAGS="-Wno-unused-parameter $old_cflags"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }; CFLAGS="$old_cflags"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+ old_cflags="$CFLAGS"
+ CFLAGS="-Wall -Werror $CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler accepts -Wall" >&5
+$as_echo_n "checking whether the compiler accepts -Wall... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; CFLAGS="-Wall $old_cflags"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }; CFLAGS="$old_cflags"
@@ -17910,10 +18029,6 @@ if test "${enable_werror}" = "yes"; then
CFLAGS="${CFLAGS} -Werror"
fi
-if test "${WIN32}" = "yes"; then
- test -z "${MAN2HTML}" && as_fn_error $? "man2html is required for win32" "$LINENO" 5
-fi
-
if test "${enable_plugin_auth_pam}" = "yes"; then
PLUGIN_AUTH_PAM_CFLAGS="${LIBPAM_CFLAGS}"
if test "${enable_pam_dlopen}" = "yes"; then
@@ -18129,14 +18244,6 @@ else
ENABLE_PLUGIN_DOWN_ROOT_FALSE=
fi
- if test "${enable_crypto}" = "yes"; then
- ENABLE_CRYPTO_TRUE=
- ENABLE_CRYPTO_FALSE='#'
-else
- ENABLE_CRYPTO_TRUE='#'
- ENABLE_CRYPTO_FALSE=
-fi
-
if test "${have_ld_wrap_support}" = "yes"; then
HAVE_LD_WRAP_SUPPORT_TRUE=
HAVE_LD_WRAP_SUPPORT_FALSE='#'
@@ -18145,6 +18252,14 @@ else
HAVE_LD_WRAP_SUPPORT_FALSE=
fi
+ if test "${have_openssl_engine}" = "yes"; then
+ OPENSSL_ENGINE_TRUE=
+ OPENSSL_ENGINE_FALSE='#'
+else
+ OPENSSL_ENGINE_TRUE='#'
+ OPENSSL_ENGINE_FALSE=
+fi
+
sampledir="\$(docdir)/sample"
@@ -18153,93 +18268,111 @@ sampledir="\$(docdir)/sample"
-TEST_LDFLAGS="${OPTIONAL_CRYPTO_LIBS} ${OPTIONAL_PKCS11_HELPER_LIBS} -lcmocka -L\$(top_builddir)/vendor/dist/lib -Wl,-rpath,\$(top_builddir)/vendor/dist/lib"
-TEST_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${OPTIONAL_PKCS11_HELPER_CFLAGS} -I\$(top_srcdir)/include -I\$(top_builddir)/vendor/dist/include"
+# Check whether --enable-unit-tests was given.
+if test "${enable_unit_tests+set}" = set; then :
+ enableval=$enable_unit_tests;
+else
+ enable_unit_tests="yes"
+fi
+# Check if cmocka is available - needed for unit testing
-# Check if cmake is available and cmocka git submodule is initialized,
-# needed for unit testing
-for ac_prog in cmake
-do
- # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CMAKE+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CMAKE"; then
- ac_cv_prog_CMAKE="$CMAKE" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_CMAKE="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CMOCKA" >&5
+$as_echo_n "checking for CMOCKA... " >&6; }
+if test -n "$CMOCKA_CFLAGS"; then
+ pkg_cv_CMOCKA_CFLAGS="$CMOCKA_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cmocka\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "cmocka") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_CMOCKA_CFLAGS=`$PKG_CONFIG --cflags "cmocka" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
fi
+ else
+ pkg_failed=untried
fi
-CMAKE=$ac_cv_prog_CMAKE
-if test -n "$CMAKE"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CMAKE" >&5
-$as_echo "$CMAKE" >&6; }
+if test -n "$CMOCKA_LIBS"; then
+ pkg_cv_CMOCKA_LIBS="$CMOCKA_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cmocka\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "cmocka") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_CMOCKA_LIBS=`$PKG_CONFIG --libs "cmocka" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
fi
- test -n "$CMAKE" && break
-done
-if test -n "${CMAKE}"; then
- if test -f "${srcdir}/vendor/cmocka/CMakeLists.txt"; then
- if true; then
- CMOCKA_INITIALIZED_TRUE=
- CMOCKA_INITIALIZED_FALSE='#'
-else
- CMOCKA_INITIALIZED_TRUE='#'
- CMOCKA_INITIALIZED_FALSE=
-fi
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
- else
- if false; then
- CMOCKA_INITIALIZED_TRUE=
- CMOCKA_INITIALIZED_FALSE='#'
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
else
- CMOCKA_INITIALIZED_TRUE='#'
- CMOCKA_INITIALIZED_FALSE=
+ _pkg_short_errors_supported=no
fi
+ if test $_pkg_short_errors_supported = yes; then
+ CMOCKA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cmocka" 2>&1`
+ else
+ CMOCKA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cmocka" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CMOCKA_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cmocka.pc not found on the system. Unit tests disabled" >&5
+$as_echo "$as_me: WARNING: cmocka.pc not found on the system. Unit tests disabled" >&2;}
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cmocka.pc not found on the system. Unit tests disabled" >&5
+$as_echo "$as_me: WARNING: cmocka.pc not found on the system. Unit tests disabled" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: !! WARNING !! The cmoka git submodule has not been initialized or updated. Unit testing cannot be performed." >&5
-$as_echo "!! WARNING !! The cmoka git submodule has not been initialized or updated. Unit testing cannot be performed." >&6; }
- fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: !! WARNING !! CMake is NOT available. Unit testing cannot be performed." >&5
-$as_echo "!! WARNING !! CMake is NOT available. Unit testing cannot be performed." >&6; }
- if false; then
- CMOCKA_INITIALIZED_TRUE=
- CMOCKA_INITIALIZED_FALSE='#'
+ CMOCKA_CFLAGS=$pkg_cv_CMOCKA_CFLAGS
+ CMOCKA_LIBS=$pkg_cv_CMOCKA_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ have_cmocka="yes"
+fi
+ if test "${enable_unit_tests}" = "yes" -a "${have_cmocka}" = "yes" ; then
+ ENABLE_UNITTESTS_TRUE=
+ ENABLE_UNITTESTS_FALSE='#'
else
- CMOCKA_INITIALIZED_TRUE='#'
- CMOCKA_INITIALIZED_FALSE=
+ ENABLE_UNITTESTS_TRUE='#'
+ ENABLE_UNITTESTS_FALSE=
fi
-fi
-ac_config_files="$ac_config_files version.sh Makefile build/Makefile build/msvc/Makefile build/msvc/msvc-generate/Makefile distro/Makefile distro/systemd/Makefile include/Makefile src/Makefile src/compat/Makefile src/openvpn/Makefile src/openvpnserv/Makefile src/plugins/Makefile src/plugins/auth-pam/Makefile src/plugins/down-root/Makefile tests/Makefile tests/unit_tests/Makefile tests/unit_tests/example_test/Makefile tests/unit_tests/openvpn/Makefile tests/unit_tests/plugins/Makefile tests/unit_tests/plugins/auth-pam/Makefile vendor/Makefile sample/Makefile doc/Makefile"
+TEST_LDFLAGS="${OPTIONAL_CRYPTO_LIBS} ${OPTIONAL_PKCS11_HELPER_LIBS}"
+TEST_LDFLAGS="${TEST_LDFLAGS} ${OPTIONAL_LZO_LIBS} ${CMOCKA_LIBS}"
+TEST_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${OPTIONAL_PKCS11_HELPER_CFLAGS}"
+TEST_CFLAGS="${TEST_CFLAGS} ${OPTIONAL_LZO_CFLAGS}"
+TEST_CFLAGS="${TEST_CFLAGS} -I\$(top_srcdir)/include ${CMOCKA_CFLAGS}"
+
+
+
+
+ac_config_files="$ac_config_files version.sh Makefile build/Makefile build/msvc/Makefile build/msvc/msvc-generate/Makefile distro/Makefile distro/systemd/Makefile doc/Makefile doc/doxygen/Makefile doc/doxygen/openvpn.doxyfile include/Makefile sample/sample-plugins/Makefile src/Makefile src/compat/Makefile src/openvpn/Makefile src/openvpnmsica/Makefile src/openvpnserv/Makefile src/plugins/Makefile src/plugins/auth-pam/Makefile src/plugins/down-root/Makefile src/tapctl/Makefile tests/Makefile tests/unit_tests/Makefile tests/unit_tests/example_test/Makefile tests/unit_tests/openvpn/Makefile tests/unit_tests/plugins/Makefile tests/unit_tests/plugins/auth-pam/Makefile tests/unit_tests/engine-key/Makefile sample/Makefile"
ac_config_files="$ac_config_files tests/t_client.sh"
@@ -18376,10 +18509,30 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${TARGET_LINUX_TRUE}" && test -z "${TARGET_LINUX_FALSE}"; then
+ as_fn_error $? "conditional \"TARGET_LINUX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${TARGET_LINUX_TRUE}" && test -z "${TARGET_LINUX_FALSE}"; then
+ as_fn_error $? "conditional \"TARGET_LINUX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_PYDOCUTILS_TRUE}" && test -z "${HAVE_PYDOCUTILS_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_PYDOCUTILS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${ENABLE_SYSTEMD_TRUE}" && test -z "${ENABLE_SYSTEMD_FALSE}"; then
as_fn_error $? "conditional \"ENABLE_SYSTEMD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${HAVE_SITNL_TRUE}" && test -z "${HAVE_SITNL_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_SITNL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_SITNL_TRUE}" && test -z "${HAVE_SITNL_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_SITNL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then
as_fn_error $? "conditional \"WIN32\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -18396,24 +18549,16 @@ if test -z "${ENABLE_PLUGIN_DOWN_ROOT_TRUE}" && test -z "${ENABLE_PLUGIN_DOWN_RO
as_fn_error $? "conditional \"ENABLE_PLUGIN_DOWN_ROOT\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
-if test -z "${ENABLE_CRYPTO_TRUE}" && test -z "${ENABLE_CRYPTO_FALSE}"; then
- as_fn_error $? "conditional \"ENABLE_CRYPTO\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
if test -z "${HAVE_LD_WRAP_SUPPORT_TRUE}" && test -z "${HAVE_LD_WRAP_SUPPORT_FALSE}"; then
as_fn_error $? "conditional \"HAVE_LD_WRAP_SUPPORT\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
-if test -z "${CMOCKA_INITIALIZED_TRUE}" && test -z "${CMOCKA_INITIALIZED_FALSE}"; then
- as_fn_error $? "conditional \"CMOCKA_INITIALIZED\" was never defined.
+if test -z "${OPENSSL_ENGINE_TRUE}" && test -z "${OPENSSL_ENGINE_FALSE}"; then
+ as_fn_error $? "conditional \"OPENSSL_ENGINE\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
-if test -z "${CMOCKA_INITIALIZED_TRUE}" && test -z "${CMOCKA_INITIALIZED_FALSE}"; then
- as_fn_error $? "conditional \"CMOCKA_INITIALIZED\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
-if test -z "${CMOCKA_INITIALIZED_TRUE}" && test -z "${CMOCKA_INITIALIZED_FALSE}"; then
- as_fn_error $? "conditional \"CMOCKA_INITIALIZED\" was never defined.
+if test -z "${ENABLE_UNITTESTS_TRUE}" && test -z "${ENABLE_UNITTESTS_FALSE}"; then
+ as_fn_error $? "conditional \"ENABLE_UNITTESTS\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
@@ -18813,7 +18958,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by OpenVPN $as_me 2.4.9, which was
+This file was extended by OpenVPN $as_me 2.5.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -18879,7 +19024,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-OpenVPN config.status 2.4.9
+OpenVPN config.status 2.5.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -19378,23 +19523,28 @@ do
"build/msvc/msvc-generate/Makefile") CONFIG_FILES="$CONFIG_FILES build/msvc/msvc-generate/Makefile" ;;
"distro/Makefile") CONFIG_FILES="$CONFIG_FILES distro/Makefile" ;;
"distro/systemd/Makefile") CONFIG_FILES="$CONFIG_FILES distro/systemd/Makefile" ;;
+ "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+ "doc/doxygen/Makefile") CONFIG_FILES="$CONFIG_FILES doc/doxygen/Makefile" ;;
+ "doc/doxygen/openvpn.doxyfile") CONFIG_FILES="$CONFIG_FILES doc/doxygen/openvpn.doxyfile" ;;
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
+ "sample/sample-plugins/Makefile") CONFIG_FILES="$CONFIG_FILES sample/sample-plugins/Makefile" ;;
"src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
"src/compat/Makefile") CONFIG_FILES="$CONFIG_FILES src/compat/Makefile" ;;
"src/openvpn/Makefile") CONFIG_FILES="$CONFIG_FILES src/openvpn/Makefile" ;;
+ "src/openvpnmsica/Makefile") CONFIG_FILES="$CONFIG_FILES src/openvpnmsica/Makefile" ;;
"src/openvpnserv/Makefile") CONFIG_FILES="$CONFIG_FILES src/openvpnserv/Makefile" ;;
"src/plugins/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/Makefile" ;;
"src/plugins/auth-pam/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/auth-pam/Makefile" ;;
"src/plugins/down-root/Makefile") CONFIG_FILES="$CONFIG_FILES src/plugins/down-root/Makefile" ;;
+ "src/tapctl/Makefile") CONFIG_FILES="$CONFIG_FILES src/tapctl/Makefile" ;;
"tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
"tests/unit_tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/Makefile" ;;
"tests/unit_tests/example_test/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/example_test/Makefile" ;;
"tests/unit_tests/openvpn/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/openvpn/Makefile" ;;
"tests/unit_tests/plugins/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/plugins/Makefile" ;;
"tests/unit_tests/plugins/auth-pam/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/plugins/auth-pam/Makefile" ;;
- "vendor/Makefile") CONFIG_FILES="$CONFIG_FILES vendor/Makefile" ;;
+ "tests/unit_tests/engine-key/Makefile") CONFIG_FILES="$CONFIG_FILES tests/unit_tests/engine-key/Makefile" ;;
"sample/Makefile") CONFIG_FILES="$CONFIG_FILES sample/Makefile" ;;
- "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
"tests/t_client.sh") CONFIG_FILES="$CONFIG_FILES tests/t_client.sh" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -20073,7 +20223,9 @@ $as_echo X/"$am_mf" |
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "Something went wrong bootstrapping makefile fragments
- for automatic dependency tracking. Try re-running configure with the
+ for automatic dependency tracking. If GNU make was not used, consider
+ re-running the configure script with MAKE=\"gmake\" (or whatever is
+ necessary). You can also try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).
See \`config.log' for more details" "$LINENO" 5; }
diff --git a/configure.ac b/configure.ac
index 4690028..1f166c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@ dnl session authentication and key exchange,
dnl packet encryption, packet authentication, and
dnl packet compression.
dnl
-dnl Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+dnl Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
dnl Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
@@ -54,7 +54,9 @@ m4_define([serial_tests], [
awk '{split ($NF,a,"."); if (a[1] == 1 && a[2] >= 12) { print "serial-tests" }}'
])
])
-AM_INIT_AUTOMAKE(foreign serial_tests) dnl NB: Do not [quote] this parameter.
+# This foreign option prevents autoreconf from overriding our COPYING and
+# INSTALL targets:
+AM_INIT_AUTOMAKE(foreign serial_tests 1.9) dnl NB: Do not [quote] this parameter.
AC_CANONICAL_HOST
AC_USE_SYSTEM_EXTENSIONS
@@ -78,13 +80,6 @@ AC_ARG_ENABLE(comp-stub,
)
AC_ARG_ENABLE(
- [crypto],
- [AS_HELP_STRING([--disable-crypto], [disable crypto support @<:@default=yes@:>@])],
- ,
- [enable_crypto="yes"]
-)
-
-AC_ARG_ENABLE(
[ofb-cfb],
[AS_HELP_STRING([--disable-ofb-cfb], [disable support for OFB and CFB cipher modes @<:@default=yes@:>@])],
,
@@ -99,13 +94,6 @@ AC_ARG_ENABLE(
)
AC_ARG_ENABLE(
- [server],
- [AS_HELP_STRING([--disable-server], [disable server support only (but retain client support) @<:@default=yes@:>@])],
- ,
- [enable_server="yes"]
-)
-
-AC_ARG_ENABLE(
[plugins],
[AS_HELP_STRING([--disable-plugins], [disable plug-in support @<:@default=yes@:>@])],
,
@@ -251,7 +239,7 @@ AC_ARG_ENABLE(
AC_ARG_ENABLE(
[systemd],
- [AS_HELP_STRING([--enable-systemd], [enable systemd suppport @<:@default=no@:>@])],
+ [AS_HELP_STRING([--enable-systemd], [enable systemd support @<:@default=no@:>@])],
,
[enable_systemd="no"]
)
@@ -301,15 +289,19 @@ else
fi
AC_DEFINE_UNQUOTED([TARGET_ALIAS], ["${host}"], [A string representing our host])
+AM_CONDITIONAL([TARGET_LINUX], [false])
case "$host" in
*-*-linux*)
AC_DEFINE([TARGET_LINUX], [1], [Are we running on Linux?])
+ AM_CONDITIONAL([TARGET_LINUX], [true])
AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["L"], [Target prefix])
+ have_sitnl="yes"
;;
*-*-solaris*)
AC_DEFINE([TARGET_SOLARIS], [1], [Are we running on Solaris?])
AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["S"], [Target prefix])
CPPFLAGS="$CPPFLAGS -D_XPG4_2"
+ test -x /bin/bash && SHELL="/bin/bash"
;;
*-*-openbsd*)
AC_DEFINE([TARGET_OPENBSD], [1], [Are we running on OpenBSD?])
@@ -364,7 +356,6 @@ AC_ARG_VAR([IFCONFIG], [full path to ipconfig utility])
AC_ARG_VAR([ROUTE], [full path to route utility])
AC_ARG_VAR([IPROUTE], [full path to ip utility])
AC_ARG_VAR([NETSTAT], [path to netstat utility]) # tests
-AC_ARG_VAR([MAN2HTML], [path to man2html utility])
AC_ARG_VAR([GIT], [path to git utility])
AC_ARG_VAR([SYSTEMD_ASK_PASSWORD], [path to systemd-ask-password utility])
AC_ARG_VAR([SYSTEMD_UNIT_DIR], [Path of systemd unit directory @<:@default=LIBDIR/systemd/system@:>@])
@@ -374,13 +365,21 @@ AC_PATH_PROGS([ROUTE], [route],, [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
AC_PATH_PROGS([IPROUTE], [ip],, [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
AC_PATH_PROGS([SYSTEMD_ASK_PASSWORD], [systemd-ask-password],, [$PATH:/usr/local/bin:/usr/bin:/bin])
AC_CHECK_PROGS([NETSTAT], [netstat], [netstat], [$PATH:/usr/local/sbin:/usr/sbin:/sbin:/etc]) # tests
-AC_CHECK_PROGS([MAN2HTML], [man2html])
AC_CHECK_PROGS([GIT], [git]) # optional
AC_DEFINE_UNQUOTED([IFCONFIG_PATH], ["$IFCONFIG"], [Path to ifconfig tool])
AC_DEFINE_UNQUOTED([IPROUTE_PATH], ["$IPROUTE"], [Path to iproute tool])
AC_DEFINE_UNQUOTED([ROUTE_PATH], ["$ROUTE"], [Path to route tool])
AC_DEFINE_UNQUOTED([SYSTEMD_ASK_PASSWORD_PATH], ["$SYSTEMD_ASK_PASSWORD"], [Path to systemd-ask-password tool])
+#
+# man page generation - based on python-docutils
+#
+AC_ARG_VAR([RST2MAN], [path to rst2man utility])
+AC_ARG_VAR([RST2HTML], [path to rst2html utility])
+AC_CHECK_PROGS([RST2MAN], [rst2man rst2man.py])
+AC_CHECK_PROGS([RST2HTML], [rst2html rst2html.py])
+AM_CONDITIONAL([HAVE_PYDOCUTILS], [test "${RST2MAN}" -a "${RST2HTML}"])
+
# Set -std=c99 unless user already specified a -std=
case "${CFLAGS}" in
*-std=*) ;;
@@ -441,7 +440,7 @@ AC_CHECK_HEADERS([ \
unistd.h signal.h libgen.h stropts.h \
syslog.h pwd.h grp.h \
sys/sockio.h sys/uio.h linux/sockios.h \
- linux/types.h sys/poll.h sys/epoll.h err.h \
+ linux/types.h poll.h sys/epoll.h err.h \
])
SOCKET_INCLUDES="
@@ -658,7 +657,7 @@ AC_CHECK_FUNCS([ \
ctime memset vsnprintf strdup \
setsid chdir putenv getpeername unlink \
chsize ftruncate execve getpeereid umask basename dirname access \
- epoll_create \
+ epoll_create strsep \
])
AC_CHECK_LIB(
@@ -841,7 +840,7 @@ PKG_CHECK_MODULES(
[]
)
-if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
+if test "${with_crypto_library}" = "openssl"; then
AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL])
@@ -849,11 +848,10 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
# if the user did not explicitly specify flags, try to autodetect
PKG_CHECK_MODULES(
[OPENSSL],
- [libcrypto >= 0.9.8, libssl >= 0.9.8],
- [have_openssl="yes"],
- [have_openssl="no"] # Provide if-not-found to prevent erroring out
+ [openssl >= 1.0.2],
+ [have_openssl="yes"],
+ [] # If this fails, we will do another test next
)
-
OPENSSL_LIBS=${OPENSSL_LIBS:--lssl -lcrypto}
fi
@@ -862,6 +860,27 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
CFLAGS="${CFLAGS} ${OPENSSL_CFLAGS}"
LIBS="${LIBS} ${OPENSSL_LIBS}"
+ # If pkgconfig check failed or OPENSSL_CFLAGS/OPENSSL_LIBS env vars
+ # are used, check the version directly in the OpenSSL include file
+ if test "${have_openssl}" != "yes"; then
+ AC_MSG_CHECKING([additionally if OpenSSL is available and version >= 1.0.2])
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#include <openssl/opensslv.h>
+ ]],
+ [[
+/* Version encoding: MNNFFPPS - see opensslv.h for details */
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#error OpenSSL too old
+#endif
+ ]]
+ )],
+ [AC_MSG_RESULT([ok])],
+ [AC_MSG_ERROR([OpenSSL version too old])]
+ )
+ fi
+
AC_CHECK_FUNCS([SSL_CTX_new EVP_CIPHER_CTX_set_key_length],
,
[AC_MSG_ERROR([openssl check failed])]
@@ -888,13 +907,16 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [OpenSSL engine support available])
fi
- have_crypto_aead_modes="yes"
- AC_CHECK_FUNCS(
+ AC_CHECK_FUNC(
[EVP_aes_256_gcm],
,
- [have_crypto_aead_modes="no"; break]
+ [AC_MSG_ERROR([OpenSSL check for AES-256-GCM support failed])]
)
+ # All supported OpenSSL version (>= 1.0.2)
+ # have this feature
+ have_export_keying_material="yes"
+
AC_CHECK_FUNCS(
[ \
HMAC_CTX_new \
@@ -904,8 +926,10 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
EVP_MD_CTX_free \
EVP_MD_CTX_reset \
EVP_CIPHER_CTX_reset \
+ OpenSSL_version \
SSL_CTX_get_default_passwd_cb \
SSL_CTX_get_default_passwd_cb_userdata \
+ SSL_CTX_set1_groups \
SSL_CTX_set_security_level \
X509_get0_notBefore \
X509_get0_notAfter \
@@ -913,7 +937,6 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
X509_STORE_get0_objects \
X509_OBJECT_free \
X509_OBJECT_get_type \
- EVP_PKEY_id \
EVP_PKEY_get0_RSA \
EVP_PKEY_get0_DSA \
EVP_PKEY_get0_EC_KEY \
@@ -941,11 +964,10 @@ if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
CFLAGS="${saved_CFLAGS}"
LIBS="${saved_LIBS}"
- have_crypto="yes"
AC_DEFINE([ENABLE_CRYPTO_OPENSSL], [1], [Use OpenSSL library])
CRYPTO_CFLAGS="${OPENSSL_CFLAGS}"
CRYPTO_LIBS="${OPENSSL_LIBS}"
-elif test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "mbedtls"; then
+elif test "${with_crypto_library}" = "mbedtls"; then
AC_ARG_VAR([MBEDTLS_CFLAGS], [C compiler flags for mbedtls])
AC_ARG_VAR([MBEDTLS_LIBS], [linker flags for mbedtls])
@@ -983,52 +1005,28 @@ elif test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "mbedtls"; th
[AC_MSG_ERROR([mbed TLS 2.y.z required])]
)
- mbedtls_with_pkcs11="no"
- AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [[
-#include <mbedtls/config.h>
- ]],
- [[
-#ifndef MBEDTLS_PKCS11_C
-#error pkcs11 wrapper missing
-#endif
- ]]
- )],
- mbedtls_with_pkcs11="yes")
-
- AC_MSG_CHECKING([mbedtls pkcs11 support])
- if test "${enable_pkcs11}" = "yes"; then
- if test "${mbedtls_with_pkcs11}" = "yes"; then
- AC_MSG_RESULT([ok])
- else
- AC_MSG_ERROR([mbedtls has no pkcs11 wrapper compiled in])
- fi
- else
- if test "${mbedtls_with_pkcs11}" != "yes"; then
- AC_MSG_RESULT([ok])
- else
- AC_MSG_ERROR([mbed TLS compiled with PKCS11, while OpenVPN is not])
- fi
- fi
-
- have_crypto_aead_modes="yes"
AC_CHECK_FUNCS(
[ \
mbedtls_cipher_write_tag \
mbedtls_cipher_check_tag \
],
,
- [have_crypto_aead_modes="no"; break]
+ [AC_MSG_ERROR([mbed TLS check for AEAD support failed])]
+ )
+
+ have_export_keying_material="yes"
+ AC_CHECK_FUNC(
+ [mbedtls_ssl_conf_export_keys_ext_cb],
+ ,
+ [have_export_keying_material="no"]
)
CFLAGS="${saved_CFLAGS}"
LIBS="${saved_LIBS}"
- have_crypto="yes"
AC_DEFINE([ENABLE_CRYPTO_MBEDTLS], [1], [Use mbed TLS library])
CRYPTO_CFLAGS="${MBEDTLS_CFLAGS}"
CRYPTO_LIBS="${MBEDTLS_LIBS}"
-elif test "${enable_crypto}" = "yes"; then
+else
AC_MSG_ERROR([Invalid crypto library: ${with_crypto_library}])
fi
@@ -1217,7 +1215,6 @@ if test "${enable_x509_alt_username}" = "yes"; then
fi
test "${ac_cv_header_sys_uio_h}" = "yes" && AC_DEFINE([HAVE_IOVEC], [1], [struct iovec needed for IPv6 support])
-test "${enable_server}" = "no" && AC_DEFINE([ENABLE_CLIENT_ONLY], [1], [Enable client capability only])
test "${enable_management}" = "yes" && AC_DEFINE([ENABLE_MANAGEMENT], [1], [Enable management server capability])
test "${enable_multihome}" = "yes" && AC_DEFINE([ENABLE_MULTIHOME], [1], [Enable multi-homed UDP server capability])
test "${enable_debug}" = "yes" && AC_DEFINE([ENABLE_DEBUG], [1], [Enable debugging support])
@@ -1228,14 +1225,15 @@ test "${enable_def_auth}" = "yes" && AC_DEFINE([ENABLE_DEF_AUTH], [1], [Enable d
test "${enable_pf}" = "yes" && AC_DEFINE([ENABLE_PF], [1], [Enable internal packet filter])
test "${enable_strict_options}" = "yes" && AC_DEFINE([ENABLE_STRICT_OPTIONS_CHECK], [1], [Enable strict options check between peers])
-if test "${enable_crypto}" = "yes"; then
- test "${have_crypto}" != "yes" && AC_MSG_ERROR([${with_crypto_library} crypto is required but missing])
- test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes])
- test "${have_crypto_aead_modes}" = "yes" && AC_DEFINE([HAVE_AEAD_CIPHER_MODES], [1], [Use crypto library])
- OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
- OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
- AC_DEFINE([ENABLE_CRYPTO], [1], [Enable crypto library])
+test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes])
+if test "${have_export_keying_material}" = "yes"; then
+ AC_DEFINE(
+ [HAVE_EXPORT_KEYING_MATERIAL], [1],
+ [Crypto library supports keying material exporter]
+ )
fi
+OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
+OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
if test "${enable_plugins}" = "yes"; then
OPTIONAL_DL_LIBS="${DL_LIBS}"
@@ -1245,14 +1243,19 @@ else
enable_plugin_down_root="no"
fi
+AM_CONDITIONAL([HAVE_SITNL], [false])
+
if test "${enable_iproute2}" = "yes"; then
test -z "${IPROUTE}" && AC_MSG_ERROR([ip utility is required but missing])
AC_DEFINE([ENABLE_IPROUTE], [1], [enable iproute2 support])
-else
- if test "${WIN32}" != "yes"; then
- test -z "${ROUTE}" && AC_MSG_ERROR([route utility is required but missing])
- test -z "${IFCONFIG}" && AC_MSG_ERROR([ifconfig utility is required but missing])
- fi
+else if test "${have_sitnl}" = "yes"; then
+ AC_DEFINE([ENABLE_SITNL], [1], [enable sitnl support])
+ AM_CONDITIONAL([HAVE_SITNL], [true])
+else if test "${WIN32}" != "yes" -a "${have_sitnl}" != "yes"; then
+ test -z "${ROUTE}" && AC_MSG_ERROR([route utility is required but missing])
+ test -z "${IFCONFIG}" && AC_MSG_ERROR([ifconfig utility is required but missing])
+fi
+fi
fi
if test "${enable_selinux}" = "yes"; then
@@ -1275,7 +1278,6 @@ fi
if test "${enable_pkcs11}" = "yes"; then
test "${have_pkcs11_helper}" != "yes" && AC_MSG_ERROR([PKCS11 enabled but libpkcs11-helper is missing])
- test "${enable_crypto}" != "yes" && AC_MSG_ERROR([PKCS11 can be enabled only if crypto is enabled])
OPTIONAL_PKCS11_HELPER_CFLAGS="${PKCS11_HELPER_CFLAGS}"
OPTIONAL_PKCS11_HELPER_LIBS="${PKCS11_HELPER_LIBS}"
AC_DEFINE([ENABLE_PKCS11], [1], [Enable PKCS11])
@@ -1288,14 +1290,18 @@ if test "${enable_pkcs11}" = "yes"; then
)
fi
+# When testing a compiler option, we add -Werror to force
+# an error when the option is unsupported. This is not
+# required for gcc, but some compilers such as clang needs it.
AC_DEFUN([ACL_CHECK_ADD_COMPILE_FLAGS], [
old_cflags="$CFLAGS"
- CFLAGS="$1 $CFLAGS"
- AC_MSG_CHECKING([whether the compiler acceppts $1])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AC_MSG_RESULT([yes])],
+ CFLAGS="$1 -Werror $CFLAGS"
+ AC_MSG_CHECKING([whether the compiler accepts $1])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AC_MSG_RESULT([yes])]; CFLAGS="$1 $old_cflags",
[AC_MSG_RESULT([no]); CFLAGS="$old_cflags"])]
)
+ACL_CHECK_ADD_COMPILE_FLAGS([-Wno-stringop-truncation])
ACL_CHECK_ADD_COMPILE_FLAGS([-Wno-unused-function])
ACL_CHECK_ADD_COMPILE_FLAGS([-Wno-unused-parameter])
ACL_CHECK_ADD_COMPILE_FLAGS([-Wall])
@@ -1312,10 +1318,6 @@ if test "${enable_werror}" = "yes"; then
CFLAGS="${CFLAGS} -Werror"
fi
-if test "${WIN32}" = "yes"; then
- test -z "${MAN2HTML}" && AC_MSG_ERROR([man2html is required for win32])
-fi
-
if test "${enable_plugin_auth_pam}" = "yes"; then
PLUGIN_AUTH_PAM_CFLAGS="${LIBPAM_CFLAGS}"
if test "${enable_pam_dlopen}" = "yes"; then
@@ -1383,8 +1385,8 @@ AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"])
AM_CONDITIONAL([ENABLE_PLUGIN_AUTH_PAM], [test "${enable_plugin_auth_pam}" = "yes"])
AM_CONDITIONAL([ENABLE_PLUGIN_DOWN_ROOT], [test "${enable_plugin_down_root}" = "yes"])
-AM_CONDITIONAL([ENABLE_CRYPTO], [test "${enable_crypto}" = "yes"])
AM_CONDITIONAL([HAVE_LD_WRAP_SUPPORT], [test "${have_ld_wrap_support}" = "yes"])
+AM_CONDITIONAL([OPENSSL_ENGINE], [test "${have_openssl_engine}" = "yes"])
sampledir="\$(docdir)/sample"
AC_SUBST([plugindir])
@@ -1393,27 +1395,31 @@ AC_SUBST([sampledir])
AC_SUBST([systemdunitdir])
AC_SUBST([tmpfilesdir])
-TEST_LDFLAGS="${OPTIONAL_CRYPTO_LIBS} ${OPTIONAL_PKCS11_HELPER_LIBS} -lcmocka -L\$(top_builddir)/vendor/dist/lib -Wl,-rpath,\$(top_builddir)/vendor/dist/lib"
-TEST_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${OPTIONAL_PKCS11_HELPER_CFLAGS} -I\$(top_srcdir)/include -I\$(top_builddir)/vendor/dist/include"
+AC_ARG_ENABLE(
+ [unit-tests],
+ [AS_HELP_STRING([--disable-unit-tests],
+ [Disables building and running the unit tests suite])],
+ [],
+ [enable_unit_tests="yes"]
+)
-AC_SUBST([TEST_LDFLAGS])
-AC_SUBST([TEST_CFLAGS])
+# Check if cmocka is available - needed for unit testing
+PKG_CHECK_MODULES(
+ [CMOCKA], [cmocka],
+ [have_cmocka="yes"],
+ [AC_MSG_WARN([cmocka.pc not found on the system. Unit tests disabled])]
+)
+AM_CONDITIONAL([ENABLE_UNITTESTS], [test "${enable_unit_tests}" = "yes" -a "${have_cmocka}" = "yes" ])
+AC_SUBST([ENABLE_UNITTESTS])
-# Check if cmake is available and cmocka git submodule is initialized,
-# needed for unit testing
-AC_CHECK_PROGS([CMAKE], [cmake])
-if test -n "${CMAKE}"; then
- if test -f "${srcdir}/vendor/cmocka/CMakeLists.txt"; then
- AM_CONDITIONAL([CMOCKA_INITIALIZED], [true])
- else
- AM_CONDITIONAL([CMOCKA_INITIALIZED], [false])
- AC_MSG_RESULT([!! WARNING !! The cmoka git submodule has not been initialized or updated. Unit testing cannot be performed.])
- fi
-else
- AC_MSG_RESULT([!! WARNING !! CMake is NOT available. Unit testing cannot be performed.])
- AM_CONDITIONAL([CMOCKA_INITIALIZED], [false])
-fi
+TEST_LDFLAGS="${OPTIONAL_CRYPTO_LIBS} ${OPTIONAL_PKCS11_HELPER_LIBS}"
+TEST_LDFLAGS="${TEST_LDFLAGS} ${OPTIONAL_LZO_LIBS} ${CMOCKA_LIBS}"
+TEST_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${OPTIONAL_PKCS11_HELPER_CFLAGS}"
+TEST_CFLAGS="${TEST_CFLAGS} ${OPTIONAL_LZO_CFLAGS}"
+TEST_CFLAGS="${TEST_CFLAGS} -I\$(top_srcdir)/include ${CMOCKA_CFLAGS}"
+AC_SUBST([TEST_LDFLAGS])
+AC_SUBST([TEST_CFLAGS])
AC_CONFIG_FILES([
version.sh
@@ -1423,23 +1429,28 @@ AC_CONFIG_FILES([
build/msvc/msvc-generate/Makefile
distro/Makefile
distro/systemd/Makefile
+ doc/Makefile
+ doc/doxygen/Makefile
+ doc/doxygen/openvpn.doxyfile
include/Makefile
+ sample/sample-plugins/Makefile
src/Makefile
src/compat/Makefile
src/openvpn/Makefile
+ src/openvpnmsica/Makefile
src/openvpnserv/Makefile
src/plugins/Makefile
src/plugins/auth-pam/Makefile
src/plugins/down-root/Makefile
+ src/tapctl/Makefile
tests/Makefile
tests/unit_tests/Makefile
tests/unit_tests/example_test/Makefile
tests/unit_tests/openvpn/Makefile
tests/unit_tests/plugins/Makefile
tests/unit_tests/plugins/auth-pam/Makefile
- vendor/Makefile
+ tests/unit_tests/engine-key/Makefile
sample/Makefile
- doc/Makefile
])
AC_CONFIG_FILES([tests/t_client.sh], [chmod +x tests/t_client.sh])
AC_OUTPUT
diff --git a/contrib/vcpkg-ports/openssl/portfile.cmake b/contrib/vcpkg-ports/openssl/portfile.cmake
new file mode 100644
index 0000000..9b59a3c
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/portfile.cmake
@@ -0,0 +1,25 @@
+if(EXISTS "${CURRENT_INSTALLED_DIR}/include/openssl/ssl.h")
+ message(FATAL_ERROR "Can't build openssl if libressl/boringssl is installed. Please remove libressl/boringssl, and try install openssl again if you need it.")
+endif()
+
+set(OPENSSL_VERSION 1.1.1k)
+vcpkg_download_distfile(ARCHIVE
+ URLS "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz" "https://www.openssl.org/source/old/1.1.1/openssl-${OPENSSL_VERSION}.tar.gz"
+ FILENAME "openssl-${OPENSSL_VERSION}.tar.gz"
+ SHA512 73cd042d4056585e5a9dd7ab68e7c7310a3a4c783eafa07ab0b560e7462b924e4376436a6d38a155c687f6942a881cfc0c1b9394afcde1d8c46bf396e7d51121
+)
+
+vcpkg_find_acquire_program(PERL)
+get_filename_component(PERL_EXE_PATH ${PERL} DIRECTORY)
+vcpkg_add_to_path("${PERL_EXE_PATH}")
+
+if(VCPKG_TARGET_IS_UWP)
+ include("${CMAKE_CURRENT_LIST_DIR}/uwp/portfile.cmake")
+elseif(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
+ include("${CMAKE_CURRENT_LIST_DIR}/windows/portfile.cmake")
+else()
+ include("${CMAKE_CURRENT_LIST_DIR}/unix/portfile.cmake")
+endif()
+
+
+file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
diff --git a/contrib/vcpkg-ports/openssl/unix/CMakeLists.txt b/contrib/vcpkg-ports/openssl/unix/CMakeLists.txt
new file mode 100644
index 0000000..fd84816
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/unix/CMakeLists.txt
@@ -0,0 +1,280 @@
+cmake_minimum_required(VERSION 3.9)
+project(openssl C)
+
+if(NOT SOURCE_PATH)
+ message(FATAL_ERROR "Requires SOURCE_PATH")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(PLATFORM linux-x86_64)
+ else()
+ set(PLATFORM linux-generic32)
+ endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+ if(VCPKG_TARGET_ARCHITECTURE MATCHES "arm64")
+ set(PLATFORM ios64-xcrun)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "arm")
+ set(PLATFORM ios-xcrun)
+ elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "x86" OR
+ VCPKG_TARGET_ARCHITECTURE MATCHES "x64")
+ set(PLATFORM iossimulator-xcrun)
+ else()
+ message(FATAL_ERROR "Unknown iOS target architecture: ${VCPKG_TARGET_ARCHITECTURE}")
+ endif()
+ # disable that makes linkage error (e.g. require stderr usage)
+ list(APPEND DISABLES no-stdio no-ui no-asm)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ if(VCPKG_TARGET_ARCHITECTURE MATCHES "arm64")
+ set(PLATFORM darwin64-arm64-cc)
+ else()
+ set(PLATFORM darwin64-x86_64-cc)
+ endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ set(PLATFORM BSD-generic64)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+ set(PLATFORM BSD-generic64)
+elseif(MINGW)
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ set(PLATFORM mingw64)
+ else()
+ set(PLATFORM mingw)
+ endif()
+elseif(EMSCRIPTEN)
+ set(MAKE $ENV{EMSDK}/upstream/emscripten/emmake)
+ set(ENV{MAKE} $ENV{EMSDK}/upstream/emscripten/emmake)
+else()
+ message(FATAL_ERROR "Unknown platform")
+endif()
+
+get_filename_component(COMPILER_ROOT "${CMAKE_C_COMPILER}" DIRECTORY)
+
+message("CMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
+message("COMPILER_ROOT=${COMPILER_ROOT}")
+message("CMAKE_SYSROOT=${CMAKE_SYSROOT}")
+message("CMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}")
+message("CMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
+message("CMAKE_C_FLAGS=${CMAKE_C_FLAGS}")
+message("CMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}")
+message("CMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}")
+message("CMAKE_INCLUDE_SYSTEM_FLAG_C=${CMAKE_INCLUDE_SYSTEM_FLAG_C}")
+message("CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG=${CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG}")
+
+set(CFLAGS "${CMAKE_C_FLAGS}")
+if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(CFLAGS "-Wno-error=unused-command-line-argument ${CMAKE_C_FLAGS}")
+endif()
+if(CMAKE_C_COMPILER_TARGET AND CMAKE_C_COMPILE_OPTIONS_TARGET)
+ set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}")
+endif()
+if(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN AND CMAKE_C_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN)
+ set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN}")
+endif()
+if(CMAKE_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT)
+ set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}")
+elseif(CMAKE_OSX_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT)
+ set(CFLAGS "${CFLAGS} ${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_OSX_SYSROOT}")
+endif()
+if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG)
+ set(CFLAGS "${CFLAGS} ${CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}")
+endif()
+
+string(REGEX REPLACE "^ " "" CFLAGS "${CFLAGS}")
+
+if(CMAKE_HOST_WIN32)
+ file(TO_NATIVE_PATH ENV_PATH "${COMPILER_ROOT};$ENV{PATH}")
+else()
+ file(TO_NATIVE_PATH ENV_PATH "${COMPILER_ROOT}:$ENV{PATH}")
+endif()
+set(ENV{ANDROID_DEV} "${CMAKE_SYSROOT}/usr")
+
+if(NOT IOS)
+ set(ENV{CC} "${CMAKE_C_COMPILER}")
+endif()
+
+message("ENV{ANDROID_DEV}=$ENV{ANDROID_DEV}")
+
+get_filename_component(SOURCE_PATH_NAME "${SOURCE_PATH}" NAME)
+set(BUILDDIR "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_PATH_NAME}")
+
+if(NOT EXISTS "${BUILDDIR}")
+ file(COPY ${SOURCE_PATH} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+endif()
+
+get_filename_component(MSYS_BIN_DIR "${MAKE}" DIRECTORY)
+
+if(BUILD_SHARED_LIBS)
+ set(SHARED shared)
+ file(STRINGS "${BUILDDIR}/include/openssl/opensslv.h" SHLIB_VERSION
+ REGEX "^#[\t ]*define[\t ]+SHLIB_VERSION_NUMBER[\t ]+\".*\".*")
+ string(REGEX REPLACE "^.*SHLIB_VERSION_NUMBER[\t ]+\"([^\"]*)\".*$" "\\1"
+ SHLIB_VERSION "${SHLIB_VERSION}")
+ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
+ set(LIB_EXT dylib)
+ set(LIB_EXTS ${SHLIB_VERSION}.${LIB_EXT})
+ elseif(MINGW)
+ string(REPLACE "." "_" SHLIB_VERSION "${SHLIB_VERSION}")
+ set(BIN_EXT dll)
+ set(LIB_EXT dll.a)
+ else()
+ set(LIB_EXT so)
+ set(LIB_EXTS ${LIB_EXT}.${SHLIB_VERSION})
+ endif()
+ list(APPEND BIN_EXTS ${BIN_EXT})
+ list(APPEND LIB_EXTS ${LIB_EXT})
+else()
+ set(SHARED no-shared)
+ set(LIB_EXTS a)
+endif()
+foreach(lib ssl crypto)
+ foreach(ext ${LIB_EXTS})
+ list(APPEND INSTALL_LIBS "${BUILDDIR}/lib${lib}.${ext}")
+ list(APPEND INSTALL_PKG_CONFIGS "${BUILDDIR}/lib${lib}.pc")
+ endforeach()
+ foreach(ext ${BIN_EXTS})
+ # This might be wrong for targets which don't follow this naming scheme, but I'm not aware of any
+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ list(APPEND INSTALL_BINS "${BUILDDIR}/lib${lib}-${SHLIB_VERSION}-x64.${ext}")
+ else()
+ list(APPEND INSTALL_BINS "${BUILDDIR}/lib${lib}-${SHLIB_VERSION}.${ext}")
+ endif()
+ endforeach()
+endforeach()
+
+if(CMAKE_HOST_WIN32)
+ set(ENV_COMMAND set)
+ set(PATH_VAR ";%PATH%")
+else()
+ set(ENV_COMMAND export)
+ set(PATH_VAR ":$ENV{PATH}")
+endif()
+
+add_custom_command(
+ OUTPUT "${BUILDDIR}/Makefile"
+ COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}"
+ VERBATIM
+ WORKING_DIRECTORY "${BUILDDIR}"
+)
+
+if(NOT IOS)
+ add_custom_command(
+ OUTPUT "${BUILDDIR}/Makefile"
+ COMMAND ${ENV_COMMAND} CC=${CMAKE_C_COMPILER}
+ COMMAND ${ENV_COMMAND} AR=${CMAKE_AR}
+ COMMAND ${ENV_COMMAND} LD=${CMAKE_LINKER}
+ COMMAND ${ENV_COMMAND} RANLIB=${CMAKE_RANLIB}
+ COMMAND ${ENV_COMMAND} MAKE=${MAKE}
+ COMMAND ${ENV_COMMAND} MAKEDEPPROG=${CMAKE_C_COMPILER}
+ VERBATIM
+ APPEND
+ )
+
+ if(EMSCRIPTEN)
+ list(APPEND DISABLES
+ threads
+ no-engine
+ no-dso
+ no-asm
+ no-shared
+ no-sse2
+ no-srtp
+ )
+ else()
+ list(APPEND DISABLES
+ enable-static-engine
+ no-zlib
+ no-ssl2
+ no-idea
+ no-cast
+ no-seed
+ no-md2
+ no-tests)
+ endif()
+endif()
+
+if(EMSCRIPTEN)
+ add_custom_command(
+ OUTPUT "${BUILDDIR}/Makefile"
+ COMMAND "$ENV{EMSDK}/upstream/emscripten/emconfigure" ./config
+ ${SHARED}
+ ${DISABLES}
+ "--prefix=${CMAKE_INSTALL_PREFIX}"
+ "--openssldir=/etc/ssl"
+ "--cross-compile-prefix=\"/\""
+ VERBATIM
+ APPEND
+ )
+
+ add_custom_target(build_libs ALL
+ COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}"
+ COMMAND "${CMAKE_COMMAND}" -E touch "${BUILDDIR}/krb5.h"
+ COMMAND "${MAKE}" make build_libs
+ VERBATIM
+ WORKING_DIRECTORY "${BUILDDIR}"
+ DEPENDS "${BUILDDIR}/Makefile"
+ BYPRODUCTS ${INSTALL_LIBS}
+ )
+else()
+ add_custom_command(
+ OUTPUT "${BUILDDIR}/Makefile"
+ COMMAND "${PERL}" Configure
+ ${SHARED}
+ ${DISABLES}
+ ${PLATFORM}
+ "--prefix=${CMAKE_INSTALL_PREFIX}"
+ "--openssldir=/etc/ssl"
+ ${CFLAGS}
+ VERBATIM
+ APPEND
+ )
+
+ add_custom_target(build_libs ALL
+ COMMAND ${ENV_COMMAND} "PATH=${MSYS_BIN_DIR}${PATH_VAR}"
+ COMMAND "${CMAKE_COMMAND}" -E touch "${BUILDDIR}/krb5.h"
+ COMMAND "${MAKE}" -j ${VCPKG_CONCURRENCY} build_libs
+ VERBATIM
+ WORKING_DIRECTORY "${BUILDDIR}"
+ DEPENDS "${BUILDDIR}/Makefile"
+ BYPRODUCTS ${INSTALL_LIBS}
+ )
+endif()
+
+add_custom_command(
+ OUTPUT "${BUILDDIR}/Makefile"
+ COMMAND "${CMAKE_COMMAND}" "-DDIR=${BUILDDIR}" -P "${CMAKE_CURRENT_LIST_DIR}/remove-deps.cmake"
+ VERBATIM
+ APPEND
+)
+
+if((CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS") AND BUILD_SHARED_LIBS)
+ if(DEFINED CMAKE_INSTALL_NAME_DIR)
+ set(ID_PREFIX "${CMAKE_INSTALL_NAME_DIR}")
+ else()
+ set(ID_PREFIX "@rpath")
+ endif()
+
+ add_custom_command(
+ TARGET build_libs
+ COMMAND /usr/bin/install_name_tool -id "${ID_PREFIX}/libssl.${SHLIB_VERSION}.dylib"
+ "${BUILDDIR}/libssl.${SHLIB_VERSION}.dylib"
+ COMMAND /usr/bin/install_name_tool -id "${ID_PREFIX}/libcrypto.${SHLIB_VERSION}.dylib"
+ "${BUILDDIR}/libcrypto.1.1.dylib"
+ COMMAND /usr/bin/install_name_tool -change "${CMAKE_INSTALL_PREFIX}/lib/libcrypto.${SHLIB_VERSION}.dylib"
+ "${ID_PREFIX}/libcrypto.${SHLIB_VERSION}.dylib"
+ "${BUILDDIR}/libssl.${SHLIB_VERSION}.dylib"
+ VERBATIM
+ )
+endif()
+
+install(
+ FILES ${INSTALL_LIBS}
+ DESTINATION lib
+)
+install(
+ FILES ${INSTALL_BINS}
+ DESTINATION bin
+)
+install(
+ FILES ${INSTALL_PKG_CONFIGS}
+ DESTINATION lib/pkgconfig
+)
diff --git a/contrib/vcpkg-ports/openssl/unix/portfile.cmake b/contrib/vcpkg-ports/openssl/unix/portfile.cmake
new file mode 100644
index 0000000..9122349
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/unix/portfile.cmake
@@ -0,0 +1,49 @@
+if (NOT VCPKG_TARGET_IS_MINGW)
+ vcpkg_fail_port_install(MESSAGE "${PORT} is only for openssl on Unix-like systems" ON_TARGET "UWP" "Windows")
+endif()
+
+vcpkg_extract_source_archive_ex(
+ OUT_SOURCE_PATH MASTER_COPY_SOURCE_PATH
+ ARCHIVE "${ARCHIVE}"
+ REF ${OPENSSL_VERSION}
+)
+
+if(CMAKE_HOST_WIN32)
+ vcpkg_acquire_msys(MSYS_ROOT PACKAGES make perl)
+ set(MAKE ${MSYS_ROOT}/usr/bin/make.exe)
+ set(PERL ${MSYS_ROOT}/usr/bin/perl.exe)
+else()
+ find_program(MAKE make)
+ if(NOT MAKE)
+ message(FATAL_ERROR "Could not find make. Please install it through your package manager.")
+ endif()
+endif()
+
+vcpkg_configure_cmake(
+ SOURCE_PATH ${CMAKE_CURRENT_LIST_DIR}
+ PREFER_NINJA
+ OPTIONS
+ -DSOURCE_PATH=${MASTER_COPY_SOURCE_PATH}
+ -DPERL=${PERL}
+ -DMAKE=${MAKE}
+ -DVCPKG_CONCURRENCY=${VCPKG_CONCURRENCY}
+ OPTIONS_RELEASE
+ -DINSTALL_HEADERS=ON
+)
+
+vcpkg_install_cmake()
+vcpkg_fixup_pkgconfig()
+
+file(GLOB HEADERS ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/*/include/openssl/*.h)
+set(RESOLVED_HEADERS)
+foreach(HEADER ${HEADERS})
+ get_filename_component(X "${HEADER}" REALPATH)
+ list(APPEND RESOLVED_HEADERS "${X}")
+endforeach()
+
+file(INSTALL ${RESOLVED_HEADERS} DESTINATION ${CURRENT_PACKAGES_DIR}/include/openssl)
+file(INSTALL ${MASTER_COPY_SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright)
+
+if(VCPKG_LIBRARY_LINKAGE STREQUAL "static")
+ file(COPY ${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake DESTINATION ${CURRENT_PACKAGES_DIR}/share/openssl)
+endif()
diff --git a/contrib/vcpkg-ports/openssl/unix/remove-deps.cmake b/contrib/vcpkg-ports/openssl/unix/remove-deps.cmake
new file mode 100644
index 0000000..53ad6ef
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/unix/remove-deps.cmake
@@ -0,0 +1,7 @@
+file(GLOB_RECURSE MAKEFILES ${DIR}/*/Makefile)
+foreach(MAKEFILE ${MAKEFILES})
+ message("removing deps from ${MAKEFILE}")
+ file(READ "${MAKEFILE}" _contents)
+ string(REGEX REPLACE "\n# DO NOT DELETE THIS LINE.*" "" _contents "${_contents}")
+ file(WRITE "${MAKEFILE}" "${_contents}")
+endforeach()
diff --git a/contrib/vcpkg-ports/openssl/unix/vcpkg-cmake-wrapper.cmake b/contrib/vcpkg-ports/openssl/unix/vcpkg-cmake-wrapper.cmake
new file mode 100644
index 0000000..f36b687
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/unix/vcpkg-cmake-wrapper.cmake
@@ -0,0 +1,18 @@
+_find_package(${ARGS})
+if(OPENSSL_FOUND)
+ find_library(OPENSSL_DL_LIBRARY NAMES dl)
+ if(OPENSSL_DL_LIBRARY)
+ list(APPEND OPENSSL_LIBRARIES "dl")
+ if(TARGET OpenSSL::Crypto)
+ set_property(TARGET OpenSSL::Crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES "dl")
+ endif()
+ endif()
+ find_package(Threads REQUIRED)
+ list(APPEND OPENSSL_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+ if(TARGET OpenSSL::Crypto)
+ set_property(TARGET OpenSSL::Crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES "Threads::Threads")
+ endif()
+ if(TARGET OpenSSL::SSL)
+ set_property(TARGET OpenSSL::SSL APPEND PROPERTY INTERFACE_LINK_LIBRARIES "Threads::Threads")
+ endif()
+endif()
diff --git a/contrib/vcpkg-ports/openssl/usage b/contrib/vcpkg-ports/openssl/usage
new file mode 100644
index 0000000..cf83f33
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/usage
@@ -0,0 +1,4 @@
+The package openssl is compatible with built-in CMake targets:
+
+ find_package(OpenSSL REQUIRED)
+ target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto)
diff --git a/contrib/vcpkg-ports/openssl/uwp/EnableUWPSupport.patch b/contrib/vcpkg-ports/openssl/uwp/EnableUWPSupport.patch
new file mode 100644
index 0000000..fe78374
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/uwp/EnableUWPSupport.patch
@@ -0,0 +1,170 @@
+diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
+index 3c4299d264..99fcb1f713 100644
+--- a/Configurations/10-main.conf
++++ b/Configurations/10-main.conf
+@@ -1287,7 +1287,7 @@ my %targets = (
+ },
+ "VC-WIN64I" => {
+ inherit_from => [ "VC-WIN64-common", asm("ia64_asm"),
+- sub { $disabled{shared} ? () : "ia64_uplink" } ],
++ sub { $disabled{uplink} ? () : "ia64_uplink" } ],
+ AS => "ias",
+ ASFLAGS => "-d debug",
+ asoutflag => "-o ",
+@@ -1299,7 +1299,7 @@ my %targets = (
+ },
+ "VC-WIN64A" => {
+ inherit_from => [ "VC-WIN64-common", asm("x86_64_asm"),
+- sub { $disabled{shared} ? () : "x86_64_uplink" } ],
++ sub { $disabled{uplink} ? () : "x86_64_uplink" } ],
+ AS => sub { vc_win64a_info()->{AS} },
+ ASFLAGS => sub { vc_win64a_info()->{ASFLAGS} },
+ asoutflag => sub { vc_win64a_info()->{asoutflag} },
+@@ -1312,7 +1312,7 @@ my %targets = (
+ },
+ "VC-WIN32" => {
+ inherit_from => [ "VC-noCE-common", asm("x86_asm"),
+- sub { $disabled{shared} ? () : "uplink_common" } ],
++ sub { $disabled{uplink} ? () : "uplink_common" } ],
+ AS => sub { vc_win32_info()->{AS} },
+ ASFLAGS => sub { vc_win32_info()->{ASFLAGS} },
+ asoutflag => sub { vc_win32_info()->{asoutflag} },
+@@ -1374,7 +1374,7 @@ my %targets = (
+ #### MinGW
+ "mingw" => {
+ inherit_from => [ "BASE_unix", asm("x86_asm"),
+- sub { $disabled{shared} ? () : "x86_uplink" } ],
++ sub { $disabled{uplink} ? () : "x86_uplink" } ],
+ CC => "gcc",
+ CFLAGS => picker(default => "-Wall",
+ debug => "-g -O0",
+diff --git a/Configurations/50-win-onecore.conf b/Configurations/50-win-onecore.conf
+index d478f42b0f..e0fb70daca 100644
+--- a/Configurations/50-win-onecore.conf
++++ b/Configurations/50-win-onecore.conf
+@@ -1,3 +1,4 @@
++## -*- mode: perl; -*-
+ # Windows OneCore targets.
+ #
+ # OneCore is new API stability "contract" that transcends Desktop, IoT and
+@@ -10,6 +11,25 @@
+ # TODO: extend error handling to use ETW based eventing
+ # (Or rework whole error messaging)
+
++my $UWP_info = {};
++sub UWP_info {
++ unless (%$UWP_info) {
++ my $SDKver = `pwsh.exe -Command \"& {\$(Get-Item \\\"hklm:\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\\").GetValue(\\\"CurrentVersion\\\")}\"`;
++ $SDKver =~ s|\R$||;
++ my @SDKver_split = split(/\./, $SDKver);
++ # SDK version older than 10.0.17763 don't support our ASM builds
++ if ($SDKver_split[0] < 10
++ || ($SDKver_split[0] == 10
++ && $SDKver_split[1] == 0
++ && $SDKver_split[2] < 17763)) {
++ $UWP_info->{disable} = [ 'asm' ];
++ } else {
++ $UWP_info->{disable} = [ ];
++ }
++ }
++ return $UWP_info;
++}
++
+ my %targets = (
+ "VC-WIN32-ONECORE" => {
+ inherit_from => [ "VC-WIN32" ],
+@@ -61,4 +81,57 @@ my %targets = (
+ ex_libs => "onecore.lib",
+ multilib => "-arm64",
+ },
++
++ # Universal Windows Platform (UWP) App Support
++
++ # TODO
++ #
++ # The 'disable' attribute should have 'uplink'.
++ # however, these are checked in some 'inherit_from', which is processed
++ # very early, before the 'disable' attributes are seen.
++ # This is a problem that needs to be resolved in Configure first.
++ #
++ # But if you want to build library with Windows 10 Version 1809 SDK or
++ # earlier, the 'disable' attribute should also have 'asm'.
++
++ "VC-WIN32-UWP" => {
++ inherit_from => [ "VC-WIN32-ONECORE" ],
++ lflags => add("/APPCONTAINER"),
++ defines => add("WINAPI_FAMILY=WINAPI_FAMILY_APP",
++ "_WIN32_WINNT=0x0A00"),
++ dso_scheme => "",
++ disable => sub { [ 'ui-console', 'stdio', 'async', 'uplink',
++ @{ UWP_info()->{disable} } ] },
++ ex_libs => "WindowsApp.lib",
++ },
++ "VC-WIN64A-UWP" => {
++ inherit_from => [ "VC-WIN64A-ONECORE" ],
++ lflags => add("/APPCONTAINER"),
++ defines => add("WINAPI_FAMILY=WINAPI_FAMILY_APP",
++ "_WIN32_WINNT=0x0A00"),
++ dso_scheme => "",
++ disable => sub { [ 'ui-console', 'stdio', 'async', 'uplink',
++ @{ UWP_info()->{disable} } ] },
++ ex_libs => "WindowsApp.lib",
++ },
++ "VC-WIN32-ARM-UWP" => {
++ inherit_from => [ "VC-WIN32-ARM" ],
++ lflags => add("/APPCONTAINER"),
++ defines => add("WINAPI_FAMILY=WINAPI_FAMILY_APP",
++ "_WIN32_WINNT=0x0A00"),
++ dso_scheme => "",
++ disable => sub { [ 'ui-console', 'stdio', 'async', 'uplink',
++ @{ UWP_info()->{disable} } ] },
++ ex_libs => "WindowsApp.lib",
++ },
++ "VC-WIN64-ARM-UWP" => {
++ inherit_from => [ "VC-WIN64-ARM" ],
++ lflags => add("/APPCONTAINER"),
++ defines => add("WINAPI_FAMILY=WINAPI_FAMILY_APP",
++ "_WIN32_WINNT=0x0A00"),
++ dso_scheme => "",
++ disable => sub { [ 'ui-console', 'stdio', 'async', 'uplink',
++ @{ UWP_info()->{disable} } ] },
++ ex_libs => "WindowsApp.lib",
++ },
+ );
+diff --git a/Configure b/Configure
+index 5a699836f3..de45f1e299 100755
+--- a/Configure
++++ b/Configure
+@@ -407,6 +408,7 @@ my @disablables = (
+ "ubsan",
+ "ui-console",
+ "unit-test",
++ "uplink",
+ "whirlpool",
+ "weak-ssl-ciphers",
+ "zlib",
+@@ -491,8 +493,8 @@ my @disable_cascades = (
+
+ # Without position independent code, there can be no shared libraries or DSOs
+ "pic" => [ "shared" ],
+- "shared" => [ "dynamic-engine" ],
++ "shared" => [ "dynamic-engine", "uplink" ],
+ "dso" => [ "dynamic-engine" ],
+ "engine" => [ "afalgeng", "devcryptoeng" ],
+
+ # no-autoalginit is only useful when building non-shared
+diff --git a/INSTALL b/INSTALL
+index 2119cbae9e..ee54e8c215 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -560,6 +560,10 @@
+ likely to complement configuration command line with
+ suitable compiler-specific option.
+
++ no-uplink
++ Don't build support for UPLINK interface.
++
++
+ no-<prot>
+ Don't build support for negotiating the specified SSL/TLS
+ protocol (one of ssl, ssl3, tls, tls1, tls1_1, tls1_2,
diff --git a/contrib/vcpkg-ports/openssl/uwp/make-openssl.bat b/contrib/vcpkg-ports/openssl/uwp/make-openssl.bat
new file mode 100644
index 0000000..6f6166a
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/uwp/make-openssl.bat
@@ -0,0 +1,16 @@
+set build=%1
+
+perl Configure no-asm no-hw no-dso VC-WINUNIVERSAL -FS -FIWindows.h
+
+for /D %%f in ("%WindowsSdkDir%References\%WindowsSDKLibVersion%Windows.Foundation.FoundationContract\*") do set LibPath=%LibPath%;%%f\
+for /D %%f in ("%WindowsSdkDir%References\%WindowsSDKLibVersion%Windows.Foundation.UniversalApiContract\*") do set LibPath=%LibPath%;%%f\
+for /D %%f in ("%WindowsSdkDir%References\Windows.Foundation.FoundationContract\*") do set LibPath=%LibPath%;%%f\
+for /D %%f in ("%WindowsSdkDir%References\Windows.Foundation.UniversalApiContract\*") do set LibPath=%LibPath%;%%f\
+
+call ms\do_winuniversal.bat
+
+mkdir inc32\openssl
+
+jom -j %NUMBER_OF_PROCESSORS% -k -f ms\ntdll.mak
+REM due to a race condition in the build, we need to have a second single-threaded pass.
+nmake -f ms\ntdll.mak
diff --git a/contrib/vcpkg-ports/openssl/uwp/portfile.cmake b/contrib/vcpkg-ports/openssl/uwp/portfile.cmake
new file mode 100644
index 0000000..08a523c
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/uwp/portfile.cmake
@@ -0,0 +1,156 @@
+vcpkg_fail_port_install(MESSAGE "${PORT} is only for Windows Universal Platform" ON_TARGET "Linux" "OSX")
+
+vcpkg_check_linkage(ONLY_DYNAMIC_LIBRARY)
+
+vcpkg_find_acquire_program(JOM)
+get_filename_component(JOM_EXE_PATH ${JOM} DIRECTORY)
+vcpkg_add_to_path("${PERL_EXE_PATH}")
+
+vcpkg_extract_source_archive_ex(
+ OUT_SOURCE_PATH SOURCE_PATH
+ ARCHIVE ${ARCHIVE}
+ PATCHES
+ uwp/EnableUWPSupport.patch
+)
+
+vcpkg_find_acquire_program(NASM)
+get_filename_component(NASM_EXE_PATH ${NASM} DIRECTORY)
+vcpkg_add_to_path(PREPEND "${NASM_EXE_PATH}")
+
+set(CONFIGURE_COMMAND ${PERL} Configure
+ enable-static-engine
+ enable-capieng
+ no-unit-test
+ no-ssl2
+ no-asm
+ no-uplink
+ no-tests
+ -utf-8
+ shared
+)
+
+if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86")
+ set(OPENSSL_ARCH VC-WIN32-UWP)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64")
+ set(OPENSSL_ARCH VC-WIN64A-UWP)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm")
+ set(OPENSSL_ARCH VC-WIN32-ARM-UWP)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64")
+ set(OPENSSL_ARCH VC-WIN64-ARM-UWP)
+else()
+ message(FATAL_ERROR "Unsupported target architecture: ${VCPKG_TARGET_ARCHITECTURE}")
+endif()
+
+set(OPENSSL_MAKEFILE "makefile")
+
+file(REMOVE_RECURSE ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg)
+
+
+if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
+
+ # Copy openssl sources.
+ message(STATUS "Copying openssl release source files...")
+ file(GLOB OPENSSL_SOURCE_FILES "${SOURCE_PATH}/*")
+ foreach(SOURCE_FILE ${OPENSSL_SOURCE_FILES})
+ file(COPY ${SOURCE_FILE} DESTINATION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel")
+ endforeach()
+ message(STATUS "Copying openssl release source files... done")
+ set(SOURCE_PATH_RELEASE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel")
+
+ set(OPENSSLDIR_RELEASE "${CURRENT_PACKAGES_DIR}")
+
+ message(STATUS "Configure ${TARGET_TRIPLET}-rel")
+ vcpkg_execute_required_process(
+ COMMAND ${CONFIGURE_COMMAND} ${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_RELEASE}" "--openssldir=${OPENSSLDIR_RELEASE}" -FS
+ WORKING_DIRECTORY "${SOURCE_PATH_RELEASE}"
+ LOGNAME configure-perl-${TARGET_TRIPLET}-${VCPKG_BUILD_TYPE}-rel
+ )
+ message(STATUS "Configure ${TARGET_TRIPLET}-rel done")
+
+ message(STATUS "Build ${TARGET_TRIPLET}-rel")
+ # Openssl's buildsystem has a race condition which will cause JOM to fail at some point.
+ # This is ok; we just do as much work as we can in parallel first, then follow up with a single-threaded build.
+ make_directory(${SOURCE_PATH_RELEASE}/inc32/openssl)
+ execute_process(
+ COMMAND "${JOM}" -k -j ${VCPKG_CONCURRENCY} -f "${OPENSSL_MAKEFILE}" build_libs
+ WORKING_DIRECTORY "${SOURCE_PATH_RELEASE}"
+ OUTPUT_FILE "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-out.log"
+ ERROR_FILE "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-err.log"
+ )
+ vcpkg_execute_required_process(
+ COMMAND nmake -f "${OPENSSL_MAKEFILE}" install_dev
+ WORKING_DIRECTORY "${SOURCE_PATH_RELEASE}"
+ LOGNAME build-${TARGET_TRIPLET}-rel-1)
+
+ message(STATUS "Build ${TARGET_TRIPLET}-rel done")
+endif()
+
+
+if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
+ # Copy openssl sources.
+ message(STATUS "Copying openssl debug source files...")
+ file(GLOB OPENSSL_SOURCE_FILES ${SOURCE_PATH}/*)
+ foreach(SOURCE_FILE ${OPENSSL_SOURCE_FILES})
+ file(COPY "${SOURCE_FILE}" DESTINATION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+ endforeach()
+ message(STATUS "Copying openssl debug source files... done")
+ set(SOURCE_PATH_DEBUG "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+
+ set(OPENSSLDIR_DEBUG "${CURRENT_PACKAGES_DIR}/debug")
+
+ message(STATUS "Configure ${TARGET_TRIPLET}-dbg")
+ vcpkg_execute_required_process(
+ COMMAND ${CONFIGURE_COMMAND} debug-${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_DEBUG}" "--openssldir=${OPENSSLDIR_DEBUG}" -FS
+ WORKING_DIRECTORY "${SOURCE_PATH_DEBUG}"
+ LOGNAME configure-perl-${TARGET_TRIPLET}-${VCPKG_BUILD_TYPE}-dbg
+ )
+ message(STATUS "Configure ${TARGET_TRIPLET}-dbg done")
+
+ message(STATUS "Build ${TARGET_TRIPLET}-dbg")
+ make_directory("${SOURCE_PATH_DEBUG}/inc32/openssl")
+ execute_process(
+ COMMAND "${JOM}" -k -j ${VCPKG_CONCURRENCY} -f "${OPENSSL_MAKEFILE}" build_libs
+ WORKING_DIRECTORY "${SOURCE_PATH_DEBUG}"
+ OUTPUT_FILE "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-out.log"
+ ERROR_FILE "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-err.log"
+ )
+ vcpkg_execute_required_process(
+ COMMAND nmake -f "${OPENSSL_MAKEFILE}" install_dev
+ WORKING_DIRECTORY "${SOURCE_PATH_DEBUG}"
+ LOGNAME build-${TARGET_TRIPLET}-dbg-1)
+
+ message(STATUS "Build ${TARGET_TRIPLET}-dbg done")
+endif()
+
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/certs")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/private")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/engines-1_1")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/certs")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/engines-1_1")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/private")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
+
+file(REMOVE
+ "${CURRENT_PACKAGES_DIR}/bin/openssl.exe"
+ "${CURRENT_PACKAGES_DIR}/debug/bin/openssl.exe"
+ "${CURRENT_PACKAGES_DIR}/debug/openssl.cnf"
+ "${CURRENT_PACKAGES_DIR}/openssl.cnf"
+ "${CURRENT_PACKAGES_DIR}/ct_log_list.cnf"
+ "${CURRENT_PACKAGES_DIR}/ct_log_list.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/openssl.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/debug/ct_log_list.cnf"
+ "${CURRENT_PACKAGES_DIR}/debug/ct_log_list.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/debug/openssl.cnf.dist"
+)
+
+file(READ "${CURRENT_PACKAGES_DIR}/include/openssl/dtls1.h" _contents)
+string(REPLACE "<winsock.h>" "<winsock2.h>" _contents "${_contents}")
+file(WRITE "${CURRENT_PACKAGES_DIR}/include/openssl/dtls1.h" "${_contents}")
+
+file(READ "${CURRENT_PACKAGES_DIR}/include/openssl/rand.h" _contents)
+string(REPLACE "# include <windows.h>" "#ifndef _WINSOCKAPI_\n#define _WINSOCKAPI_\n#endif\n# include <windows.h>" _contents "${_contents}")
+file(WRITE "${CURRENT_PACKAGES_DIR}/include/openssl/rand.h" "${_contents}")
+
+vcpkg_copy_pdbs()
+
+file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
diff --git a/contrib/vcpkg-ports/openssl/vcpkg.json b/contrib/vcpkg-ports/openssl/vcpkg.json
new file mode 100644
index 0000000..2d0eb13
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/vcpkg.json
@@ -0,0 +1,7 @@
+{
+ "name": "openssl",
+ "version-string": "1.1.1k",
+ "port-version": 4,
+ "description": "OpenSSL is an open source project that provides a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. It is also a general-purpose cryptography library.",
+ "homepage": "https://www.openssl.org"
+}
diff --git a/contrib/vcpkg-ports/openssl/windows/portfile.cmake b/contrib/vcpkg-ports/openssl/windows/portfile.cmake
new file mode 100644
index 0000000..c873eb7
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/windows/portfile.cmake
@@ -0,0 +1,174 @@
+vcpkg_fail_port_install(MESSAGE "${PORT} is only for Windows Desktop" ON_TARGET "UWP" "Linux" "OSX")
+
+vcpkg_extract_source_archive_ex(
+ OUT_SOURCE_PATH SOURCE_PATH
+ ARCHIVE ${ARCHIVE}
+)
+
+vcpkg_find_acquire_program(NASM)
+get_filename_component(NASM_EXE_PATH "${NASM}" DIRECTORY)
+vcpkg_add_to_path(PREPEND "${NASM_EXE_PATH}")
+
+vcpkg_find_acquire_program(JOM)
+
+set(OPENSSL_SHARED no-shared)
+if(VCPKG_LIBRARY_LINKAGE STREQUAL dynamic)
+ set(OPENSSL_SHARED shared)
+endif()
+
+set(CONFIGURE_OPTIONS
+ enable-static-engine
+ enable-capieng
+ no-ssl2
+ no-tests
+ no-autoload-config
+ -utf-8
+ ${OPENSSL_SHARED}
+)
+
+if(DEFINED OPENSSL_USE_NOPINSHARED)
+ set(CONFIGURE_OPTIONS ${CONFIGURE_OPTIONS} no-pinshared)
+endif()
+
+set(CONFIGURE_COMMAND "${PERL}" Configure ${CONFIGURE_OPTIONS})
+
+if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86")
+ set(OPENSSL_ARCH VC-WIN32)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64")
+ set(OPENSSL_ARCH VC-WIN64A)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm")
+ set(OPENSSL_ARCH VC-WIN32-ARM)
+elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64")
+ set(OPENSSL_ARCH VC-WIN64-ARM)
+else()
+ message(FATAL_ERROR "Unsupported target architecture: ${VCPKG_TARGET_ARCHITECTURE}")
+endif()
+
+set(OPENSSL_MAKEFILE "makefile")
+
+file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel"
+ "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+
+if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release")
+
+ # Copy openssl sources.
+ message(STATUS "Copying openssl release source files...")
+ file(GLOB OPENSSL_SOURCE_FILES ${SOURCE_PATH}/*)
+ foreach(SOURCE_FILE ${OPENSSL_SOURCE_FILES})
+ file(COPY ${SOURCE_FILE} DESTINATION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel")
+ endforeach()
+ message(STATUS "Copying openssl release source files... done")
+ set(SOURCE_PATH_RELEASE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel")
+
+ set(OPENSSLDIR_RELEASE ${CURRENT_PACKAGES_DIR})
+
+ message(STATUS "Configure ${TARGET_TRIPLET}-rel")
+ vcpkg_execute_required_process(
+ COMMAND ${CONFIGURE_COMMAND} ${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_RELEASE}" "--openssldir=${OPENSSLDIR_RELEASE}" -FS
+ WORKING_DIRECTORY ${SOURCE_PATH_RELEASE}
+ LOGNAME configure-perl-${TARGET_TRIPLET}-rel
+ )
+ message(STATUS "Configure ${TARGET_TRIPLET}-rel done")
+
+ message(STATUS "Build ${TARGET_TRIPLET}-rel")
+ # Openssl's buildsystem has a race condition which will cause JOM to fail at some point.
+ # This is ok; we just do as much work as we can in parallel first, then follow up with a single-threaded build.
+ make_directory(${SOURCE_PATH_RELEASE}/inc32/openssl)
+ execute_process(
+ COMMAND ${JOM} -k -j $ENV{NUMBER_OF_PROCESSORS} -f ${OPENSSL_MAKEFILE}
+ WORKING_DIRECTORY ${SOURCE_PATH_RELEASE}
+ OUTPUT_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-out.log
+ ERROR_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-0-err.log
+ )
+ vcpkg_execute_required_process(
+ COMMAND nmake -f ${OPENSSL_MAKEFILE} install_sw install_ssldirs
+ WORKING_DIRECTORY ${SOURCE_PATH_RELEASE}
+ LOGNAME build-${TARGET_TRIPLET}-rel-1)
+
+ message(STATUS "Build ${TARGET_TRIPLET}-rel done")
+endif()
+
+
+if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug")
+ # Copy openssl sources.
+ message(STATUS "Copying openssl debug source files...")
+ file(GLOB OPENSSL_SOURCE_FILES ${SOURCE_PATH}/*)
+ foreach(SOURCE_FILE ${OPENSSL_SOURCE_FILES})
+ file(COPY ${SOURCE_FILE} DESTINATION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+ endforeach()
+ message(STATUS "Copying openssl debug source files... done")
+ set(SOURCE_PATH_DEBUG "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg")
+
+ set(OPENSSLDIR_DEBUG ${CURRENT_PACKAGES_DIR}/debug)
+
+ message(STATUS "Configure ${TARGET_TRIPLET}-dbg")
+ vcpkg_execute_required_process(
+ COMMAND ${CONFIGURE_COMMAND} debug-${OPENSSL_ARCH} "--prefix=${OPENSSLDIR_DEBUG}" "--openssldir=${OPENSSLDIR_DEBUG}" -FS
+ WORKING_DIRECTORY ${SOURCE_PATH_DEBUG}
+ LOGNAME configure-perl-${TARGET_TRIPLET}-dbg
+ )
+ message(STATUS "Configure ${TARGET_TRIPLET}-dbg done")
+
+ message(STATUS "Build ${TARGET_TRIPLET}-dbg")
+ make_directory(${SOURCE_PATH_DEBUG}/inc32/openssl)
+ execute_process(
+ COMMAND "${JOM}" -k -j ${VCPKG_CONCURRENCY} -f "${OPENSSL_MAKEFILE}"
+ WORKING_DIRECTORY ${SOURCE_PATH_DEBUG}
+ OUTPUT_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-out.log
+ ERROR_FILE ${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-dbg-0-err.log
+ )
+ vcpkg_execute_required_process(
+ COMMAND nmake -f "${OPENSSL_MAKEFILE}" install_sw install_ssldirs
+ WORKING_DIRECTORY ${SOURCE_PATH_DEBUG}
+ LOGNAME build-${TARGET_TRIPLET}-dbg-1)
+
+ message(STATUS "Build ${TARGET_TRIPLET}-dbg done")
+endif()
+
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/certs")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/private")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/engines-1_1")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/certs")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/engines-1_1")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/private")
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
+
+file(REMOVE
+ "${CURRENT_PACKAGES_DIR}/ct_log_list.cnf"
+ "${CURRENT_PACKAGES_DIR}/ct_log_list.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/openssl.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/debug/bin/openssl.exe"
+ "${CURRENT_PACKAGES_DIR}/debug/ct_log_list.cnf"
+ "${CURRENT_PACKAGES_DIR}/debug/ct_log_list.cnf.dist"
+ "${CURRENT_PACKAGES_DIR}/debug/openssl.cnf"
+ "${CURRENT_PACKAGES_DIR}/debug/openssl.cnf.dist"
+)
+
+file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/tools/openssl/")
+file(RENAME "${CURRENT_PACKAGES_DIR}/bin/openssl.exe" "${CURRENT_PACKAGES_DIR}/tools/openssl/openssl.exe")
+file(RENAME "${CURRENT_PACKAGES_DIR}/openssl.cnf" "${CURRENT_PACKAGES_DIR}/tools/openssl/openssl.cnf")
+
+vcpkg_copy_tool_dependencies("${CURRENT_PACKAGES_DIR}/tools/openssl")
+
+if(VCPKG_LIBRARY_LINKAGE STREQUAL static)
+ # They should be empty, only the exes deleted above were in these directories
+ file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/bin/")
+ file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin/")
+endif()
+
+vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/openssl/dtls1.h"
+ "<winsock.h>"
+ "<winsock2.h>"
+)
+
+vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/openssl/rand.h"
+ "# include <windows.h>"
+ "#ifndef _WINSOCKAPI_\n#define _WINSOCKAPI_\n#endif\n# include <windows.h>"
+)
+
+vcpkg_copy_pdbs()
+
+file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
+if(VCPKG_LIBRARY_LINKAGE STREQUAL "static")
+ file(COPY "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" DESTINATION "${CURRENT_PACKAGES_DIR}/share/openssl")
+endif()
diff --git a/contrib/vcpkg-ports/openssl/windows/vcpkg-cmake-wrapper.cmake b/contrib/vcpkg-ports/openssl/windows/vcpkg-cmake-wrapper.cmake
new file mode 100644
index 0000000..1e3b837
--- /dev/null
+++ b/contrib/vcpkg-ports/openssl/windows/vcpkg-cmake-wrapper.cmake
@@ -0,0 +1,10 @@
+_find_package(${ARGS})
+if(OPENSSL_FOUND)
+ list(APPEND OPENSSL_LIBRARIES Crypt32.lib ws2_32.lib)
+ if(TARGET OpenSSL::Crypto)
+ set_property(TARGET OpenSSL::Crypto APPEND PROPERTY INTERFACE_LINK_LIBRARIES "Crypt32.lib;ws2_32.lib")
+ endif()
+ if(TARGET OpenSSL::SSL)
+ set_property(TARGET OpenSSL::SSL APPEND PROPERTY INTERFACE_LINK_LIBRARIES "Crypt32.lib;ws2_32.lib")
+ endif()
+endif()
diff --git a/contrib/vcpkg-ports/pkcs11-helper/0001-nmake-openssl-1.1.1-support.patch b/contrib/vcpkg-ports/pkcs11-helper/0001-nmake-openssl-1.1.1-support.patch
new file mode 100644
index 0000000..d1942a9
--- /dev/null
+++ b/contrib/vcpkg-ports/pkcs11-helper/0001-nmake-openssl-1.1.1-support.patch
@@ -0,0 +1,88 @@
+From 324026ce179468fcea348e59259dbc5456438ead Mon Sep 17 00:00:00 2001
+From: Lev Stipakov <lev@openvpn.net>
+Date: Fri, 14 May 2021 14:35:53 +0300
+Subject: [PATCH] nmake: openssl 1.1.1 support
+
+Starting from version 1.1.1, OpenSSL includes routines
+like RSA_meth_xxx and DSA_meth_xxx. pkcs11-helper includes
+implementation of those routines. That code is compiled if
+they're missing from OpenSSL.
+
+nmake build uses pre-generated config-w32-vc.h, which lacks
+defines which indicate that OpenSSL includes above routines,
+which causes pkcs11's own implementaion to be compiled. However,
+pkcs11-helper implementation is not compatible with OpenSSL 1.1.1 -
+for example, it takes size of opaque struct RSA_METHOD, which
+has become internal in OpenSSL.
+
+This adds necessary defines to config header used by nmake build
+so that pkcs11-helper code, which is not compatible with OpenSSL 1.1.1,
+is not compiled.
+
+Also libeay is changed to libcrypto.
+
+Signed-off-by: Lev Stipakov <lev@openvpn.net>
+---
+ config-w32-vc.h.in | 33 +++++++++++++++++++++++++++++++++
+ lib/Makefile.w32-vc | 4 ++--
+ 2 files changed, 35 insertions(+), 2 deletions(-)
+
+diff --git a/config-w32-vc.h b/config-w32-vc.h
+index 6346f02..102b2e3 100644
+--- a/config-w32-vc.h
++++ b/config-w32-vc.h
+@@ -185,3 +185,36 @@
+ #if _MSC_VER >= 1400
+ #define HAVE_CPP_VARARG_MACRO_ISO 1
+ #endif
++
++/* Define to 1 if you have the `RSA_meth_dup' function. */
++#define HAVE_RSA_METH_DUP 1
++
++/* Define to 1 if you have the `RSA_meth_free' function. */
++#define HAVE_RSA_METH_FREE 1
++
++/* Define to 1 if you have the `RSA_meth_set1_name' function. */
++#define HAVE_RSA_METH_SET1_NAME 1
++
++/* Define to 1 if you have the `RSA_meth_set_flags' function. */
++#define HAVE_RSA_METH_SET_FLAGS 1
++
++/* Define to 1 if you have the `RSA_meth_set_priv_dec' function. */
++#define HAVE_RSA_METH_SET_PRIV_DEC 1
++
++/* Define to 1 if you have the `RSA_meth_set_priv_enc' function. */
++#define HAVE_RSA_METH_SET_PRIV_ENC 1
++
++/* Define to 1 if you have the `DSA_meth_dup' function. */
++#define HAVE_DSA_METH_DUP 1
++
++/* Define to 1 if you have the `DSA_meth_free' function. */
++#define HAVE_DSA_METH_FREE 1
++
++/* Define to 1 if you have the `DSA_meth_set1_name' function. */
++#define HAVE_DSA_METH_SET1_NAME 1
++
++/* Define to 1 if you have the `DSA_meth_set_sign' function. */
++#define HAVE_DSA_METH_SET_SIGN 1
++
++/* Define to 1 if you have the `DSA_SIG_set0' function. */
++#define HAVE_DSA_SIG_SET0 1
+diff --git a/lib/Makefile.w32-vc b/lib/Makefile.w32-vc
+index 2edab39..b2ac746 100644
+--- a/lib/Makefile.w32-vc
++++ b/lib/Makefile.w32-vc
+@@ -60,9 +60,9 @@ OPENSSL_HOME = ..\..\openssl-0.9.8a
+ !endif
+
+ !ifdef OPENSSL
+-OPENSSL_STATIC = libeay32.lib
++OPENSSL_STATIC = libcrypto.lib
+ #OPENSSL_STATIC = libeay32sd.lib
+-OPENSSL_DYNAMIC = libeay32.lib
++OPENSSL_DYNAMIC = libcrypto.lib
+ #OPENSSL_DYNAMIC = libeay32d.lib
+
+ OPENSSL_INC=$(OPENSSL_HOME)\include
+--
+2.23.0.windows.1
diff --git a/contrib/vcpkg-ports/pkcs11-helper/CONTROL b/contrib/vcpkg-ports/pkcs11-helper/CONTROL
new file mode 100644
index 0000000..0183180
--- /dev/null
+++ b/contrib/vcpkg-ports/pkcs11-helper/CONTROL
@@ -0,0 +1,4 @@
+Source: pkcs11-helper
+Version: 1.27-1
+Homepage: https://github.com/OpenSC/pkcs11-helper
+Description: pkcs11-helper is a library that simplifies the interaction with PKCS#11 providers for end-user applications.
diff --git a/contrib/vcpkg-ports/pkcs11-helper/pkcs11-helper-001-RFC7512.patch b/contrib/vcpkg-ports/pkcs11-helper/pkcs11-helper-001-RFC7512.patch
new file mode 100644
index 0000000..84fba08
--- /dev/null
+++ b/contrib/vcpkg-ports/pkcs11-helper/pkcs11-helper-001-RFC7512.patch
@@ -0,0 +1,686 @@
+commit 90590b02085edc3830bdfe0942a46c4e7bf3f1ab (HEAD -> master)
+Author: David Woodhouse <David.Woodhouse@intel.com>
+Date: Thu Apr 30 14:58:24 2015 +0100
+
+ Serialize to RFC7512-compliant PKCS#11 URIs
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+
+commit 4d5280da8df591aab701dff4493d13a835a9b29c
+Author: David Woodhouse <David.Woodhouse@intel.com>
+Date: Wed Dec 10 14:00:21 2014 +0000
+
+ Accept RFC7512-compliant PKCS#11 URIs as serialized token/certificate IDs
+
+ The old format is still accepted for compatibility.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+
+commit 14e09211c3d50eb06825090c9765e4382cf52f19
+Author: David Woodhouse <David.Woodhouse@intel.com>
+Date: Sun Dec 14 19:42:18 2014 +0000
+
+ Stop _pkcs11h_util_hexToBinary() checking for trailing NUL
+
+ We are going to want to use this for parsing %XX hex escapes in RFC7512
+ PKCS#11 URIs, where we cannot expect a trailing NUL. Since there's only
+ one existing caller at the moment, it's simple just to let the caller
+ have responsibility for that check.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+diff --git a/lib/pkcs11h-serialization.c b/lib/pkcs11h-serialization.c
+index ad275f8..1d077e4 100644
+--- a/lib/pkcs11h-serialization.c
++++ b/lib/pkcs11h-serialization.c
+@@ -61,29 +61,127 @@
+
+ #if defined(ENABLE_PKCS11H_TOKEN) || defined(ENABLE_PKCS11H_CERTIFICATE)
+
++#define URI_SCHEME "pkcs11:"
++
++#define token_field_ofs(field) ((unsigned long)&(((struct pkcs11h_token_id_s *)0)->field))
++#define token_field_size(field) sizeof((((struct pkcs11h_token_id_s *)0)->field))
++#define token_field(name, field) { name "=", sizeof(name), \
++ token_field_ofs(field), token_field_size(field) }
++
++static struct {
++ const char const *name;
++ size_t namelen;
++ unsigned long field_ofs;
++ size_t field_size;
++} __token_fields[] = {
++ token_field ("model", model),
++ token_field ("token", label),
++ token_field ("manufacturer", manufacturerID ),
++ token_field ("serial", serialNumber ),
++ { NULL },
++};
++
++#define P11_URL_VERBATIM "abcdefghijklmnopqrstuvwxyz" \
++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
++ "0123456789_-."
++
++static
++int
++__token_attr_escape(char *uri, char *attr, size_t attrlen)
++{
++ int len = 0, i;
++
++ for (i = 0; i < attrlen; i++) {
++ if ((attr[i] != '\x0') && strchr(P11_URL_VERBATIM, attr[i])) {
++ if (uri) {
++ *(uri++) = attr[i];
++ }
++ len++;
++ } else {
++ if (uri) {
++ sprintf(uri, "%%%02x", (unsigned char)attr[i]);
++ uri += 3;
++ }
++ len += 3;
++ }
++ }
++ return len;
++}
++
++static
++CK_RV
++__generate_pkcs11_uri (
++ OUT char * const sz,
++ IN OUT size_t *max,
++ IN const pkcs11h_certificate_id_t certificate_id,
++ IN const pkcs11h_token_id_t token_id
++) {
++ size_t _max;
++ char *p = sz;
++ int i;
++
++ _PKCS11H_ASSERT (max!=NULL);
++ _PKCS11H_ASSERT (token_id!=NULL);
++
++ _max = strlen(URI_SCHEME);
++ for (i = 0; __token_fields[i].name; i++) {
++ char *field = ((char *)token_id) + __token_fields[i].field_ofs;
++
++ _max += __token_fields[i].namelen;
++ _max += __token_attr_escape (NULL, field, strlen(field));
++ _max++; /* For a semicolon or trailing NUL */
++ }
++ if (certificate_id) {
++ _max += strlen (";id=");
++ _max += __token_attr_escape (NULL,
++ (char *)certificate_id->attrCKA_ID,
++ certificate_id->attrCKA_ID_size);
++ }
++
++ if (!sz) {
++ *max = _max;
++ return CKR_OK;
++ }
++
++ if (sz && *max < _max)
++ return CKR_ATTRIBUTE_VALUE_INVALID;
++
++ p += sprintf(p, URI_SCHEME);
++ for (i = 0; __token_fields[i].name; i++) {
++ char *field = ((char *)token_id) + __token_fields[i].field_ofs;
++
++ p += sprintf (p, "%s", __token_fields[i].name);
++ p += __token_attr_escape (p, field, strlen(field));
++ *(p++) = ';';
++ }
++ if (certificate_id) {
++ p += sprintf (p, "id=");
++ p += __token_attr_escape (p,
++ (char *)certificate_id->attrCKA_ID,
++ certificate_id->attrCKA_ID_size);
++ } else {
++ /* Remove the unneeded trailing semicolon */
++ p--;
++ }
++ *(p++) = 0;
++
++ *max = _max;
++
++ return CKR_OK;
++}
++
+ CK_RV
+ pkcs11h_token_serializeTokenId (
+ OUT char * const sz,
+ IN OUT size_t *max,
+ IN const pkcs11h_token_id_t token_id
+ ) {
+- const char *sources[5];
+ CK_RV rv = CKR_FUNCTION_FAILED;
+- size_t n;
+- int e;
+
+ /*_PKCS11H_ASSERT (sz!=NULL); Not required*/
+ _PKCS11H_ASSERT (max!=NULL);
+ _PKCS11H_ASSERT (token_id!=NULL);
+
+- { /* Must be after assert */
+- sources[0] = token_id->manufacturerID;
+- sources[1] = token_id->model;
+- sources[2] = token_id->serialNumber;
+- sources[3] = token_id->label;
+- sources[4] = NULL;
+- }
+-
+ _PKCS11H_DEBUG (
+ PKCS11H_LOG_DEBUG2,
+ "PKCS#11: pkcs11h_token_serializeTokenId entry sz=%p, *max="P_Z", token_id=%p",
+@@ -92,67 +190,161 @@ pkcs11h_token_serializeTokenId (
+ (void *)token_id
+ );
+
+- n = 0;
+- for (e=0;sources[e] != NULL;e++) {
+- size_t t;
+- if (
+- (rv = _pkcs11h_util_escapeString (
+- NULL,
+- sources[e],
+- &t,
+- __PKCS11H_SERIALIZE_INVALID_CHARS
+- )) != CKR_OK
+- ) {
+- goto cleanup;
++ rv = __generate_pkcs11_uri(sz, max, NULL, token_id);
++
++ _PKCS11H_DEBUG (
++ PKCS11H_LOG_DEBUG2,
++ "PKCS#11: pkcs11h_token_serializeTokenId return rv=%lu-'%s', *max="P_Z", sz='%s'",
++ rv,
++ pkcs11h_getMessage (rv),
++ *max,
++ sz
++ );
++
++ return rv;
++}
++
++static
++CK_RV
++__parse_token_uri_attr (
++ const char *uri,
++ size_t urilen,
++ char *tokstr,
++ size_t toklen,
++ size_t *parsed_len
++) {
++ size_t orig_toklen = toklen;
++ CK_RV rv = CKR_OK;
++
++ while (urilen && toklen > 1) {
++ if (*uri == '%') {
++ size_t size = 1;
++
++ if (urilen < 3) {
++ rv = CKR_ATTRIBUTE_VALUE_INVALID;
++ goto done;
++ }
++
++ rv = _pkcs11h_util_hexToBinary ((unsigned char *)tokstr,
++ uri + 1, &size);
++ if (rv != CKR_OK) {
++ goto done;
++ }
++
++ uri += 2;
++ urilen -= 2;
++ } else {
++ *tokstr = *uri;
+ }
+- n+=t;
++ tokstr++;
++ uri++;
++ toklen--;
++ urilen--;
++ tokstr[0] = 0;
+ }
+
+- if (sz != NULL) {
+- if (*max < n) {
+- rv = CKR_ATTRIBUTE_VALUE_INVALID;
+- goto cleanup;
++ if (urilen) {
++ rv = CKR_ATTRIBUTE_VALUE_INVALID;
++ } else if (parsed_len) {
++ *parsed_len = orig_toklen - toklen;
++ }
++
++ done:
++ return rv;
++}
++
++static
++CK_RV
++__parse_pkcs11_uri (
++ OUT pkcs11h_token_id_t token_id,
++ OUT pkcs11h_certificate_id_t certificate_id,
++ IN const char * const sz
++) {
++ const char *end, *p;
++ CK_RV rv = CKR_OK;
++
++ _PKCS11H_ASSERT (token_id!=NULL);
++ _PKCS11H_ASSERT (sz!=NULL);
++
++ if (strncmp (sz, URI_SCHEME, strlen (URI_SCHEME)))
++ return CKR_ATTRIBUTE_VALUE_INVALID;
++
++ end = sz + strlen (URI_SCHEME) - 1;
++ while (rv == CKR_OK && end[0] && end[1]) {
++ int i;
++
++ p = end + 1;
++ end = strchr (p, ';');
++ if (!end)
++ end = p + strlen(p);
++
++ for (i = 0; __token_fields[i].name; i++) {
++ /* Parse the token=, label=, manufacturer= and serial= fields */
++ if (!strncmp(p, __token_fields[i].name, __token_fields[i].namelen)) {
++ char *field = ((char *)token_id) + __token_fields[i].field_ofs;
++
++ p += __token_fields[i].namelen;
++ rv = __parse_token_uri_attr (p, end - p, field,
++ __token_fields[i].field_size,
++ NULL);
++ if (rv != CKR_OK) {
++ goto cleanup;
++ }
++
++ goto matched;
++ }
+ }
++ if (certificate_id && !strncmp(p, "id=", 3)) {
++ p += 3;
++
++ rv = _pkcs11h_mem_malloc ((void *)&certificate_id->attrCKA_ID,
++ end - p + 1);
++ if (rv != CKR_OK) {
++ goto cleanup;
++ }
+
+- n = 0;
+- for (e=0;sources[e] != NULL;e++) {
+- size_t t = *max-n;
+- if (
+- (rv = _pkcs11h_util_escapeString (
+- sz+n,
+- sources[e],
+- &t,
+- __PKCS11H_SERIALIZE_INVALID_CHARS
+- )) != CKR_OK
+- ) {
++ rv = __parse_token_uri_attr (p, end - p,
++ (char *)certificate_id->attrCKA_ID,
++ end - p + 1,
++ &certificate_id->attrCKA_ID_size);
++ if (rv != CKR_OK) {
+ goto cleanup;
+ }
+- n+=t;
+- sz[n-1] = '/';
++
++ goto matched;
+ }
+- sz[n-1] = '\x0';
+- }
+
+- *max = n;
+- rv = CKR_OK;
++ /* We don't parse object= because the match code doesn't support
++ matching by label. */
++
++ /* Failed to parse PKCS#11 URI element. */
++ return CKR_ATTRIBUTE_VALUE_INVALID;
+
++ matched:
++ ;
++ }
+ cleanup:
++ /* The matching code doesn't support support partial matches; it needs
++ * *all* of manufacturer, model, serial and label attributes to be
++ * defined. So reject partial URIs early instead of letting it do the
++ * wrong thing. We can maybe improve this later. */
++ if (!token_id->model[0] || !token_id->label[0] ||
++ !token_id->manufacturerID[0] || !token_id->serialNumber[0]) {
++ return CKR_ATTRIBUTE_VALUE_INVALID;
++ }
+
+- _PKCS11H_DEBUG (
+- PKCS11H_LOG_DEBUG2,
+- "PKCS#11: pkcs11h_token_serializeTokenId return rv=%lu-'%s', *max="P_Z", sz='%s'",
+- rv,
+- pkcs11h_getMessage (rv),
+- *max,
+- sz
+- );
++ /* For a certificate ID we need CKA_ID */
++ if (certificate_id && !certificate_id->attrCKA_ID_size) {
++ return CKR_ATTRIBUTE_VALUE_INVALID;
++ }
+
+ return rv;
+ }
+
++static
+ CK_RV
+-pkcs11h_token_deserializeTokenId (
+- OUT pkcs11h_token_id_t *p_token_id,
++__pkcs11h_token_legacy_deserializeTokenId (
++ OUT pkcs11h_token_id_t token_id,
+ IN const char * const sz
+ ) {
+ #define __PKCS11H_TARGETS_NUMBER 4
+@@ -161,24 +353,11 @@ pkcs11h_token_deserializeTokenId (
+ size_t s;
+ } targets[__PKCS11H_TARGETS_NUMBER];
+
+- pkcs11h_token_id_t token_id = NULL;
+ char *p1 = NULL;
+ char *_sz = NULL;
+ int e;
+ CK_RV rv = CKR_FUNCTION_FAILED;
+
+- _PKCS11H_ASSERT (p_token_id!=NULL);
+- _PKCS11H_ASSERT (sz!=NULL);
+-
+- _PKCS11H_DEBUG (
+- PKCS11H_LOG_DEBUG2,
+- "PKCS#11: pkcs11h_token_deserializeTokenId entry p_token_id=%p, sz='%s'",
+- (void *)p_token_id,
+- sz
+- );
+-
+- *p_token_id = NULL;
+-
+ if (
+ (rv = _pkcs11h_mem_strdup (
+ (void *)&_sz,
+@@ -190,10 +369,6 @@ pkcs11h_token_deserializeTokenId (
+
+ p1 = _sz;
+
+- if ((rv = _pkcs11h_token_newTokenId (&token_id)) != CKR_OK) {
+- goto cleanup;
+- }
+-
+ targets[0].p = token_id->manufacturerID;
+ targets[0].s = sizeof (token_id->manufacturerID);
+ targets[1].p = token_id->model;
+@@ -252,6 +427,51 @@ pkcs11h_token_deserializeTokenId (
+ p1 = p2+1;
+ }
+
++ rv = CKR_OK;
++
++cleanup:
++
++ if (_sz != NULL) {
++ _pkcs11h_mem_free ((void *)&_sz);
++ }
++
++ return rv;
++#undef __PKCS11H_TARGETS_NUMBER
++}
++
++CK_RV
++pkcs11h_token_deserializeTokenId (
++ OUT pkcs11h_token_id_t *p_token_id,
++ IN const char * const sz
++) {
++ pkcs11h_token_id_t token_id = NULL;
++ CK_RV rv = CKR_FUNCTION_FAILED;
++
++ _PKCS11H_ASSERT (p_token_id!=NULL);
++ _PKCS11H_ASSERT (sz!=NULL);
++
++ _PKCS11H_DEBUG (
++ PKCS11H_LOG_DEBUG2,
++ "PKCS#11: pkcs11h_token_deserializeTokenId entry p_token_id=%p, sz='%s'",
++ (void *)p_token_id,
++ sz
++ );
++
++ *p_token_id = NULL;
++
++ if ((rv = _pkcs11h_token_newTokenId (&token_id)) != CKR_OK) {
++ goto cleanup;
++ }
++
++ if (!strncmp (sz, URI_SCHEME, strlen (URI_SCHEME))) {
++ rv = __parse_pkcs11_uri(token_id, NULL, sz);
++ } else {
++ rv = __pkcs11h_token_legacy_deserializeTokenId(token_id, sz);
++ }
++ if (rv != CKR_OK) {
++ goto cleanup;
++ }
++
+ strncpy (
+ token_id->display,
+ token_id->label,
+@@ -264,11 +484,6 @@ pkcs11h_token_deserializeTokenId (
+ rv = CKR_OK;
+
+ cleanup:
+-
+- if (_sz != NULL) {
+- _pkcs11h_mem_free ((void *)&_sz);
+- }
+-
+ if (token_id != NULL) {
+ pkcs11h_token_freeTokenId (token_id);
+ }
+@@ -281,7 +496,6 @@ cleanup:
+ );
+
+ return rv;
+-#undef __PKCS11H_TARGETS_NUMBER
+ }
+
+ #endif /* ENABLE_PKCS11H_TOKEN || ENABLE_PKCS11H_CERTIFICATE */
+@@ -295,9 +509,6 @@ pkcs11h_certificate_serializeCertificateId (
+ IN const pkcs11h_certificate_id_t certificate_id
+ ) {
+ CK_RV rv = CKR_FUNCTION_FAILED;
+- size_t saved_max = 0;
+- size_t n = 0;
+- size_t _max = 0;
+
+ /*_PKCS11H_ASSERT (sz!=NULL); Not required */
+ _PKCS11H_ASSERT (max!=NULL);
+@@ -311,42 +522,7 @@ pkcs11h_certificate_serializeCertificateId (
+ (void *)certificate_id
+ );
+
+- if (sz != NULL) {
+- saved_max = n = *max;
+- }
+- *max = 0;
+-
+- if (
+- (rv = pkcs11h_token_serializeTokenId (
+- sz,
+- &n,
+- certificate_id->token_id
+- )) != CKR_OK
+- ) {
+- goto cleanup;
+- }
+-
+- _max = n + certificate_id->attrCKA_ID_size*2 + 1;
+-
+- if (sz != NULL) {
+- if (saved_max < _max) {
+- rv = CKR_ATTRIBUTE_VALUE_INVALID;
+- goto cleanup;
+- }
+-
+- sz[n-1] = '/';
+- rv = _pkcs11h_util_binaryToHex (
+- sz+n,
+- saved_max-n,
+- certificate_id->attrCKA_ID,
+- certificate_id->attrCKA_ID_size
+- );
+- }
+-
+- *max = _max;
+- rv = CKR_OK;
+-
+-cleanup:
++ rv = __generate_pkcs11_uri(sz, max, certificate_id, certificate_id->token_id);
+
+ _PKCS11H_DEBUG (
+ PKCS11H_LOG_DEBUG2,
+@@ -360,27 +536,16 @@ cleanup:
+ return rv;
+ }
+
++static
+ CK_RV
+-pkcs11h_certificate_deserializeCertificateId (
+- OUT pkcs11h_certificate_id_t * const p_certificate_id,
++__pkcs11h_certificate_legacy_deserializeCertificateId (
++ OUT pkcs11h_certificate_id_t certificate_id,
+ IN const char * const sz
+ ) {
+- pkcs11h_certificate_id_t certificate_id = NULL;
+ CK_RV rv = CKR_FUNCTION_FAILED;
+ char *p = NULL;
+ char *_sz = NULL;
+-
+- _PKCS11H_ASSERT (p_certificate_id!=NULL);
+- _PKCS11H_ASSERT (sz!=NULL);
+-
+- *p_certificate_id = NULL;
+-
+- _PKCS11H_DEBUG (
+- PKCS11H_LOG_DEBUG2,
+- "PKCS#11: pkcs11h_certificate_deserializeCertificateId entry p_certificate_id=%p, sz='%s'",
+- (void *)p_certificate_id,
+- sz
+- );
++ size_t id_hex_len;
+
+ if (
+ (rv = _pkcs11h_mem_strdup (
+@@ -393,10 +558,6 @@ pkcs11h_certificate_deserializeCertificateId (
+
+ p = _sz;
+
+- if ((rv = _pkcs11h_certificate_newCertificateId (&certificate_id)) != CKR_OK) {
+- goto cleanup;
+- }
+-
+ if ((p = strrchr (_sz, '/')) == NULL) {
+ rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto cleanup;
+@@ -414,7 +575,12 @@ pkcs11h_certificate_deserializeCertificateId (
+ goto cleanup;
+ }
+
+- certificate_id->attrCKA_ID_size = strlen (p)/2;
++ id_hex_len = strlen (p);
++ if (id_hex_len & 1) {
++ rv = CKR_ATTRIBUTE_VALUE_INVALID;
++ goto cleanup;
++ }
++ certificate_id->attrCKA_ID_size = id_hex_len/2;
+
+ if (
+ (rv = _pkcs11h_mem_malloc (
+@@ -430,21 +596,64 @@ pkcs11h_certificate_deserializeCertificateId (
+ goto cleanup;
+ }
+
++ rv = CKR_OK;
++
++cleanup:
++
++ if (_sz != NULL) {
++ _pkcs11h_mem_free ((void *)&_sz);
++ }
++
++ return rv;
++
++}
++
++CK_RV
++pkcs11h_certificate_deserializeCertificateId (
++ OUT pkcs11h_certificate_id_t * const p_certificate_id,
++ IN const char * const sz
++) {
++ pkcs11h_certificate_id_t certificate_id = NULL;
++ CK_RV rv = CKR_FUNCTION_FAILED;
++
++ _PKCS11H_ASSERT (p_certificate_id!=NULL);
++ _PKCS11H_ASSERT (sz!=NULL);
++
++ *p_certificate_id = NULL;
++
++ _PKCS11H_DEBUG (
++ PKCS11H_LOG_DEBUG2,
++ "PKCS#11: pkcs11h_certificate_deserializeCertificateId entry p_certificate_id=%p, sz='%s'",
++ (void *)p_certificate_id,
++ sz
++ );
++
++ if ((rv = _pkcs11h_certificate_newCertificateId (&certificate_id)) != CKR_OK) {
++ goto cleanup;
++ }
++ if ((rv = _pkcs11h_token_newTokenId (&certificate_id->token_id)) != CKR_OK) {
++ goto cleanup;
++ }
++
++ if (!strncmp(sz, URI_SCHEME, strlen (URI_SCHEME))) {
++ rv = __parse_pkcs11_uri (certificate_id->token_id, certificate_id, sz);
++ } else {
++ rv = __pkcs11h_certificate_legacy_deserializeCertificateId (certificate_id, sz);
++ }
++ if (rv != CKR_OK) {
++ goto cleanup;
++ }
++
+ *p_certificate_id = certificate_id;
+ certificate_id = NULL;
+ rv = CKR_OK;
+
+ cleanup:
+-
+ if (certificate_id != NULL) {
+ pkcs11h_certificate_freeCertificateId (certificate_id);
+ certificate_id = NULL;
+ }
+
+- if (_sz != NULL) {
+- _pkcs11h_mem_free ((void *)&_sz);
+- }
+-
+ _PKCS11H_DEBUG (
+ PKCS11H_LOG_DEBUG2,
+ "PKCS#11: pkcs11h_certificate_deserializeCertificateId return rv=%lu-'%s'",
+diff --git a/lib/pkcs11h-util.c b/lib/pkcs11h-util.c
+index 0743fd1..f90e443 100644
+--- a/lib/pkcs11h-util.c
++++ b/lib/pkcs11h-util.c
+@@ -110,12 +110,7 @@ _pkcs11h_util_hexToBinary (
+ p++;
+ }
+
+- if (*p != '\x0') {
+- return CKR_ATTRIBUTE_VALUE_INVALID;
+- }
+- else {
+- return CKR_OK;
+- }
++ return CKR_OK;
+ }
+
+ CK_RV
diff --git a/contrib/vcpkg-ports/pkcs11-helper/portfile.cmake b/contrib/vcpkg-ports/pkcs11-helper/portfile.cmake
new file mode 100644
index 0000000..54a0009
--- /dev/null
+++ b/contrib/vcpkg-ports/pkcs11-helper/portfile.cmake
@@ -0,0 +1,35 @@
+set(VERSION 1.27)
+
+vcpkg_download_distfile(ARCHIVE
+ URLS "https://github.com/OpenSC/pkcs11-helper/releases/download/pkcs11-helper-${VERSION}/pkcs11-helper-${VERSION}.0.tar.bz2"
+ FILENAME "pkcs11-helper-${VERSION}.tar.bz2"
+ SHA512 5799342cb755dae8b7ba0880d652e9d4b4f1e52a74043015e1185e1e059326cb2689bb51957db98060ac2257dee34e2f047dcf3d52ad59fd49b91fedcfc5332b
+)
+
+vcpkg_extract_source_archive_ex(
+ OUT_SOURCE_PATH SOURCE_PATH
+ ARCHIVE ${ARCHIVE}
+ REF ${VERSION}
+ PATCHES
+ 0001-nmake-openssl-1.1.1-support.patch
+ pkcs11-helper-001-RFC7512.patch
+)
+
+vcpkg_build_nmake(
+ SOURCE_PATH ${SOURCE_PATH}
+ NO_DEBUG
+ PROJECT_SUBPATH lib
+ PROJECT_NAME Makefile.w32-vc
+ OPTIONS
+ OPENSSL=1
+ OPENSSL_HOME=${CURRENT_PACKAGES_DIR}/../openssl_${TARGET_TRIPLET}
+)
+
+file(INSTALL ${SOURCE_PATH}/include/pkcs11-helper-1.0 DESTINATION ${CURRENT_PACKAGES_DIR}/include/)
+file(INSTALL ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}/lib/pkcs11-helper.dll.lib DESTINATION ${CURRENT_PACKAGES_DIR}/lib)
+file(INSTALL ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}/lib/pkcs11-helper.dll.lib DESTINATION ${CURRENT_PACKAGES_DIR}/debug/lib)
+
+file(INSTALL ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}/lib/libpkcs11-helper-1.dll DESTINATION ${CURRENT_PACKAGES_DIR}/bin)
+file(INSTALL ${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}/lib/libpkcs11-helper-1.dll DESTINATION ${CURRENT_PACKAGES_DIR}/debug/bin)
+
+file(INSTALL ${SOURCE_PATH}/COPYING DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright)
diff --git a/contrib/vcpkg-triplets/arm64-windows-ovpn.cmake b/contrib/vcpkg-triplets/arm64-windows-ovpn.cmake
new file mode 100644
index 0000000..dd3c6c0
--- /dev/null
+++ b/contrib/vcpkg-triplets/arm64-windows-ovpn.cmake
@@ -0,0 +1,7 @@
+set(VCPKG_TARGET_ARCHITECTURE arm64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE dynamic)
+
+if(PORT STREQUAL "lz4")
+ set(VCPKG_LIBRARY_LINKAGE static)
+endif()
diff --git a/contrib/vcpkg-triplets/x64-windows-ovpn.cmake b/contrib/vcpkg-triplets/x64-windows-ovpn.cmake
new file mode 100644
index 0000000..7036ed2
--- /dev/null
+++ b/contrib/vcpkg-triplets/x64-windows-ovpn.cmake
@@ -0,0 +1,7 @@
+set(VCPKG_TARGET_ARCHITECTURE x64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE dynamic)
+
+if(PORT STREQUAL "lz4")
+ set(VCPKG_LIBRARY_LINKAGE static)
+endif()
diff --git a/contrib/vcpkg-triplets/x86-windows-ovpn.cmake b/contrib/vcpkg-triplets/x86-windows-ovpn.cmake
new file mode 100644
index 0000000..7d3bf34
--- /dev/null
+++ b/contrib/vcpkg-triplets/x86-windows-ovpn.cmake
@@ -0,0 +1,7 @@
+set(VCPKG_TARGET_ARCHITECTURE x86)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE dynamic)
+
+if(PORT STREQUAL "lz4")
+ set(VCPKG_LIBRARY_LINKAGE static)
+endif()
diff --git a/depcomp b/depcomp
index 65cbf70..6b39162 100755
--- a/depcomp
+++ b/depcomp
@@ -3,7 +3,7 @@
scriptversion=2018-03-07.03; # UTC
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/distro/Makefile.am b/distro/Makefile.am
index a6795c4..1049f00 100644
--- a/distro/Makefile.am
+++ b/distro/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
diff --git a/distro/Makefile.in b/distro/Makefile.in
index 02b704c..a5a39b6 100644
--- a/distro/Makefile.in
+++ b/distro/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
VPATH = @srcdir@
@@ -209,7 +209,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -223,6 +224,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -250,7 +252,6 @@ 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@
@@ -301,6 +302,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -364,6 +367,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/distro/systemd/Makefile.am b/distro/systemd/Makefile.am
index 69e1269..59e0994 100644
--- a/distro/systemd/Makefile.am
+++ b/distro/systemd/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2017-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2017-2021 OpenVPN Inc <sales@openvpn.net>
#
%.service: %.service.in Makefile
diff --git a/distro/systemd/Makefile.in b/distro/systemd/Makefile.in
index 7e12bcc..8fded37 100644
--- a/distro/systemd/Makefile.in
+++ b/distro/systemd/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2017-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2017-2021 OpenVPN Inc <sales@openvpn.net>
#
VPATH = @srcdir@
@@ -181,7 +181,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -195,6 +196,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -222,7 +224,6 @@ 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@
@@ -273,6 +274,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -336,6 +339,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/doc/Makefile.am b/doc/Makefile.am
index c091ce0..1e4fcde 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -5,27 +5,88 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
-MAINTAINERCLEANFILES = \
- $(srcdir)/Makefile.in
+SUBDIRS = doxygen
-CLEANFILES = openvpn.8.html
+#
+# List of man and HTML pages we build when rst2man/rst2html is available
+#
+# NOTE: Remember to add source .rst files to $(dist_noinst_DATA) below
+# This could be automated with GNU Make, but we need BSD Make support
+#
+build_man_pages = openvpn.8 openvpn-examples.5
+build_html_pages = openvpn.8.html openvpn-examples.5.html
dist_doc_DATA = \
- management-notes.txt
+ management-notes.txt gui-notes.txt
dist_noinst_DATA = \
- README.plugins interactive-service-notes.rst
+ README.plugins interactive-service-notes.rst \
+ openvpn.8.rst \
+ openvpn-examples.5.rst \
+ man-sections/advanced-options.rst \
+ man-sections/client-options.rst \
+ man-sections/connection-profiles.rst \
+ man-sections/encryption-options.rst \
+ man-sections/examples.rst \
+ man-sections/generic-options.rst \
+ man-sections/inline-files.rst \
+ man-sections/link-options.rst \
+ man-sections/log-options.rst \
+ man-sections/management-options.rst \
+ man-sections/network-config.rst \
+ man-sections/pkcs11-options.rst \
+ man-sections/plugin-options.rst \
+ man-sections/protocol-options.rst \
+ man-sections/proxy-options.rst \
+ man-sections/renegotiation.rst \
+ man-sections/signals.rst \
+ man-sections/script-options.rst \
+ man-sections/server-options.rst \
+ man-sections/tls-options.rst \
+ man-sections/unsupported-options.rst \
+ man-sections/virtual-routing-and-forwarding.rst \
+ man-sections/vpn-network-options.rst \
+ man-sections/windows-options.rst
+
+
+###### GENERIC RULES ##########
+
+SUFFIXES = .8.rst .8 .8.html .5.rst .5 .5.html
+
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+.8.rst.8 .5.rst.5 :
+if HAVE_PYDOCUTILS
+ $(RST2MAN) $< > $@
+else
+ @echo "Missing python-docutils - skipping man page generation ($@)"
+endif
+
+.8.rst.8.html .5.rst.5.html :
+if HAVE_PYDOCUTILS
+ $(RST2HTML) $< > $@
+else
+ @echo "Missing python-docutils - skipping html page generation ($@)"
+endif
+
+
+if HAVE_PYDOCUTILS
+dist_noinst_DATA += $(build_man_pages)
+dist_html_DATA = $(build_html_pages)
+
+# Failsafe - do not delete these files unless we can recreate them
+CLEANFILES = $(build_man_pages) $(build_html_pages)
+
+endif
if WIN32
-dist_noinst_DATA += openvpn.8
-nodist_html_DATA = openvpn.8.html
-openvpn.8.html: $(srcdir)/openvpn.8
- $(MAN2HTML) < $(srcdir)/openvpn.8 > openvpn.8.html
else
-dist_man_MANS = openvpn.8
+dist_man_MANS = $(build_man_pages)
endif
+dist-hook : $(build_man_pages) $(build_html_pages)
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 6c86ac8..ef41a37 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -99,7 +99,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-@WIN32_TRUE@am__append_1 = openvpn.8
+@HAVE_PYDOCUTILS_TRUE@am__append_1 = $(build_man_pages)
subdir = doc
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
@@ -112,7 +112,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(dist_doc_DATA) \
- $(am__dist_noinst_DATA_DIST) $(am__DIST_COMMON)
+ $(am__dist_html_DATA_DIST) $(am__dist_noinst_DATA_DIST) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h \
$(top_builddir)/include/openvpn-plugin.h
@@ -132,6 +133,14 @@ am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -164,17 +173,89 @@ am__uninstall_files_from_dir = { \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
+man5dir = $(mandir)/man5
+am__installdirs = "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(docdir)" "$(DESTDIR)$(htmldir)"
man8dir = $(mandir)/man8
-am__installdirs = "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" \
- "$(DESTDIR)$(htmldir)"
NROFF = nroff
MANS = $(dist_man_MANS)
+am__dist_html_DATA_DIST = openvpn.8.html openvpn-examples.5.html
am__dist_noinst_DATA_DIST = README.plugins \
- interactive-service-notes.rst openvpn.8
-DATA = $(dist_doc_DATA) $(dist_noinst_DATA) $(nodist_html_DATA)
+ interactive-service-notes.rst openvpn.8.rst \
+ openvpn-examples.5.rst man-sections/advanced-options.rst \
+ man-sections/client-options.rst \
+ man-sections/connection-profiles.rst \
+ man-sections/encryption-options.rst man-sections/examples.rst \
+ man-sections/generic-options.rst man-sections/inline-files.rst \
+ man-sections/link-options.rst man-sections/log-options.rst \
+ man-sections/management-options.rst \
+ man-sections/network-config.rst \
+ man-sections/pkcs11-options.rst \
+ man-sections/plugin-options.rst \
+ man-sections/protocol-options.rst \
+ man-sections/proxy-options.rst man-sections/renegotiation.rst \
+ man-sections/signals.rst man-sections/script-options.rst \
+ man-sections/server-options.rst man-sections/tls-options.rst \
+ man-sections/unsupported-options.rst \
+ man-sections/virtual-routing-and-forwarding.rst \
+ man-sections/vpn-network-options.rst \
+ man-sections/windows-options.rst openvpn.8 openvpn-examples.5
+DATA = $(dist_doc_DATA) $(dist_html_DATA) $(dist_noinst_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
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
+DIST_SUBDIRS = $(SUBDIRS)
am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
@@ -187,7 +268,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -201,6 +283,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -228,7 +311,6 @@ 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@
@@ -279,6 +361,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -342,6 +426,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -353,20 +438,54 @@ tmpfilesdir = @tmpfilesdir@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-MAINTAINERCLEANFILES = \
- $(srcdir)/Makefile.in
+SUBDIRS = doxygen
-CLEANFILES = openvpn.8.html
+#
+# List of man and HTML pages we build when rst2man/rst2html is available
+#
+# NOTE: Remember to add source .rst files to $(dist_noinst_DATA) below
+# This could be automated with GNU Make, but we need BSD Make support
+#
+build_man_pages = openvpn.8 openvpn-examples.5
+build_html_pages = openvpn.8.html openvpn-examples.5.html
dist_doc_DATA = \
- management-notes.txt
+ management-notes.txt gui-notes.txt
dist_noinst_DATA = README.plugins interactive-service-notes.rst \
- $(am__append_1)
-@WIN32_TRUE@nodist_html_DATA = openvpn.8.html
-@WIN32_FALSE@dist_man_MANS = openvpn.8
-all: all-am
+ openvpn.8.rst openvpn-examples.5.rst \
+ man-sections/advanced-options.rst \
+ man-sections/client-options.rst \
+ man-sections/connection-profiles.rst \
+ man-sections/encryption-options.rst man-sections/examples.rst \
+ man-sections/generic-options.rst man-sections/inline-files.rst \
+ man-sections/link-options.rst man-sections/log-options.rst \
+ man-sections/management-options.rst \
+ man-sections/network-config.rst \
+ man-sections/pkcs11-options.rst \
+ man-sections/plugin-options.rst \
+ man-sections/protocol-options.rst \
+ man-sections/proxy-options.rst man-sections/renegotiation.rst \
+ man-sections/signals.rst man-sections/script-options.rst \
+ man-sections/server-options.rst man-sections/tls-options.rst \
+ man-sections/unsupported-options.rst \
+ man-sections/virtual-routing-and-forwarding.rst \
+ man-sections/vpn-network-options.rst \
+ man-sections/windows-options.rst $(am__append_1)
+
+###### GENERIC RULES ##########
+SUFFIXES = .8.rst .8 .8.html .5.rst .5 .5.html
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+@HAVE_PYDOCUTILS_TRUE@dist_html_DATA = $(build_html_pages)
+
+# Failsafe - do not delete these files unless we can recreate them
+@HAVE_PYDOCUTILS_TRUE@CLEANFILES = $(build_man_pages) $(build_html_pages)
+@WIN32_FALSE@dist_man_MANS = $(build_man_pages)
+all: all-recursive
.SUFFIXES:
+.SUFFIXES: .8.rst .8 .8.html .5.rst .5 .5.html
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -402,6 +521,49 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
+install-man5: $(dist_man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(dist_man_MANS)'; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.5[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
install-man8: $(dist_man_MANS)
@$(NORMAL_INSTALL)
@list1=''; \
@@ -466,9 +628,9 @@ uninstall-dist_docDATA:
@list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir)
-install-nodist_htmlDATA: $(nodist_html_DATA)
+install-dist_htmlDATA: $(dist_html_DATA)
@$(NORMAL_INSTALL)
- @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \
+ @list='$(dist_html_DATA)'; test -n "$(htmldir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \
$(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \
@@ -482,17 +644,110 @@ install-nodist_htmlDATA: $(nodist_html_DATA)
$(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \
done
-uninstall-nodist_htmlDATA:
+uninstall-dist_htmlDATA:
@$(NORMAL_UNINSTALL)
- @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \
+ @list='$(dist_html_DATA)'; test -n "$(htmldir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(htmldir)'; $(am__uninstall_files_from_dir)
-tags TAGS:
-ctags CTAGS:
-
-cscope cscopelist:
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(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-recursive
+
+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-recursive
+
+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
@@ -527,22 +782,51 @@ distdir-am: $(DISTFILES)
|| exit 1; \
fi; \
done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
check-am: all-am
-check: check-am
+check: check-recursive
all-am: Makefile $(MANS) $(DATA)
-installdirs:
- for dir in "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(htmldir)"; do \
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(htmldir)"; 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: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-installcheck: installcheck-am
+installcheck: installcheck-recursive
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
@@ -566,96 +850,107 @@ 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: clean-recursive
clean-am: clean-generic clean-libtool mostlyclean-am
-distclean: distclean-am
+distclean: distclean-recursive
-rm -f Makefile
-distclean-am: clean-am distclean-generic
+distclean-am: clean-am distclean-generic distclean-tags
-dvi: dvi-am
+dvi: dvi-recursive
dvi-am:
-html: html-am
+html: html-recursive
html-am:
-info: info-am
+info: info-recursive
info-am:
-install-data-am: install-dist_docDATA install-man \
- install-nodist_htmlDATA
+install-data-am: install-dist_docDATA install-dist_htmlDATA \
+ install-man
-install-dvi: install-dvi-am
+install-dvi: install-dvi-recursive
install-dvi-am:
install-exec-am:
-install-html: install-html-am
+install-html: install-html-recursive
install-html-am:
-install-info: install-info-am
+install-info: install-info-recursive
install-info-am:
-install-man: install-man8
+install-man: install-man5 install-man8
-install-pdf: install-pdf-am
+install-pdf: install-pdf-recursive
install-pdf-am:
-install-ps: install-ps-am
+install-ps: install-ps-recursive
install-ps-am:
installcheck-am:
-maintainer-clean: maintainer-clean-am
+maintainer-clean: maintainer-clean-recursive
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
-mostlyclean: mostlyclean-am
+mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
-pdf: pdf-am
+pdf: pdf-recursive
pdf-am:
-ps: ps-am
+ps: ps-recursive
ps-am:
-uninstall-am: uninstall-dist_docDATA uninstall-man \
- uninstall-nodist_htmlDATA
+uninstall-am: uninstall-dist_docDATA uninstall-dist_htmlDATA \
+ uninstall-man
+
+uninstall-man: uninstall-man5 uninstall-man8
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am dist-hook distclean 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-dist_docDATA install-dist_htmlDATA \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-man5 install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-dist_docDATA \
+ uninstall-dist_htmlDATA uninstall-man uninstall-man5 \
+ uninstall-man8
-uninstall-man: uninstall-man8
+.PRECIOUS: Makefile
-.MAKE: install-am install-strip
-.PHONY: all all-am check check-am clean clean-generic clean-libtool \
- cscopelist-am ctags-am distclean distclean-generic \
- distclean-libtool distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-dist_docDATA install-dvi install-dvi-am install-exec \
- install-exec-am install-html install-html-am install-info \
- install-info-am install-man install-man8 \
- install-nodist_htmlDATA install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
- ps ps-am tags-am uninstall uninstall-am uninstall-dist_docDATA \
- uninstall-man uninstall-man8 uninstall-nodist_htmlDATA
+.8.rst.8 .5.rst.5 :
+@HAVE_PYDOCUTILS_TRUE@ $(RST2MAN) $< > $@
+@HAVE_PYDOCUTILS_FALSE@ @echo "Missing python-docutils - skipping man page generation ($@)"
-.PRECIOUS: Makefile
+.8.rst.8.html .5.rst.5.html :
+@HAVE_PYDOCUTILS_TRUE@ $(RST2HTML) $< > $@
+@HAVE_PYDOCUTILS_FALSE@ @echo "Missing python-docutils - skipping html page generation ($@)"
-@WIN32_TRUE@openvpn.8.html: $(srcdir)/openvpn.8
-@WIN32_TRUE@ $(MAN2HTML) < $(srcdir)/openvpn.8 > openvpn.8.html
+dist-hook : $(build_man_pages) $(build_html_pages)
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am
new file mode 100644
index 0000000..82d909d
--- /dev/null
+++ b/doc/doxygen/Makefile.am
@@ -0,0 +1,21 @@
+#
+# 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) 2017-2021 Fox-IT B.V. <openvpn@foxcrypto.com>
+#
+
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+DISTCLEANFILES = openvpn.doxyfile
+
+.PHONY: doxygen
+doxygen: openvpn.doxyfile
+ doxygen openvpn.doxyfile
+
+clean-local:
+ -rm -rf html latex
diff --git a/vendor/Makefile.in b/doc/doxygen/Makefile.in
index 5b5ffed..48bd413 100644
--- a/vendor/Makefile.in
+++ b/doc/doxygen/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -13,6 +13,16 @@
# 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) 2017-2021 Fox-IT B.V. <openvpn@foxcrypto.com>
+#
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
@@ -87,7 +97,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-subdir = vendor
+subdir = doc/doxygen
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
$(top_srcdir)/m4/ax_socklen_t.m4 \
@@ -102,7 +112,7 @@ 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_FILES = openvpn.doxyfile
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -124,7 +134,7 @@ am__can_run_installinfo = \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-am__DIST_COMMON = $(srcdir)/Makefile.in
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/openvpn.doxyfile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
@@ -138,7 +148,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -152,6 +163,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -179,7 +191,6 @@ 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@
@@ -230,6 +241,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -293,6 +306,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -304,15 +318,10 @@ tmpfilesdir = @tmpfilesdir@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-cmockasrc = $(srcdir)/cmocka
-# Not just '$(builddir)/cmocka', because cmocka requires an out-of-source build
-cmockabuild = $(builddir)/cmocka_build
-cmockadist = $(builddir)/dist
MAINTAINERCLEANFILES = \
- $(srcdir)/Makefile.in \
- "$(cmockabuild)" \
- "$(cmockadist)"
+ $(srcdir)/Makefile.in
+DISTCLEANFILES = openvpn.doxyfile
all: all-am
.SUFFIXES:
@@ -325,9 +334,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
exit 1;; \
esac; \
done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign vendor/Makefile'; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/doxygen/Makefile'; \
$(am__cd) $(top_srcdir) && \
- $(AUTOMAKE) --foreign vendor/Makefile
+ $(AUTOMAKE) --foreign doc/doxygen/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -345,6 +354,8 @@ $(top_srcdir)/configure: $(am__configure_deps)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+openvpn.doxyfile: $(top_builddir)/config.status $(srcdir)/openvpn.doxyfile.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
mostlyclean-libtool:
-rm -f *.lo
@@ -421,12 +432,15 @@ 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)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
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-am: clean-generic clean-libtool mostlyclean-am
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
distclean: distclean-am
-rm -f Makefile
@@ -493,7 +507,7 @@ uninstall-am:
.MAKE: install-am install-strip
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
- cscopelist-am ctags-am distclean distclean-generic \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
distclean-libtool 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 \
@@ -507,15 +521,12 @@ uninstall-am:
.PRECIOUS: Makefile
-libcmocka:
-@CMOCKA_INITIALIZED_TRUE@ mkdir -p $(cmockabuild) $(cmockadist)
-@CMOCKA_INITIALIZED_TRUE@ (cd $(cmockabuild) && cmake -DCMAKE_INSTALL_PREFIX=../$(cmockadist) ../$(cmockasrc) && make && make install)
-
-check: libcmocka
+.PHONY: doxygen
+doxygen: openvpn.doxyfile
+ doxygen openvpn.doxyfile
-clean:
- rm -rf $(cmockabuild)
- rm -rf $(cmockainstall)
+clean-local:
+ -rm -rf html latex
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/doc/doxygen/openvpn.doxyfile.in b/doc/doxygen/openvpn.doxyfile.in
new file mode 100644
index 0000000..beb02d9
--- /dev/null
+++ b/doc/doxygen/openvpn.doxyfile.in
@@ -0,0 +1,279 @@
+# Doxyfile 1.5.5
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "OpenVPN"
+PROJECT_NUMBER =
+OUTPUT_DIRECTORY = "@abs_top_builddir@/doc/doxygen"
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = "@abs_top_srcdir@"
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = YES # NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+BUILTIN_STL_SUPPORT = NO
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+TYPEDEF_HIDES_STRUCT = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = YES
+EXTRACT_ANON_NSPACES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = NO
+FILE_VERSION_FILTER =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = "@abs_top_srcdir@"
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.d \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox \
+ *.py \
+ *.f90 \
+ *.f \
+ *.vhd \
+ *.vhdl
+RECURSIVE = YES
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Doxygen generated docs"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+HTML_DYNAMIC_SECTIONS = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = YES # NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = _WIN32 NTLM USE_LZO ENABLE_FRAGMENT P2MP ENABLE_CRYPTO_OPENSSL ENABLE_PLUGIN ENABLE_MANAGEMENT ENABLE_OCC HAVE_GETTIMEOFDAY
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = NO
+MSCGEN_PATH =
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO # YES
+CALLER_GRAPH = NO # YES
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH = "/usr/bin/dot"
+DOTFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 1000
+DOT_TRANSPARENT = YES
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/doc/gui-notes.txt b/doc/gui-notes.txt
new file mode 100644
index 0000000..e3bbf24
--- /dev/null
+++ b/doc/gui-notes.txt
@@ -0,0 +1,369 @@
+Management Interface "echo" protocol
+
+================================================================================
+THIS IS A PRELIMINARY VERSION OF THIS DOCUMENT. ALL INFORMATION IN IT
+IS SUBJECT TO CHANGE.
+================================================================================
+
+
+ CONTENTS
+ THE OPENVPN --ECHO OPTION
+ ENVIRONMENT COMMAND
+ MESSSAGE COMMANDS
+ PASSWORD COMMANDS
+ QUOTING
+ COMMMAND DETAILS
+
+
+=========================
+THE OPENVPN --ECHO OPTION
+=========================
+
+The OpenVPN --echo option causes commands to be sent out through the
+management interface, typically to a Graphic User Interface (GUI) such
+as "OpenVPN for Android", "Tunnelblick" (for macOS), or "Windows
+OpenVPN GUI". It can be included in a configuration file or on a
+command line, or can be pushed from the server.
+
+This document describes the commands that can be sent and how they are
+interpreted by various GUIs.
+
+ * OpenVPN does not process the commands in an --echo option; it only
+sends them out through the management interface.
+
+ * "echo" commands are processed by the GUI if, as, when, and in the
+order they are received. If no GUI is present the processing of
+commands may be delayed, the commands may never be processed, or only
+some commands may be processed. (That can happen if OpenVPN discards
+commands because its buffer for the commands fills up.)
+
+ * There is no mechanism for the GUI to acknowledge the receipt,
+success, or failure of a command.
+
+ * "echo" commands are stored by OpenVPN (within limits, see the next
+point) and sent only when the GUI requests them through the management
+interface. "echo" commands in the configuration file or the command
+line are typically requested and processed at the start of a
+connection attempt. "echo" commands that are pushed by the server are
+also typically asked for at the start of a connection attempt but can
+be sent at any time. They are processed in the middle of a connection
+attempt or after a connection is established, as the "push" options
+are received by the client from the server.
+
+ * OpenVPN's storage for echo commands is limited in size, so a large
+number of commands or commands with long messages may require that
+some commands be removed from the storage. If that happens, some of
+the commands may not be sent through the management interface when a
+GUI does connect to it or asks for the "echo" commands.
+
+ * On SIGUSR1 and SIGHUP connection restarts, "echo" commands that
+were sent through the management interface and have been saved by
+OpenVPN are sent again and will be re-processed by the GUI. (The
+message commands include a mechanism for muting (skipping) duplicate
+messages, see MESSAGE COMMANDS, below.)
+
+ * OpenVPN limits the number of separate arguments in each line of a
+configuration file. Arguments may be quoted to work around this
+limitation, see QUOTING, below.
+
+ * OpenVPN limits the size of each "echo" command sent over the
+management interface to 255 bytes, including overhead characters. To
+allow messages of arbitrary length, several message commands can be
+concatenated together before being displayed to the user, see MESSAGE
+COMMANDS, below.
+
+ * There no indication to the GUI of the source of the command
+(configuration file, command line option, or pushed from a server). It
+might be possible for the GUI to deduce that a command was pushed from
+a server because of timing or other management interface interactions.
+
+
+===================
+ENVIRONMENT COMMAND
+===================
+
+Typically, a GUI allows users to specify shell commands (typically
+scripts) to run at certain points in the connection/disconnection
+process, in addition to those provided by OpenVPN options such as
+"--up" and "--down".
+
+The "setenv" command can be used to set environment variables that are
+available to the scripts run by the GUI. Each "setenv" command
+specifies a value for one environment variable that is available to
+the scripts that the GUI runs.
+
+This is similar to Openvpn's "--setenv" option, which specifies an
+additional environment variable that is included in the environment
+variables that are available to the scripts that OpenVPN runs.
+
+
+=================
+MESSSAGE COMMANDS
+=================
+
+Four commands can be used to display a message to the user from the
+OpenVPN configuration or server:
+
+ msg
+ msg-n
+ msg-window
+ msg-notify
+
+"msg" and "msg-n" commands are concatenated to construct a message.
+When a "msg-window"or "msg-notify" command is received the message is
+displayed to the user.
+
+Identical messages (same title, text, and destination) received during
+one connection may be ignored or muted. Some GUIs may only show the
+first message for a connection, or the first message shown in a window
+and the first message shown as a notification.
+
+
+=================
+PASSWORD COMMANDS
+=================
+
+Three commands can be used to control the GUI's storage of usernames,
+passwords, and private keys:
+
+ disable-save-passwords
+ forget-passwords
+ save-passwords
+
+
+=======
+QUOTING
+=======
+
+ * In a configuration file, the rest of the line is parsed into
+separate arguments and then 'echo' and the arguments are passed, each
+separated by a single space, through the management interface. For
+example:
+
+ echo argument1 argument2
+ echo " argument1 argument2"
+
+will be sent through the management interface as
+
+ >ECHO:timestamp,argument1 argument2
+ >ECHO:timestamp, argument1 argument2
+
+ * In a command line option, the single argument following "--echo" is
+parsed similarly, so
+
+ --echo argument1 argument2
+ --echo " argument1 argument2"
+
+will be sent through the management interface as
+
+ >ECHO:timestamp,argument1 argument2
+ >ECHO:timestamp, argument1 argument2
+
+ * In a "push" option in a server configuration file, the single
+option following "push" is parsed similarly, so
+
+ push "echo argument1 argument2 argument3 argument4"
+ push "echo ' argument1 argument2 argument3 argument4'"
+
+will be sent through the management interface as
+
+ >ECHO:timestamp,argument1 argument2 argument3 argument4
+ >ECHO:timestamp, argument1 argument2 argument3 argument4
+
+
+================
+COMMMAND DETAILS
+================
+
+
+COMMAND -- disable-save-passwords
+---------------------------------
+
+Syntax: disable-save-passwords
+
+The GUI is instructed to not allow the user to save passwords or
+private keys for the configuration. The user is still allowed to save
+usernames. Any passwords or private keys that have been saved will be
+forgotten.
+
+This command will be effective at startup only if present in the
+configuration file or as a command line option. If pushed from the
+server, saving passwords will be disabled in password prompts only
+after the initial prompt has been shown to the user.
+
+ Android: ??????
+
+ Tunnelblick: Planned. This command will disable saving of
+passwords or private keys and forget any saved usernames, passwords,
+or private keys regardless of the normal (non-forced) global or
+per-configuration settings. A computer administrator can "force" this
+setting, overriding this command.
+
+ Windows OpenVPN GUI: Planned. This command will disable saving of
+passwords or private keys and forget any saved usernames, passwords,
+or private keys regardless of any global settings.
+
+
+COMMAND -- forget-passwords
+---------------------------
+
+Syntax: forget-passwords
+
+The GUI is instructed to forget any usernames, passwords, and private
+keys it has saved for the configuration. Useful when pushed from the
+server so that it is processed after authentication.
+
+ Android: ??????
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release 2.4.1 (GUI version 11.5.0)
+
+
+COMMAND -- msg
+--------------
+
+Syntax: msg text
+
+The text is appended to any previous text from "msg" or "msg-n"
+commands, and a newline is appended after that.
+
+A trailing newline will be removed from the completed message before
+it is displayed to the user.
+
+The text may include any UTF-8 character except a comma (","), CR
+(0x0D), LF (0x0A), or NUL (0x00).
+
+The text may not contain percent ("%") except in "percent encoding"
+sequences. To display a percent sign, use %25.
+
+The text may not contain commas (",") because of constraints imposed
+by OpenVPN. Commas should be encoded using "percent encoding" (URL
+encoding): a '%' character followed by two hexadecimal digits, the
+high- and then low-nibble of the ASCII code for the character to be
+shown. Examples: a comma is encoded as %2C or %2c; a percent sign is
+encoded as %25.
+
+Text containing comment characters # and ; must be enclosed in quotes to
+survive after option parsing by openvpn.
+
+The insertion of line endings (CR, LF) in the text is discouraged
+because it is OS dependent. Instead, use the "msg" command, which
+appends a line ending appropriate for the OS on which the GUI is
+running.
+
+ Android: Planned.
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release v2.4.11 / v2.5.1
+ (GUI version v11.22.0)
+
+COMMAND -- msg-n
+----------------
+
+Syntax: msg-n text
+
+The text is appended to any previous text from "msg"" or "msg-n""
+commands. (Like "msg" except that no newline is appended.)
+
+See "COMMAND -- msg" for details about "text".
+
+ Android: Planned.
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release v2.4.11 / v2.5.1
+ (GUI version v11.22.0)
+
+COMMAND -- msg-notify
+---------------------
+
+Syntax: msg-notify title
+
+The text from previous "msg" and/or "msg-n" commands is displayed to
+the user as a notification with title "title" and the previous text is
+forgotten.
+
+ Android: Planned.
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release v2.4.11 / v2.5.1
+ (GUI version v11.22.0)
+
+Note: The max length that will correctly display as a notification
+message is OS dependent.
+
+
+COMMAND -- msg-window title
+---------------------------
+
+Syntax: msg-window title
+
+The text from previous "msg" and/or "msg-n" commands is displayed to
+the user in a non-modal popup window with title "title" and the
+previous text is forgotten. How the title is displayed exactly is left
+to the implementation. Could be set as the window title or as a
+differently formatted text as the heading of the message, for example.
+
+ Android: Planned.
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release v2.4.11 / v2.5.1
+ (GUI version v11.22.0)
+
+
+COMMAND -- save-passwords
+-------------------------
+
+Syntax: save-passwords
+
+The GUI is instructed to allow the user to save usernames, passwords
+and private keys for the configuration.
+
+This command will be effective at startup only if present in the
+configuration file or as a command line option. If pushed from the
+server, saving passwords will be allowed in password prompts only
+after the initial prompt has been shown to the user.
+
+This command typically has the effect of presenting the password
+dialogs to the user with a "save password" checkbox checked. The user
+may still uncheck it during the dialog.
+
+ Android: ??????
+
+ Tunnelblick: Planned. Tunnelblick ignores this command. Usernames,
+passwords, and private keys may be saved by default, and this command
+will not override the separate Tunnelblick global or per-configuration
+settings used to disable saving them.
+
+ Windows OpenVPN GUI: Supported since release 2.4.1 (GUI version 11.5.0)
+
+
+COMMAND -- setenv
+-----------------
+
+Syntax: setenv name value
+
+Sets an environment variable that will be available to the scripts run
+by the GUI.
+
+This will set environment variable "OPENVPN_name" to value "value" for
+the scripts run by the GUI. "name" is changed to "OPENVPN_name" to
+prevent overwriting sensitive variables such as PATH. Variables are
+set in the order received, with later values replacing earlier ones
+for the same "name".
+
+Names may include only alphanumeric characters and underscores. A
+"setenv" command with an invalid name will be ignored.
+
+ Android: ??????
+
+ Tunnelblick: Planned.
+
+ Windows OpenVPN GUI: supported since release v2.4.7 (GUI version v11.12.0)
+The variables set by "setenv" are merged with those for the process
+environment. In case of duplicate names the one in the setenv list is
+chosen.
diff --git a/doc/man-sections/advanced-options.rst b/doc/man-sections/advanced-options.rst
new file mode 100644
index 0000000..bedc884
--- /dev/null
+++ b/doc/man-sections/advanced-options.rst
@@ -0,0 +1,110 @@
+Standalone Debug Options
+------------------------
+
+--show-gateway args
+ (Standalone) Show current IPv4 and IPv6 default gateway and interface
+ towards the gateway (if the protocol in question is enabled).
+
+ Valid syntax:
+ ::
+
+ --show-gateway
+ --show-gateway IPv6-target
+
+ For IPv6 this queries the route towards ::/128, or the specified IPv6
+ target address if passed as argument.
+ For IPv4 on Linux, Windows, MacOS and BSD it looks for a 0.0.0.0/0 route.
+ If there are more specific routes, the result will not always be matching
+ the route of the IPv4 packets to the VPN gateway.
+
+
+Advanced Expert Options
+-----------------------
+These are options only required when special tweaking is needed, often
+used when debugging or testing out special usage scenarios.
+
+--hash-size args
+ Set the size of the real address hash table to ``r`` and the virtual
+ address table to ``v``.
+
+ Valid syntax:
+ ::
+
+ hash-size r v
+
+ By default, both tables are sized at 256 buckets.
+
+--bcast-buffers n
+ Allocate ``n`` buffers for broadcast datagrams (default :code:`256`).
+
+--persist-local-ip
+ Preserve initially resolved local IP address and port number across
+ ``SIGUSR1`` or ``--ping-restart`` restarts.
+
+--persist-remote-ip
+ Preserve most recently authenticated remote IP address and port number
+ across :code:`SIGUSR1` or ``--ping-restart`` restarts.
+
+--prng args
+ *(Advanced)* Change the PRNG (Pseudo-random number generator) parameters
+
+ Valid syntaxes:
+ ::
+
+ prng alg
+ prng alg nsl
+
+ Changes the PRNG to use digest algorithm **alg** (default :code:`sha1`),
+ and set ``nsl`` (default :code:`16`) to the size in bytes of the nonce
+ secret length (between 16 and 64).
+
+ Set ``alg`` to :code:`none` to disable the PRNG and use the OpenSSL
+ RAND\_bytes function instead for all of OpenVPN's pseudo-random number
+ needs.
+
+--rcvbuf size
+ Set the TCP/UDP socket receive buffer size. Defaults to operating system
+ default.
+
+--shaper n
+ Limit bandwidth of outgoing tunnel data to ``n`` bytes per second on the
+ TCP/UDP port. Note that this will only work if mode is set to
+ :code:`p2p`. If you want to limit the bandwidth in both directions, use
+ this option on both peers.
+
+ OpenVPN uses the following algorithm to implement traffic shaping: Given
+ a shaper rate of ``n`` bytes per second, after a datagram write of ``b``
+ bytes is queued on the TCP/UDP port, wait a minimum of ``(b / n)``
+ seconds before queuing the next write.
+
+ It should be noted that OpenVPN supports multiple tunnels between the
+ same two peers, allowing you to construct full-speed and reduced
+ bandwidth tunnels at the same time, routing low-priority data such as
+ off-site backups over the reduced bandwidth tunnel, and other data over
+ the full-speed tunnel.
+
+ Also note that for low bandwidth tunnels (under 1000 bytes per second),
+ you should probably use lower MTU values as well (see above), otherwise
+ the packet latency will grow so large as to trigger timeouts in the TLS
+ layer and TCP connections running over the tunnel.
+
+ OpenVPN allows ``n`` to be between 100 bytes/sec and 100 Mbytes/sec.
+
+--sndbuf size
+ Set the TCP/UDP socket send buffer size. Defaults to operating system
+ default.
+
+--tcp-queue-limit n
+ Maximum number of output packets queued before TCP (default :code:`64`).
+
+ When OpenVPN is tunneling data from a TUN/TAP device to a remote client
+ over a TCP connection, it is possible that the TUN/TAP device might
+ produce data at a faster rate than the TCP connection can support. When
+ the number of output packets queued before sending to the TCP socket
+ reaches this limit for a given client connection, OpenVPN will start to
+ drop outgoing packets directed at this client.
+
+--txqueuelen n
+ *(Linux only)* Set the TX queue length on the TUN/TAP interface.
+ Currently defaults to operating system default.
+
diff --git a/doc/man-sections/client-options.rst b/doc/man-sections/client-options.rst
new file mode 100644
index 0000000..c5b7ad9
--- /dev/null
+++ b/doc/man-sections/client-options.rst
@@ -0,0 +1,369 @@
+Client Options
+--------------
+The client options are used when connecting to an OpenVPN server configured
+to use ``--server``, ``--server-bridge``, or ``--mode server`` in its
+configuration.
+
+--allow-pull-fqdn
+ Allow client to pull DNS names from server (rather than being limited to
+ IP address) for ``--ifconfig``, ``--route``, and ``--route-gateway``.
+
+--allow-recursive-routing
+ When this option is set, OpenVPN will not drop incoming tun packets with
+ same destination as host.
+
+--auth-token token
+ This is not an option to be used directly in any configuration files,
+ but rather push this option from a ``--client-connect`` script or a
+ ``--plugin`` which hooks into the :code:`OPENVPN_PLUGIN_CLIENT_CONNECT`
+ or :code:`OPENVPN_PLUGIN_CLIENT_CONNECT_V2` calls. This option provides a
+ possibility to replace the clients password with an authentication token
+ during the lifetime of the OpenVPN client.
+
+ Whenever the connection is renegotiated and the
+ ``--auth-user-pass-verify`` script or ``--plugin`` making use of the
+ :code:`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY` hook is triggered, it will
+ pass over this token as the password instead of the password the user
+ provided. The authentication token can only be reset by a full reconnect
+ where the server can push new options to the client. The password the
+ user entered is never preserved once an authentication token has been
+ set. If the OpenVPN server side rejects the authentication token then
+ the client will receive an :code:`AUTH_FAILED` and disconnect.
+
+ The purpose of this is to enable two factor authentication methods, such
+ as HOTP or TOTP, to be used without needing to retrieve a new OTP code
+ each time the connection is renegotiated. Another use case is to cache
+ authentication data on the client without needing to have the users
+ password cached in memory during the life time of the session.
+
+ To make use of this feature, the ``--client-connect`` script or
+ ``--plugin`` needs to put
+ ::
+
+ push "auth-token UNIQUE_TOKEN_VALUE"
+
+ into the file/buffer for dynamic configuration data. This will then make
+ the OpenVPN server to push this value to the client, which replaces the
+ local password with the ``UNIQUE_TOKEN_VALUE``.
+
+ Newer clients (2.4.7+) will fall back to the original password method
+ after a failed auth. Older clients will keep using the token value and
+ react according to ``--auth-retry``
+
+--auth-token-user base64username
+ Companion option to ``--auth-token``. This options allows to override
+ the username used by the client when reauthenticating with the ``auth-token``.
+ It also allows to use ``--auth-token`` in setups that normally do not use
+ username and password.
+
+ The username has to be base64 encoded.
+
+--auth-user-pass
+ Authenticate with server using username/password.
+
+ Valid syntaxes:
+ ::
+
+ auth-user-pass
+ auth-user-pass up
+
+ If ``up`` is present, it must be a file containing username/password on 2
+ lines. If the password line is missing, OpenVPN will prompt for one.
+
+ If ``up`` is omitted, username/password will be prompted from the
+ console.
+
+ The server configuration must specify an ``--auth-user-pass-verify``
+ script to verify the username/password provided by the client.
+
+--auth-retry type
+ Controls how OpenVPN responds to username/password verification errors
+ such as the client-side response to an :code:`AUTH_FAILED` message from
+ the server or verification failure of the private key password.
+
+ Normally used to prevent auth errors from being fatal on the client
+ side, and to permit username/password requeries in case of error.
+
+ An :code:`AUTH_FAILED` message is generated by the server if the client
+ fails ``--auth-user-pass`` authentication, or if the server-side
+ ``--client-connect`` script returns an error status when the client
+ tries to connect.
+
+ ``type`` can be one of:
+
+ :code:`none`
+ Client will exit with a fatal error (this is the default).
+
+ :code:`nointeract`
+ Client will retry the connection without requerying
+ for an ``--auth-user-pass`` username/password. Use this option for
+ unattended clients.
+
+ :code:`interact`
+ Client will requery for an ``--auth-user-pass``
+ username/password and/or private key password before attempting a
+ reconnection.
+
+ Note that while this option cannot be pushed, it can be controlled from
+ the management interface.
+
+--client
+ A helper directive designed to simplify the configuration of OpenVPN's
+ client mode. This directive is equivalent to:
+ ::
+
+ pull
+ tls-client
+
+--client-nat args
+ This pushable client option sets up a stateless one-to-one NAT rule on
+ packet addresses (not ports), and is useful in cases where routes or
+ ifconfig settings pushed to the client would create an IP numbering
+ conflict.
+
+ Examples:
+ ::
+
+ client-nat snat 192.168.0.0/255.255.0.0
+ client-nat dnat 10.64.0.0/255.255.0.0
+
+ ``network/netmask`` (for example :code:`192.168.0.0/255.255.0.0`) defines
+ the local view of a resource from the client perspective, while
+ ``alias/netmask`` (for example :code:`10.64.0.0/255.255.0.0`) defines the
+ remote view from the server perspective.
+
+ Use :code:`snat` (source NAT) for resources owned by the client and
+ :code:`dnat` (destination NAT) for remote resources.
+
+ Set ``--verb 6`` for debugging info showing the transformation of
+ src/dest addresses in packets.
+
+--connect-retry n
+ Wait ``n`` seconds between connection attempts (default :code:`5`).
+ Repeated reconnection attempts are slowed down after 5 retries per
+ remote by doubling the wait time after each unsuccessful attempt. An
+ optional argument ``max`` specifies the maximum value of wait time in
+ seconds at which it gets capped (default :code:`300`).
+
+--connect-retry-max n
+ ``n`` specifies the number of times each ``--remote`` or
+ ``<connection>`` entry is tried. Specifying ``n`` as :code:`1` would try
+ each entry exactly once. A successful connection resets the counter.
+ (default *unlimited*).
+
+--connect-timeout n
+ See ``--server-poll-timeout``.
+
+--explicit-exit-notify n
+ In UDP client mode or point-to-point mode, send server/peer an exit
+ notification if tunnel is restarted or OpenVPN process is exited. In
+ client mode, on exit/restart, this option will tell the server to
+ immediately close its client instance object rather than waiting for a
+ timeout.
+
+ The **n** parameter (default :code:`1` if not present) controls the
+ maximum number of attempts that the client will try to resend the exit
+ notification message.
+
+ In UDP server mode, send :code:`RESTART` control channel command to
+ connected clients. The ``n`` parameter (default :code:`1` if not present)
+ controls client behavior. With ``n`` = :code:`1` client will attempt to
+ reconnect to the same server, with ``n`` = :code:`2` client will advance
+ to the next server.
+
+ OpenVPN will not send any exit notifications unless this option is
+ enabled.
+
+--inactive args
+ Causes OpenVPN to exit after ``n`` seconds of inactivity on the TUN/TAP
+ device. The time length of inactivity is measured since the last
+ incoming or outgoing tunnel packet. The default value is 0 seconds,
+ which disables this feature.
+
+ Valid syntaxes:
+ ::
+
+ inactive n
+ inactive n bytes
+
+ If the optional ``bytes`` parameter is included, exit if less than
+ ``bytes`` of combined in/out traffic are produced on the tun/tap device
+ in ``n`` seconds.
+
+ In any case, OpenVPN's internal ping packets (which are just keepalives)
+ and TLS control packets are not considered "activity", nor are they
+ counted as traffic, as they are used internally by OpenVPN and are not
+ an indication of actual user activity.
+
+--proto-force p
+ When iterating through connection profiles, only consider profiles using
+ protocol ``p`` (:code:`tcp` \| :code:`udp`).
+
+--pull
+ This option must be used on a client which is connecting to a
+ multi-client server. It indicates to OpenVPN that it should accept
+ options pushed by the server, provided they are part of the legal set of
+ pushable options (note that the ``--pull`` option is implied by
+ ``--client`` ).
+
+ In particular, ``--pull`` allows the server to push routes to the
+ client, so you should not use ``--pull`` or ``--client`` in situations
+ where you don't trust the server to have control over the client's
+ routing table.
+
+--pull-filter args
+ Filter options on the client pushed by the server to the client.
+
+ Valid syntaxes:
+ ::
+
+ pull-filter accept text
+ pull-filter ignore text
+ pull-filter reject text
+
+ Filter options received from the server if the option starts with
+ :code:`text`. The action flag :code:`accept` allows the option,
+ :code:`ignore` removes it and :code:`reject` flags an error and triggers
+ a :code:`SIGUSR1` restart. The filters may be specified multiple times,
+ and each filter is applied in the order it is specified. The filtering of
+ each option stops as soon as a match is found. Unmatched options are accepted
+ by default.
+
+ Prefix comparison is used to match :code:`text` against the received option so
+ that
+ ::
+
+ pull-filter ignore "route"
+
+ would remove all pushed options starting with ``route`` which would
+ include, for example, ``route-gateway``. Enclose *text* in quotes to
+ embed spaces.
+
+ ::
+
+ pull-filter accept "route 192.168.1."
+ pull-filter ignore "route "
+
+ would remove all routes that do not start with ``192.168.1``.
+
+ *Note* that :code:`reject` may result in a repeated cycle of failure and
+ reconnect, unless multiple remotes are specified and connection to the
+ next remote succeeds. To silently ignore an option pushed by the server,
+ use :code:`ignore`.
+
+--remote args
+ Remote host name or IP address, port and protocol.
+
+ Valid syntaxes:
+ ::
+
+ remote host
+ remote host port
+ remote host port proto
+
+ The ``port`` and ``proto`` arguments are optional. The OpenVPN client
+ will try to connect to a server at ``host:port``. The ``proto`` argument
+ indicates the protocol to use when connecting with the remote, and may be
+ :code:`tcp` or :code:`udp`. To enforce IPv4 or IPv6 connections add a
+ :code:`4` or :code:`6` suffix; like :code:`udp4` / :code:`udp6`
+ / :code:`tcp4` / :code:`tcp6`.
+
+ On the client, multiple ``--remote`` options may be specified for
+ redundancy, each referring to a different OpenVPN server, in the order
+ specified by the list of ``--remote`` options. Specifying multiple
+ ``--remote`` options for this purpose is a special case of the more
+ general connection-profile feature. See the ``<connection>``
+ documentation below.
+
+ The client will move on to the next host in the list, in the event of
+ connection failure. Note that at any given time, the OpenVPN client will
+ at most be connected to one server.
+
+ Examples:
+ ::
+
+ remote server1.example.net
+ remote server1.example.net 1194
+ remote server2.example.net 1194 tcp
+
+ *Note:*
+ Since UDP is connectionless, connection failure is defined by
+ the ``--ping`` and ``--ping-restart`` options.
+
+ Also, if you use multiple ``--remote`` options, AND you are dropping
+ root privileges on the client with ``--user`` and/or ``--group`` AND
+ the client is running a non-Windows OS, if the client needs to switch
+ to a different server, and that server pushes back different TUN/TAP
+ or route settings, the client may lack the necessary privileges to
+ close and reopen the TUN/TAP interface. This could cause the client
+ to exit with a fatal error.
+
+ If ``--remote`` is unspecified, OpenVPN will listen for packets from any
+ IP address, but will not act on those packets unless they pass all
+ authentication tests. This requirement for authentication is binding on
+ all potential peers, even those from known and supposedly trusted IP
+ addresses (it is very easy to forge a source IP address on a UDP
+ packet).
+
+ When used in TCP mode, ``--remote`` will act as a filter, rejecting
+ connections from any host which does not match ``host``.
+
+ If ``host`` is a DNS name which resolves to multiple IP addresses,
+ OpenVPN will try them in the order that the system getaddrinfo()
+ presents them, so priorization and DNS randomization is done by the
+ system library. Unless an IP version is forced by the protocol
+ specification (4/6 suffix), OpenVPN will try both IPv4 and IPv6
+ addresses, in the order getaddrinfo() returns them.
+
+--remote-random
+ When multiple ``--remote`` address/ports are specified, or if connection
+ profiles are being used, initially randomize the order of the list as a
+ kind of basic load-balancing measure.
+
+--remote-random-hostname
+ Prepend a random string (6 bytes, 12 hex characters) to hostname to
+ prevent DNS caching. For example, "foo.bar.gov" would be modified to
+ "<random-chars>.foo.bar.gov".
+
+--resolv-retry n
+ If hostname resolve fails for ``--remote``, retry resolve for ``n``
+ seconds before failing.
+
+ Set ``n`` to "infinite" to retry indefinitely.
+
+ By default, ``--resolv-retry infinite`` is enabled. You can disable by
+ setting n=0.
+
+--single-session
+ After initially connecting to a remote peer, disallow any new
+ connections. Using this option means that a remote peer cannot connect,
+ disconnect, and then reconnect.
+
+ If the daemon is reset by a signal or ``--ping-restart``, it will allow
+ one new connection.
+
+ ``--single-session`` can be used with ``--ping-exit`` or ``--inactive``
+ to create a single dynamic session that will exit when finished.
+
+--server-poll-timeout n
+ When connecting to a remote server do not wait for more than ``n``
+ seconds for a response before trying the next server. The default value
+ is 120s. This timeout includes proxy and TCP connect timeouts.
+
+--static-challenge args
+ Enable static challenge/response protocol
+
+ Valid syntax:
+ ::
+
+ static-challenge text echo
+
+ The ``text`` challenge text is presented to the user which describes what
+ information is requested. The ``echo`` flag indicates if the user's
+ input should be echoed on the screen. Valid ``echo`` values are
+ :code:`0` or :code:`1`.
+
+ See management-notes.txt in the OpenVPN distribution for a description of
+ the OpenVPN challenge/response protocol.
+
+.. include:: proxy-options.rst
diff --git a/doc/man-sections/connection-profiles.rst b/doc/man-sections/connection-profiles.rst
new file mode 100644
index 0000000..fd3382b
--- /dev/null
+++ b/doc/man-sections/connection-profiles.rst
@@ -0,0 +1,75 @@
+CONNECTION PROFILES
+===================
+
+Client configuration files may contain multiple remote servers which
+it will attempt to connect against. But there are some configuration
+options which are related to specific ``--remote`` options. For these
+use cases, connection profiles are the solution.
+
+By enacpulating the ``--remote`` option and related options within
+``<connection>`` and ``</connection>``, these options are handled as a
+group.
+
+An OpenVPN client will try each connection profile sequentially until it
+achieves a successful connection.
+
+``--remote-random`` can be used to initially "scramble" the connection
+list.
+
+Here is an example of connection profile usage:
+::
+
+ client
+ dev tun
+
+ <connection>
+ remote 198.19.34.56 1194 udp
+ </connection>
+
+ <connection>
+ remote 198.19.34.56 443 tcp
+ </connection>
+
+ <connection>
+ remote 198.19.34.56 443 tcp
+ http-proxy 192.168.0.8 8080
+ </connection>
+
+ <connection>
+ remote 198.19.36.99 443 tcp
+ http-proxy 192.168.0.8 8080
+ </connection>
+
+ persist-key
+ persist-tun
+ pkcs12 client.p12
+ remote-cert-tls server
+ verb 3
+
+First we try to connect to a server at 198.19.34.56:1194 using UDP. If
+that fails, we then try to connect to 198.19.34.56:443 using TCP. If
+that also fails, then try connecting through an HTTP proxy at
+192.168.0.8:8080 to 198.19.34.56:443 using TCP. Finally, try to connect
+through the same proxy to a server at 198.19.36.99:443 using TCP.
+
+The following OpenVPN options may be used inside of a ``<connection>``
+block:
+
+``bind``, ``connect-retry``, ``connect-retry-max``, ``connect-timeout``,
+``explicit-exit-notify``, ``float``, ``fragment``, ``http-proxy``,
+``http-proxy-option``, ``key-direction``, ``link-mtu``, ``local``,
+``lport``, ``mssfix``, ``mtu-disc``, ``nobind``, ``port``, ``proto``,
+``remote``, ``rport``, ``socks-proxy``, ``tls-auth``, ``tls-crypt``,
+``tun-mtu and``, ``tun-mtu-extra``.
+
+A defaulting mechanism exists for specifying options to apply to all
+``<connection>`` profiles. If any of the above options (with the
+exception of ``remote`` ) appear outside of a ``<connection>`` block,
+but in a configuration file which has one or more ``<connection>``
+blocks, the option setting will be used as a default for
+``<connection>`` blocks which follow it in the configuration file.
+
+For example, suppose the ``nobind`` option were placed in the sample
+configuration file above, near the top of the file, before the first
+``<connection>`` block. The effect would be as if ``nobind`` were
+declared in all ``<connection>`` blocks below it.
diff --git a/doc/man-sections/encryption-options.rst b/doc/man-sections/encryption-options.rst
new file mode 100644
index 0000000..ee34f14
--- /dev/null
+++ b/doc/man-sections/encryption-options.rst
@@ -0,0 +1,135 @@
+Encryption Options
+==================
+
+SSL Library information
+-----------------------
+
+--show-ciphers
+ (Standalone) Show all cipher algorithms to use with the ``--cipher``
+ option.
+
+--show-digests
+ (Standalone) Show all message digest algorithms to use with the
+ ``--auth`` option.
+
+--show-tls
+ (Standalone) Show all TLS ciphers supported by the crypto library.
+ OpenVPN uses TLS to secure the control channel, over which the keys that
+ are used to protect the actual VPN traffic are exchanged. The TLS
+ ciphers will be sorted from highest preference (most secure) to lowest.
+
+ Be aware that whether a cipher suite in this list can actually work
+ depends on the specific setup of both peers (e.g. both peers must
+ support the cipher, and an ECDSA cipher suite will not work if you are
+ using an RSA certificate, etc.).
+
+--show-engines
+ (Standalone) Show currently available hardware-based crypto acceleration
+ engines supported by the OpenSSL library.
+
+--show-groups
+ (Standalone) Show all available elliptic curves/groups to use with the
+ ``--ecdh-curve`` and ``tls-groups`` options.
+
+Generating key material
+-----------------------
+
+--genkey args
+ (Standalone) Generate a key to be used of the type keytype. if keyfile
+ is left out or empty the key will be output on stdout. See the following
+ sections for the different keytypes.
+
+ Valid syntax:
+ ::
+
+ --genkey keytype keyfile
+
+ Valid keytype arguments are:
+
+ :code:`secret` Standard OpenVPN shared secret keys
+
+ :code:`tls-crypt` Alias for :code:`secret`
+
+ :code:`tls-auth` Alias for :code:`secret`
+
+ :code:`auth-token` Key used for ``--auth-gen-token-key``
+
+ :code:`tls-crypt-v2-server` TLS Crypt v2 server key
+
+ :code:`tls-crypt-v2-client` TLS Crypt v2 client key
+
+
+ Examples:
+ ::
+
+ $ openvpn --genkey secret shared.key
+ $ openvpn --genkey tls-crypt shared.key
+ $ openvpn --genkey tls-auth shared.key
+ $ openvpn --genkey tls-crypt-v2-server v2crypt-server.key
+ $ openvpn --tls-crypt-v2 v2crypt-server.key --genkey tls-crypt-v2-client v2crypt-client-1.key
+
+ * Generating *Shared Secret Keys*
+ Generate a shared secret, for use with the ``--secret``, ``--tls-auth``
+ or ``--tls-crypt`` options.
+
+ Syntax:
+ ::
+
+ $ openvpn --genkey secret|tls-crypt|tls-auth keyfile
+
+ The key is saved in ``keyfile``. All three variants (``--secret``,
+ ``tls-crypt`` and ``tls-auth``) generate the same type of key. The
+ aliases are added for convenience.
+
+ If using this for ``--secret``, this file must be shared with the peer
+ over a pre-existing secure channel such as ``scp``\(1).
+
+ * Generating *TLS Crypt v2 Server key*
+ Generate a ``--tls-crypt-v2`` key to be used by an OpenVPN server.
+ The key is stored in ``keyfile``.
+
+ Syntax:
+ ::
+
+ --genkey tls-crypt-v2-server keyfile
+
+ * Generating *TLS Crypt v2 Client key*
+ Generate a --tls-crypt-v2 key to be used by OpenVPN clients. The
+ key is stored in ``keyfile``.
+
+ Syntax
+ ::
+
+ --genkey tls-crypt-v2-client keyfile [metadata]
+
+ If supplied, include the supplied ``metadata`` in the wrapped client
+ key. This metadata must be supplied in base64-encoded form. The
+ metadata must be at most 735 bytes long (980 bytes in base64).
+
+ If no metadata is supplied, OpenVPN will use a 64-bit unix timestamp
+ representing the current time in UTC, encoded in network order, as
+ metadata for the generated key.
+
+ A tls-crypt-v2 client key is wrapped using a server key. To generate a
+ client key, the user must therefore supply the server key using the
+ ``--tls-crypt-v2`` option.
+
+ Servers can use ``--tls-crypt-v2-verify`` to specify a metadata
+ verification command.
+
+ * Generate *Authentication Token key*
+ Generate a new secret that can be used with **--auth-gen-token-secret**
+
+ Syntax:
+ ::
+
+ --genkey auth-token [keyfile]
+
+ *Note:*
+ This file should be kept secret to the server as anyone that has
+ access to this file will be able to generate auth tokens that the
+ OpenVPN server will accept as valid.
+
+.. include:: renegotiation.rst
+.. include:: tls-options.rst
+.. include:: pkcs11-options.rst
diff --git a/doc/man-sections/examples.rst b/doc/man-sections/examples.rst
new file mode 100644
index 0000000..3f494ea
--- /dev/null
+++ b/doc/man-sections/examples.rst
@@ -0,0 +1,240 @@
+EXAMPLES
+========
+
+Prior to running these examples, you should have OpenVPN installed on
+two machines with network connectivity between them. If you have not yet
+installed OpenVPN, consult the INSTALL file included in the OpenVPN
+distribution.
+
+
+Firewall Setup:
+---------------
+
+If firewalls exist between the two machines, they should be set to
+forward the port OpenVPN is configured to use, in both directions.
+The default for OpenVPN is 1194/udp. If you do not have control
+over the firewalls between the two machines, you may still be able to
+use OpenVPN by adding ``--ping 15`` to each of the ``openvpn`` commands
+used below in the examples (this will cause each peer to send out a UDP
+ping to its remote peer once every 15 seconds which will cause many
+stateful firewalls to forward packets in both directions without an
+explicit firewall rule).
+
+Please see your operating system guides for how to configure the firewall
+on your systems.
+
+
+VPN Address Setup:
+------------------
+
+For purposes of our example, our two machines will be called
+``bob.example.com`` and ``alice.example.com``. If you are constructing a
+VPN over the internet, then replace ``bob.example.com`` and
+``alice.example.com`` with the internet hostname or IP address that each
+machine will use to contact the other over the internet.
+
+Now we will choose the tunnel endpoints. Tunnel endpoints are private IP
+addresses that only have meaning in the context of the VPN. Each machine
+will use the tunnel endpoint of the other machine to access it over the
+VPN. In our example, the tunnel endpoint for bob.example.com will be
+10.4.0.1 and for alice.example.com, 10.4.0.2.
+
+Once the VPN is established, you have essentially created a secure
+alternate path between the two hosts which is addressed by using the
+tunnel endpoints. You can control which network traffic passes between
+the hosts (a) over the VPN or (b) independently of the VPN, by choosing
+whether to use (a) the VPN endpoint address or (b) the public internet
+address, to access the remote host. For example if you are on
+bob.example.com and you wish to connect to ``alice.example.com`` via
+``ssh`` without using the VPN (since **ssh** has its own built-in security)
+you would use the command ``ssh alice.example.com``. However in the same
+scenario, you could also use the command ``telnet 10.4.0.2`` to create a
+telnet session with alice.example.com over the VPN, that would use the
+VPN to secure the session rather than ``ssh``.
+
+You can use any address you wish for the tunnel endpoints but make sure
+that they are private addresses (such as those that begin with 10 or
+192.168) and that they are not part of any existing subnet on the
+networks of either peer, unless you are bridging. If you use an address
+that is part of your local subnet for either of the tunnel endpoints,
+you will get a weird feedback loop.
+
+
+Example 1: A simple tunnel without security
+-------------------------------------------
+
+On bob:
+::
+
+ openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 9
+
+On alice:
+::
+
+ openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 9
+
+Now verify the tunnel is working by pinging across the tunnel.
+
+On bob:
+::
+
+ ping 10.4.0.2
+
+On alice:
+::
+
+ ping 10.4.0.1
+
+The ``--verb 9`` option will produce verbose output, similar to the
+``tcpdump``\(8) program. Omit the ``--verb 9`` option to have OpenVPN run
+quietly.
+
+
+Example 2: A tunnel with static-key security (i.e. using a pre-shared secret)
+-----------------------------------------------------------------------------
+
+First build a static key on bob.
+::
+
+ openvpn --genkey --secret key
+
+This command will build a key file called ``key`` (in ascii format). Now
+copy ``key`` to ``alice.example.com`` over a secure medium such as by using
+the ``scp``\(1) program.
+
+On bob:
+::
+
+ openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 5 \
+ --secret key
+
+On alice:
+::
+
+ openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 5 \
+ --secret key
+
+Now verify the tunnel is working by pinging across the tunnel.
+
+On bob:
+::
+
+ ping 10.4.0.2
+
+On alice:
+::
+
+ ping 10.4.0.1
+
+
+Example 3: A tunnel with full TLS-based security
+------------------------------------------------
+
+For this test, we will designate ``bob`` as the TLS client and ``alice``
+as the TLS server.
+
+*Note:*
+ The client or server designation only has
+ meaning for the TLS subsystem. It has no bearing on OpenVPN's
+ peer-to-peer, UDP-based communication model.*
+
+First, build a separate certificate/key pair for both bob and alice (see
+above where ``--cert`` is discussed for more info). Then construct
+Diffie Hellman parameters (see above where ``--dh`` is discussed for
+more info). You can also use the included test files :code:`client.crt`,
+:code:`client.key`, :code:`server.crt`, :code:`server.key` and
+:code:`ca.crt`. The ``.crt`` files are certificates/public-keys, the
+``.key`` files are private keys, and :code:`ca.crt` is a certification
+authority who has signed both :code:`client.crt` and :code:`server.crt`.
+For Diffie Hellman parameters you can use the included file
+:code:`dh2048.pem`.
+
+*WARNING:*
+ All client, server, and certificate authority certificates
+ and keys included in the OpenVPN distribution are totally
+ insecure and should be used for testing only.
+
+On bob:
+::
+
+ openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 \
+ --tls-client --ca ca.crt \
+ --cert client.crt --key client.key \
+ --reneg-sec 60 --verb 5
+
+On alice:
+::
+
+ openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 \
+ --tls-server --dh dh1024.pem --ca ca.crt \
+ --cert server.crt --key server.key \
+ --reneg-sec 60 --verb 5
+
+Now verify the tunnel is working by pinging across the tunnel.
+
+On bob:
+::
+
+ ping 10.4.0.2
+
+On alice:
+::
+
+ ping 10.4.0.1
+
+Notice the ``--reneg-sec 60`` option we used above. That tells OpenVPN
+to renegotiate the data channel keys every minute. Since we used
+``--verb 5`` above, you will see status information on each new key
+negotiation.
+
+For production operations, a key renegotiation interval of 60 seconds is
+probably too frequent. Omit the ``--reneg-sec 60`` option to use
+OpenVPN's default key renegotiation interval of one hour.
+
+
+Routing:
+--------
+
+Assuming you can ping across the tunnel, the next step is to route a
+real subnet over the secure tunnel. Suppose that bob and alice have two
+network interfaces each, one connected to the internet, and the other to
+a private network. Our goal is to securely connect both private
+networks. We will assume that bob's private subnet is *10.0.0.0/24* and
+alice's is *10.0.1.0/24*.
+
+First, ensure that IP forwarding is enabled on both peers. On Linux,
+enable routing:
+::
+
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+
+This setting is not persistent. Please see your operating systems
+documentation how to properly configure IP forwarding, which is also
+persistent through system boots.
+
+If your system is configured with a firewall. Please see your operating
+systems guide on how to configure the firewall. You typically want to
+allow traffic coming from and going to the tun/tap adapter OpenVPN is
+configured to use.
+
+On bob:
+::
+
+ route add -net 10.0.1.0 netmask 255.255.255.0 gw 10.4.0.2
+
+On alice:
+::
+
+ route add -net 10.0.0.0 netmask 255.255.255.0 gw 10.4.0.1
+
+Now any machine on the *10.0.0.0/24* subnet can access any machine on the
+*10.0.1.0/24* subnet over the secure tunnel (or vice versa).
+
+In a production environment, you could put the route command(s) in a
+script and execute with the ``--up`` option.
diff --git a/doc/man-sections/generic-options.rst b/doc/man-sections/generic-options.rst
new file mode 100644
index 0000000..d5f0883
--- /dev/null
+++ b/doc/man-sections/generic-options.rst
@@ -0,0 +1,445 @@
+Generic Options
+---------------
+This section covers generic options which are accessible regardless of
+which mode OpenVPN is configured as.
+
+--help
+
+ Show options.
+
+--auth-nocache
+ Don't cache ``--askpass`` or ``--auth-user-pass`` username/passwords in
+ virtual memory.
+
+ If specified, this directive will cause OpenVPN to immediately forget
+ username/password inputs after they are used. As a result, when OpenVPN
+ needs a username/password, it will prompt for input from stdin, which
+ may be multiple times during the duration of an OpenVPN session.
+
+ When using ``--auth-nocache`` in combination with a user/password file
+ and ``--chroot`` or ``--daemon``, make sure to use an absolute path.
+
+ This directive does not affect the ``--http-proxy`` username/password.
+ It is always cached.
+
+--cd dir
+ Change directory to ``dir`` prior to reading any files such as
+ configuration files, key files, scripts, etc. ``dir`` should be an
+ absolute path, with a leading "/", and without any references to the
+ current directory such as :code:`.` or :code:`..`.
+
+ This option is useful when you are running OpenVPN in ``--daemon`` mode,
+ and you want to consolidate all of your OpenVPN control files in one
+ location.
+
+--chroot dir
+ Chroot to ``dir`` after initialization. ``--chroot`` essentially
+ redefines ``dir`` as being the top level directory tree (/). OpenVPN
+ will therefore be unable to access any files outside this tree. This can
+ be desirable from a security standpoint.
+
+ Since the chroot operation is delayed until after initialization, most
+ OpenVPN options that reference files will operate in a pre-chroot
+ context.
+
+ In many cases, the ``dir`` parameter can point to an empty directory,
+ however complications can result when scripts or restarts are executed
+ after the chroot operation.
+
+ Note: The SSL library will probably need /dev/urandom to be available
+ inside the chroot directory ``dir``. This is because SSL libraries
+ occasionally need to collect fresh random. Newer linux kernels and some
+ BSDs implement a getrandom() or getentropy() syscall that removes the
+ need for /dev/urandom to be available.
+
+--config file
+ Load additional config options from ``file`` where each line corresponds
+ to one command line option, but with the leading '--' removed.
+
+ If ``--config file`` is the only option to the openvpn command, the
+ ``--config`` can be removed, and the command can be given as ``openvpn
+ file``
+
+ Note that configuration files can be nested to a reasonable depth.
+
+ Double quotation or single quotation characters ("", '') can be used to
+ enclose single parameters containing whitespace, and "#" or ";"
+ characters in the first column can be used to denote comments.
+
+ Note that OpenVPN 2.0 and higher performs backslash-based shell escaping
+ for characters not in single quotations, so the following mappings
+ should be observed:
+ ::
+
+ \\ Maps to a single backslash character (\).
+ \" Pass a literal doublequote character ("), don't
+ interpret it as enclosing a parameter.
+ \[SPACE] Pass a literal space or tab character, don't
+ interpret it as a parameter delimiter.
+
+ For example on Windows, use double backslashes to represent pathnames:
+ ::
+
+ secret "c:\\OpenVPN\\secret.key"
+
+
+ For examples of configuration files, see
+ https://openvpn.net/community-resources/how-to/
+
+ Here is an example configuration file:
+ ::
+
+ #
+ # Sample OpenVPN configuration file for
+ # using a pre-shared static key.
+ #
+ # '#' or ';' may be used to delimit comments.
+
+ # Use a dynamic tun device.
+ dev tun
+
+ # Our remote peer
+ remote mypeer.mydomain
+
+ # 10.1.0.1 is our local VPN endpoint
+ # 10.1.0.2 is our remote VPN endpoint
+ ifconfig 10.1.0.1 10.1.0.2
+
+ # Our pre-shared static key
+ secret static.key
+
+--daemon progname
+ Become a daemon after all initialization functions are completed. This
+ option will cause all message and error output to be sent to the syslog
+ file (such as :code:`/var/log/messages`), except for the output of
+ scripts and ifconfig commands, which will go to :code:`/dev/null` unless
+ otherwise redirected. The syslog redirection occurs immediately at the
+ point that ``--daemon`` is parsed on the command line even though the
+ daemonization point occurs later. If one of the ``--log`` options is
+ present, it will supersede syslog redirection.
+
+ The optional ``progname`` parameter will cause OpenVPN to report its
+ program name to the system logger as ``progname``. This can be useful in
+ linking OpenVPN messages in the syslog file with specific tunnels. When
+ unspecified, ``progname`` defaults to "openvpn".
+
+ When OpenVPN is run with the ``--daemon`` option, it will try to delay
+ daemonization until the majority of initialization functions which are
+ capable of generating fatal errors are complete. This means that
+ initialization scripts can test the return status of the openvpn command
+ for a fairly reliable indication of whether the command has correctly
+ initialized and entered the packet forwarding event loop.
+
+ In OpenVPN, the vast majority of errors which occur after initialization
+ are non-fatal.
+
+ Note: as soon as OpenVPN has daemonized, it can not ask for usernames,
+ passwords, or key pass phrases anymore. This has certain consequences,
+ namely that using a password-protected private key will fail unless the
+ ``--askpass`` option is used to tell OpenVPN to ask for the pass phrase
+ (this requirement is new in v2.3.7, and is a consequence of calling
+ daemon() before initializing the crypto layer).
+
+ Further, using ``--daemon`` together with ``--auth-user-pass`` (entered
+ on console) and ``--auth-nocache`` will fail as soon as key
+ renegotiation (and reauthentication) occurs.
+
+--disable-occ
+ Don't output a warning message if option inconsistencies are detected
+ between peers. An example of an option inconsistency would be where one
+ peer uses ``--dev tun`` while the other peer uses ``--dev tap``.
+
+ Use of this option is discouraged, but is provided as a temporary fix in
+ situations where a recent version of OpenVPN must connect to an old
+ version.
+
+--engine engine-name
+ Enable OpenSSL hardware-based crypto engine functionality.
+
+ If ``engine-name`` is specified, use a specific crypto engine. Use the
+ ``--show-engines`` standalone option to list the crypto engines which
+ are supported by OpenSSL.
+
+--fast-io
+ (Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
+ poll/epoll/select prior to the write operation. The purpose of such a
+ call would normally be to block until the device or socket is ready to
+ accept the write. Such blocking is unnecessary on some platforms which
+ don't support write blocking on UDP sockets or TUN/TAP devices. In such
+ cases, one can optimize the event loop by avoiding the poll/epoll/select
+ call, improving CPU efficiency by 5% to 10%.
+
+ This option can only be used on non-Windows systems, when ``--proto
+ udp`` is specified, and when ``--shaper`` is NOT specified.
+
+--group group
+ Similar to the ``--user`` option, this option changes the group ID of
+ the OpenVPN process to ``group`` after initialization.
+
+--ignore-unknown-option args
+ Valid syntax:
+ ::
+
+ ignore-unknown-options opt1 opt2 opt3 ... optN
+
+ When one of options ``opt1 ... optN`` is encountered in the configuration
+ file the configuration file parsing does not fail if this OpenVPN version
+ does not support the option. Multiple ``--ignore-unknown-option`` options
+ can be given to support a larger number of options to ignore.
+
+ This option should be used with caution, as there are good security
+ reasons for having OpenVPN fail if it detects problems in a config file.
+ Having said that, there are valid reasons for wanting new software
+ features to gracefully degrade when encountered by older software
+ versions.
+
+ ``--ignore-unknown-option`` is available since OpenVPN 2.3.3.
+
+--iproute cmd
+ Set alternate command to execute instead of default ``iproute2`` command.
+ May be used in order to execute OpenVPN in unprivileged environment.
+
+--keying-material-exporter args
+ Save Exported Keying Material [RFC5705] of len bytes (must be between 16
+ and 4095 bytes) using ``label`` in environment
+ (:code:`exported_keying_material`) for use by plugins in
+ :code:`OPENVPN_PLUGIN_TLS_FINAL` callback.
+
+ Valid syntax:
+ ::
+
+ keying-material-exporter label len
+
+ Note that exporter ``labels`` have the potential to collide with existing
+ PRF labels. In order to prevent this, labels *MUST* begin with
+ :code:`EXPORTER`.
+
+--mlock
+ Disable paging by calling the POSIX mlockall function. Requires that
+ OpenVPN be initially run as root (though OpenVPN can subsequently
+ downgrade its UID using the ``--user`` option).
+
+ Using this option ensures that key material and tunnel data are never
+ written to disk due to virtual memory paging operations which occur
+ under most modern operating systems. It ensures that even if an attacker
+ was able to crack the box running OpenVPN, he would not be able to scan
+ the system swap file to recover previously used ephemeral keys, which
+ are used for a period of time governed by the ``--reneg`` options (see
+ below), then are discarded.
+
+ The downside of using ``--mlock`` is that it will reduce the amount of
+ physical memory available to other applications.
+
+ The limit on how much memory can be locked and how that limit
+ is enforced are OS-dependent. On Linux the default limit that an
+ unprivileged process may lock (RLIMIT_MEMLOCK) is low, and if
+ privileges are dropped later, future memory allocations will very
+ likely fail. The limit can be increased using ulimit or systemd
+ directives depending on how OpenVPN is started.
+
+--nice n
+ Change process priority after initialization (``n`` greater than 0 is
+ lower priority, ``n`` less than zero is higher priority).
+
+--persist-key
+ Don't re-read key files across :code:`SIGUSR1` or ``--ping-restart``.
+
+ This option can be combined with ``--user nobody`` to allow restarts
+ triggered by the :code:`SIGUSR1` signal. Normally if you drop root
+ privileges in OpenVPN, the daemon cannot be restarted since it will now
+ be unable to re-read protected key files.
+
+ This option solves the problem by persisting keys across :code:`SIGUSR1`
+ resets, so they don't need to be re-read.
+
+--remap-usr1 signal
+ Control whether internally or externally generated :code:`SIGUSR1` signals
+ are remapped to :code:`SIGHUP` (restart without persisting state) or
+ SIGTERM (exit).
+
+ ``signal`` can be set to :code:`SIGHUP` or :code:`SIGTERM`. By default,
+ no remapping occurs.
+
+--script-security level
+ This directive offers policy-level control over OpenVPN's usage of
+ external programs and scripts. Lower ``level`` values are more
+ restrictive, higher values are more permissive. Settings for ``level``:
+
+ :code:`0`
+ Strictly no calling of external programs.
+
+ :code:`1`
+ (Default) Only call built-in executables such as ifconfig,
+ ip, route, or netsh.
+
+ :code:`2`
+ Allow calling of built-in executables and user-defined
+ scripts.
+
+ :code:`3`
+ Allow passwords to be passed to scripts via environmental
+ variables (potentially unsafe).
+
+ OpenVPN releases before v2.3 also supported a ``method`` flag which
+ indicated how OpenVPN should call external commands and scripts. This
+ could be either :code:`execve` or :code:`system`. As of OpenVPN 2.3, this
+ flag is no longer accepted. In most \*nix environments the execve()
+ approach has been used without any issues.
+
+ Some directives such as ``--up`` allow options to be passed to the
+ external script. In these cases make sure the script name does not
+ contain any spaces or the configuration parser will choke because it
+ can't determine where the script name ends and script options start.
+
+ To run scripts in Windows in earlier OpenVPN versions you needed to
+ either add a full path to the script interpreter which can parse the
+ script or use the ``system`` flag to run these scripts. As of OpenVPN
+ 2.3 it is now a strict requirement to have full path to the script
+ interpreter when running non-executables files. This is not needed for
+ executable files, such as .exe, .com, .bat or .cmd files. For example,
+ if you have a Visual Basic script, you must use this syntax now:
+
+ ::
+
+ --up 'C:\\Windows\\System32\\wscript.exe C:\\Program\ Files\\OpenVPN\\config\\my-up-script.vbs'
+
+ Please note the single quote marks and the escaping of the backslashes
+ (\\) and the space character.
+
+ The reason the support for the :code:`system` flag was removed is due to
+ the security implications with shell expansions when executing scripts
+ via the :code:`system()` call.
+
+--setcon context
+ Apply SELinux ``context`` after initialization. This essentially
+ provides the ability to restrict OpenVPN's rights to only network I/O
+ operations, thanks to SELinux. This goes further than ``--user`` and
+ ``--chroot`` in that those two, while being great security features,
+ unfortunately do not protect against privilege escalation by
+ exploitation of a vulnerable system call. You can of course combine all
+ three, but please note that since setcon requires access to /proc you
+ will have to provide it inside the chroot directory (e.g. with mount
+ --bind).
+
+ Since the setcon operation is delayed until after initialization,
+ OpenVPN can be restricted to just network-related system calls, whereas
+ by applying the context before startup (such as the OpenVPN one provided
+ in the SELinux Reference Policies) you will have to allow many things
+ required only during initialization.
+
+ Like with chroot, complications can result when scripts or restarts are
+ executed after the setcon operation, which is why you should really
+ consider using the ``--persist-key`` and ``--persist-tun`` options.
+
+--status args
+ Write operational status to ``file`` every ``n`` seconds.
+
+ Valid syntaxes:
+ ::
+
+ status file
+ status file n
+
+ Status can also be written to the syslog by sending a :code:`SIGUSR2`
+ signal.
+
+ With multi-client capability enabled on a server, the status file
+ includes a list of clients and a routing table. The output format can be
+ controlled by the ``--status-version`` option in that case.
+
+ For clients or instances running in point-to-point mode, it will contain
+ the traffic statistics.
+
+--status-version n
+ Set the status file format version number to ``n``.
+
+ This only affects the status file on servers with multi-client
+ capability enabled. Valid status version values:
+
+ :code:`1`
+ Traditional format (default). The client list contains the
+ following fields comma-separated: Common Name, Real Address, Bytes
+ Received, Bytes Sent, Connected Since.
+
+ :code:`2`
+ A more reliable format for external processing. Compared to
+ version :code:`1`, the client list contains some additional fields:
+ Virtual Address, Virtual IPv6 Address, Username, Client ID, Peer ID,
+ Data Channel Cipher. Future versions may extend the number of fields.
+
+ :code:`3`
+ Identical to :code:`2`, but fields are tab-separated.
+
+--test-crypto
+ Do a self-test of OpenVPN's crypto options by encrypting and decrypting
+ test packets using the data channel encryption options specified above.
+ This option does not require a peer to function, and therefore can be
+ specified without ``--dev`` or ``--remote``.
+
+ The typical usage of ``--test-crypto`` would be something like this:
+ ::
+
+ openvpn --test-crypto --secret key
+
+ or
+
+ ::
+
+ openvpn --test-crypto --secret key --verb 9
+
+ This option is very useful to test OpenVPN after it has been ported to a
+ new platform, or to isolate problems in the compiler, OpenSSL crypto
+ library, or OpenVPN's crypto code. Since it is a self-test mode,
+ problems with encryption and authentication can be debugged
+ independently of network and tunnel issues.
+
+--tmp-dir dir
+ Specify a directory ``dir`` for temporary files. This directory will be
+ used by openvpn processes and script to communicate temporary data with
+ openvpn main process. Note that the directory must be writable by the
+ OpenVPN process after it has dropped it's root privileges.
+
+ This directory will be used by in the following cases:
+
+ * ``--client-connect`` scripts and :code:`OPENVPN_PLUGIN_CLIENT_CONNECT`
+ plug-in hook to dynamically generate client-specific configuration
+ :code:`client_connect_config_file` and return success/failure via
+ :code:`client_connect_deferred_file` when using deferred client connect
+ method
+
+ * :code:`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY` plug-in hooks returns
+ success/failure via :code:`auth_control_file` when using deferred auth
+ method
+
+ * :code:`OPENVPN_PLUGIN_ENABLE_PF` plugin hook to pass filtering rules
+ via ``pf_file``
+
+--use-prediction-resistance
+ Enable prediction resistance on mbed TLS's RNG.
+
+ Enabling prediction resistance causes the RNG to reseed in each call for
+ random. Reseeding this often can quickly deplete the kernel entropy
+ pool.
+
+ If you need this option, please consider running a daemon that adds
+ entropy to the kernel pool.
+
+--user user
+ Change the user ID of the OpenVPN process to ``user`` after
+ initialization, dropping privileges in the process. This option is
+ useful to protect the system in the event that some hostile party was
+ able to gain control of an OpenVPN session. Though OpenVPN's security
+ features make this unlikely, it is provided as a second line of defense.
+
+ By setting ``user`` to :code:`nobody` or somebody similarly unprivileged,
+ the hostile party would be limited in what damage they could cause. Of
+ course once you take away privileges, you cannot return them to an
+ OpenVPN session. This means, for example, that if you want to reset an
+ OpenVPN daemon with a :code:`SIGUSR1` signal (for example in response to
+ a DHCP reset), you should make use of one or more of the ``--persist``
+ options to ensure that OpenVPN doesn't need to execute any privileged
+ operations in order to restart (such as re-reading key files or running
+ ``ifconfig`` on the TUN device).
+
+--writepid file
+ Write OpenVPN's main process ID to ``file``.
diff --git a/doc/man-sections/inline-files.rst b/doc/man-sections/inline-files.rst
new file mode 100644
index 0000000..819bd3c
--- /dev/null
+++ b/doc/man-sections/inline-files.rst
@@ -0,0 +1,25 @@
+INLINE FILE SUPPORT
+===================
+
+OpenVPN allows including files in the main configuration for the ``--ca``,
+``--cert``, ``--dh``, ``--extra-certs``, ``--key``, ``--pkcs12``,
+``--secret``, ``--crl-verify``, ``--http-proxy-user-pass``, ``--tls-auth``,
+``--auth-gen-token-secret``, ``--tls-crypt`` and ``--tls-crypt-v2``
+options.
+
+Each inline file started by the line ``<option>`` and ended by the line
+``</option>``
+
+Here is an example of an inline file usage
+
+::
+
+ <cert>
+ -----BEGIN CERTIFICATE-----
+ [...]
+ -----END CERTIFICATE-----
+ </cert>
+
+When using the inline file feature with ``--pkcs12`` the inline file has
+to be base64 encoded. Encoding of a .p12 file into base64 can be done
+for example with OpenSSL by running :code:`openssl base64 -in input.p12`
diff --git a/doc/man-sections/link-options.rst b/doc/man-sections/link-options.rst
new file mode 100644
index 0000000..c132a62
--- /dev/null
+++ b/doc/man-sections/link-options.rst
@@ -0,0 +1,409 @@
+Link Options
+------------
+This link options section covers options related to the connection between
+the local and the remote host.
+
+--bind keywords
+ Bind to local address and port. This is the default unless any of
+ ``--proto tcp-client`` , ``--http-proxy`` or ``--socks-proxy`` are used.
+
+ If the optional :code:`ipv6only` keyword is present OpenVPN will bind only
+ to IPv6 (as opposed to IPv6 and IPv4) when a IPv6 socket is opened.
+
+--float
+ Allow remote peer to change its IP address and/or port number, such as
+ due to DHCP (this is the default if ``--remote`` is not used).
+ ``--float`` when specified with ``--remote`` allows an OpenVPN session
+ to initially connect to a peer at a known address, however if packets
+ arrive from a new address and pass all authentication tests, the new
+ address will take control of the session. This is useful when you are
+ connecting to a peer which holds a dynamic address such as a dial-in
+ user or DHCP client.
+
+ Essentially, ``--float`` tells OpenVPN to accept authenticated packets
+ from any address, not only the address which was specified in the
+ ``--remote`` option.
+
+--fragment max
+ Enable internal datagram fragmentation so that no UDP datagrams are sent
+ which are larger than ``max`` bytes.
+
+ The ``max`` parameter is interpreted in the same way as the
+ ``--link-mtu`` parameter, i.e. the UDP packet size after encapsulation
+ overhead has been added in, but not including the UDP header itself.
+
+ The ``--fragment`` option only makes sense when you are using the UDP
+ protocol (``--proto udp``).
+
+ ``--fragment`` adds 4 bytes of overhead per datagram.
+
+ See the ``--mssfix`` option below for an important related option to
+ ``--fragment``.
+
+ It should also be noted that this option is not meant to replace UDP
+ fragmentation at the IP stack level. It is only meant as a last resort
+ when path MTU discovery is broken. Using this option is less efficient
+ than fixing path MTU discovery for your IP link and using native IP
+ fragmentation instead.
+
+ Having said that, there are circumstances where using OpenVPN's internal
+ fragmentation capability may be your only option, such as tunneling a
+ UDP multicast stream which requires fragmentation.
+
+--keepalive args
+ A helper directive designed to simplify the expression of ``--ping`` and
+ ``--ping-restart``.
+
+ Valid syntax:
+ ::
+
+ keepalive interval timeout
+
+ This option can be used on both client and server side, but it is enough
+ to add this on the server side as it will push appropriate ``--ping``
+ and ``--ping-restart`` options to the client. If used on both server and
+ client, the values pushed from server will override the client local
+ values.
+
+ The ``timeout`` argument will be twice as long on the server side. This
+ ensures that a timeout is detected on client side before the server side
+ drops the connection.
+
+ For example, ``--keepalive 10 60`` expands as follows:
+ ::
+
+ if mode server:
+ ping 10 # Argument: interval
+ ping-restart 120 # Argument: timeout*2
+ push "ping 10" # Argument: interval
+ push "ping-restart 60" # Argument: timeout
+ else
+ ping 10 # Argument: interval
+ ping-restart 60 # Argument: timeout
+
+--link-mtu n
+ Sets an upper bound on the size of UDP packets which are sent between
+ OpenVPN peers. *It's best not to set this parameter unless you know what
+ you're doing.*
+
+--local host
+ Local host name or IP address for bind. If specified, OpenVPN will bind
+ to this address only. If unspecified, OpenVPN will bind to all
+ interfaces.
+
+--lport port
+ Set local TCP/UDP port number or name. Cannot be used together with
+ ``--nobind`` option.
+
+--mark value
+ Mark encrypted packets being sent with value. The mark value can be
+ matched in policy routing and packetfilter rules. This option is only
+ supported in Linux and does nothing on other operating systems.
+
+--mode m
+ Set OpenVPN major mode. By default, OpenVPN runs in point-to-point mode
+ (:code:`p2p`). OpenVPN 2.0 introduces a new mode (:code:`server`) which
+ implements a multi-client server capability.
+
+--mssfix max
+ Announce to TCP sessions running over the tunnel that they should limit
+ their send packet sizes such that after OpenVPN has encapsulated them,
+ the resulting UDP packet size that OpenVPN sends to its peer will not
+ exceed ``max`` bytes. The default value is :code:`1450`.
+
+ The ``max`` parameter is interpreted in the same way as the
+ ``--link-mtu`` parameter, i.e. the UDP packet size after encapsulation
+ overhead has been added in, but not including the UDP header itself.
+ Resulting packet would be at most 28 bytes larger for IPv4 and 48 bytes
+ for IPv6 (20/40 bytes for IP header and 8 bytes for UDP header). Default
+ value of 1450 allows IPv4 packets to be transmitted over a link with MTU
+ 1473 or higher without IP level fragmentation.
+
+ The ``--mssfix`` option only makes sense when you are using the UDP
+ protocol for OpenVPN peer-to-peer communication, i.e. ``--proto udp``.
+
+ ``--mssfix`` and ``--fragment`` can be ideally used together, where
+ ``--mssfix`` will try to keep TCP from needing packet fragmentation in
+ the first place, and if big packets come through anyhow (from protocols
+ other than TCP), ``--fragment`` will internally fragment them.
+
+ Both ``--fragment`` and ``--mssfix`` are designed to work around cases
+ where Path MTU discovery is broken on the network path between OpenVPN
+ peers.
+
+ The usual symptom of such a breakdown is an OpenVPN connection which
+ successfully starts, but then stalls during active usage.
+
+ If ``--fragment`` and ``--mssfix`` are used together, ``--mssfix`` will
+ take its default ``max`` parameter from the ``--fragment max`` option.
+
+ Therefore, one could lower the maximum UDP packet size to 1300 (a good
+ first try for solving MTU-related connection problems) with the
+ following options:
+ ::
+
+ --tun-mtu 1500 --fragment 1300 --mssfix
+
+--mtu-disc type
+ Should we do Path MTU discovery on TCP/UDP channel? Only supported on
+ OSes such as Linux that supports the necessary system call to set.
+
+ Valid types:
+
+ :code:`no` Never send DF (Don't Fragment) frames
+
+ :code:`maybe` Use per-route hints
+
+ :code:`yes` Always DF (Don't Fragment)
+
+--mtu-test
+ To empirically measure MTU on connection startup, add the ``--mtu-test``
+ option to your configuration. OpenVPN will send ping packets of various
+ sizes to the remote peer and measure the largest packets which were
+ successfully received. The ``--mtu-test`` process normally takes about 3
+ minutes to complete.
+
+--nobind
+ Do not bind to local address and port. The IP stack will allocate a
+ dynamic port for returning packets. Since the value of the dynamic port
+ could not be known in advance by a peer, this option is only suitable
+ for peers which will be initiating connections by using the --remote
+ option.
+
+--passtos
+ Set the TOS field of the tunnel packet to what the payload's TOS is.
+
+--ping n
+ Ping remote over the TCP/UDP control channel if no packets have been
+ sent for at least ``n`` seconds (specify ``--ping`` on both peers to
+ cause ping packets to be sent in both directions since OpenVPN ping
+ packets are not echoed like IP ping packets). When used in one of
+ OpenVPN's secure modes (where ``--secret``, ``--tls-server`` or
+ ``--tls-client`` is specified), the ping packet will be
+ cryptographically secure.
+
+ This option has two intended uses:
+
+ (1) Compatibility with stateful firewalls. The periodic ping will ensure
+ that a stateful firewall rule which allows OpenVPN UDP packets to
+ pass will not time out.
+
+ (2) To provide a basis for the remote to test the existence of its peer
+ using the ``--ping-exit`` option.
+
+--ping-exit n
+ Causes OpenVPN to exit after ``n`` seconds pass without reception of a
+ ping or other packet from remote. This option can be combined with
+ ``--inactive``, ``--ping`` and ``--ping-exit`` to create a two-tiered
+ inactivity disconnect.
+
+ For example,
+ ::
+
+ openvpn [options...] --inactive 3600 --ping 10 --ping-exit 60
+
+ when used on both peers will cause OpenVPN to exit within 60 seconds if
+ its peer disconnects, but will exit after one hour if no actual tunnel
+ data is exchanged.
+
+--ping-restart n
+ Similar to ``--ping-exit``, but trigger a :code:`SIGUSR1` restart after
+ ``n`` seconds pass without reception of a ping or other packet from
+ remote.
+
+ This option is useful in cases where the remote peer has a dynamic IP
+ address and a low-TTL DNS name is used to track the IP address using a
+ service such as http://dyndns.org/ + a dynamic DNS client such as
+ ``ddclient``.
+
+ If the peer cannot be reached, a restart will be triggered, causing the
+ hostname used with ``--remote`` to be re-resolved (if ``--resolv-retry``
+ is also specified).
+
+ In server mode, ``--ping-restart``, ``--inactive`` or any other type of
+ internally generated signal will always be applied to individual client
+ instance objects, never to whole server itself. Note also in server mode
+ that any internally generated signal which would normally cause a
+ restart, will cause the deletion of the client instance object instead.
+
+ In client mode, the ``--ping-restart`` parameter is set to 120 seconds
+ by default. This default will hold until the client pulls a replacement
+ value from the server, based on the ``--keepalive`` setting in the
+ server configuration. To disable the 120 second default, set
+ ``--ping-restart 0`` on the client.
+
+ See the signals section below for more information on :code:`SIGUSR1`.
+
+ Note that the behavior of ``SIGUSR1`` can be modified by the
+ ``--persist-tun``, ``--persist-key``, ``--persist-local-ip`` and
+ ``--persist-remote-ip`` options.
+
+ Also note that ``--ping-exit`` and ``--ping-restart`` are mutually
+ exclusive and cannot be used together.
+
+--ping-timer-rem
+ Run the ``--ping-exit`` / ``--ping-restart`` timer only if we have a
+ remote address. Use this option if you are starting the daemon in listen
+ mode (i.e. without an explicit ``--remote`` peer), and you don't want to
+ start clocking timeouts until a remote peer connects.
+
+--proto p
+ Use protocol ``p`` for communicating with remote host. ``p`` can be
+ :code:`udp`, :code:`tcp-client`, or :code:`tcp-server`.
+
+ The default protocol is :code:`udp` when ``--proto`` is not specified.
+
+ For UDP operation, ``--proto udp`` should be specified on both peers.
+
+ For TCP operation, one peer must use ``--proto tcp-server`` and the
+ other must use ``--proto tcp-client``. A peer started with
+ :code:`tcp-server` will wait indefinitely for an incoming connection. A peer
+ started with :code:`tcp-client` will attempt to connect, and if that fails,
+ will sleep for 5 seconds (adjustable via the ``--connect-retry`` option)
+ and try again infinite or up to N retries (adjustable via the
+ ``--connect-retry-max`` option). Both TCP client and server will
+ simulate a SIGUSR1 restart signal if either side resets the connection.
+
+ OpenVPN is designed to operate optimally over UDP, but TCP capability is
+ provided for situations where UDP cannot be used. In comparison with
+ UDP, TCP will usually be somewhat less efficient and less robust when
+ used over unreliable or congested networks.
+
+ This article outlines some of problems with tunneling IP over TCP:
+ http://sites.inka.de/sites/bigred/devel/tcp-tcp.html
+
+ There are certain cases, however, where using TCP may be advantageous
+ from a security and robustness perspective, such as tunneling non-IP or
+ application-level UDP protocols, or tunneling protocols which don't
+ possess a built-in reliability layer.
+
+--port port
+ TCP/UDP port number or port name for both local and remote (sets both
+ ``--lport`` and ``--rport`` options to given port). The current default
+ of 1194 represents the official IANA port number assignment for OpenVPN
+ and has been used since version 2.0-beta17. Previous versions used port
+ 5000 as the default.
+
+--rport port
+ Set TCP/UDP port number or name used by the ``--remote`` option. The
+ port can also be set directly using the ``--remote`` option.
+
+--replay-window args
+ Modify the replay protection sliding-window size and time window.
+
+ Valid syntax:
+ ::
+
+ replay-window n [t]
+
+ Use a replay protection sliding-window of size **n** and a time window
+ of **t** seconds.
+
+ By default **n** is 64 (the IPSec default) and **t** is 15 seconds.
+
+ This option is only relevant in UDP mode, i.e. when either **--proto
+ udp** is specified, or no **--proto** option is specified.
+
+ When OpenVPN tunnels IP packets over UDP, there is the possibility that
+ packets might be dropped or delivered out of order. Because OpenVPN,
+ like IPSec, is emulating the physical network layer, it will accept an
+ out-of-order packet sequence, and will deliver such packets in the same
+ order they were received to the TCP/IP protocol stack, provided they
+ satisfy several constraints.
+
+ (a) The packet cannot be a replay (unless ``--no-replay`` is
+ specified, which disables replay protection altogether).
+
+ (b) If a packet arrives out of order, it will only be accepted if
+ the difference between its sequence number and the highest sequence
+ number received so far is less than ``n``.
+
+ (c) If a packet arrives out of order, it will only be accepted if it
+ arrives no later than ``t`` seconds after any packet containing a higher
+ sequence number.
+
+ If you are using a network link with a large pipeline (meaning that the
+ product of bandwidth and latency is high), you may want to use a larger
+ value for ``n``. Satellite links in particular often require this.
+
+ If you run OpenVPN at ``--verb 4``, you will see the message
+ "Replay-window backtrack occurred [x]" every time the maximum sequence
+ number backtrack seen thus far increases. This can be used to calibrate
+ ``n``.
+
+ There is some controversy on the appropriate method of handling packet
+ reordering at the security layer.
+
+ Namely, to what extent should the security layer protect the
+ encapsulated protocol from attacks which masquerade as the kinds of
+ normal packet loss and reordering that occur over IP networks?
+
+ The IPSec and OpenVPN approach is to allow packet reordering within a
+ certain fixed sequence number window.
+
+ OpenVPN adds to the IPSec model by limiting the window size in time as
+ well as sequence space.
+
+ OpenVPN also adds TCP transport as an option (not offered by IPSec) in
+ which case OpenVPN can adopt a very strict attitude towards message
+ deletion and reordering: Don't allow it. Since TCP guarantees
+ reliability, any packet loss or reordering event can be assumed to be an
+ attack.
+
+ In this sense, it could be argued that TCP tunnel transport is preferred
+ when tunneling non-IP or UDP application protocols which might be
+ vulnerable to a message deletion or reordering attack which falls within
+ the normal operational parameters of IP networks.
+
+ So I would make the statement that one should never tunnel a non-IP
+ protocol or UDP application protocol over UDP, if the protocol might be
+ vulnerable to a message deletion or reordering attack that falls within
+ the normal operating parameters of what is to be expected from the
+ physical IP layer. The problem is easily fixed by simply using TCP as
+ the VPN transport layer.
+
+--replay-persist file
+ Persist replay-protection state across sessions using ``file`` to save
+ and reload the state.
+
+ This option will strengthen protection against replay attacks,
+ especially when you are using OpenVPN in a dynamic context (such as with
+ ``--inetd``) when OpenVPN sessions are frequently started and stopped.
+
+ This option will keep a disk copy of the current replay protection state
+ (i.e. the most recent packet timestamp and sequence number received from
+ the remote peer), so that if an OpenVPN session is stopped and
+ restarted, it will reject any replays of packets which were already
+ received by the prior session.
+
+ This option only makes sense when replay protection is enabled (the
+ default) and you are using either ``--secret`` (shared-secret key mode)
+ or TLS mode with ``--tls-auth``.
+
+--socket-flags flags
+ Apply the given flags to the OpenVPN transport socket. Currently, only
+ :code:`TCP_NODELAY` is supported.
+
+ The :code:`TCP_NODELAY` socket flag is useful in TCP mode, and causes the
+ kernel to send tunnel packets immediately over the TCP connection without
+ trying to group several smaller packets into a larger packet. This can
+ result in a considerably improvement in latency.
+
+ This option is pushable from server to client, and should be used on
+ both client and server for maximum effect.
+
+--tcp-nodelay
+ This macro sets the :code:`TCP_NODELAY` socket flag on the server as well
+ as pushes it to connecting clients. The :code:`TCP_NODELAY` flag disables
+ the Nagle algorithm on TCP sockets causing packets to be transmitted
+ immediately with low latency, rather than waiting a short period of time
+ in order to aggregate several packets into a larger containing packet.
+ In VPN applications over TCP, :code:`TCP_NODELAY` is generally a good
+ latency optimization.
+
+ The macro expands as follows:
+ ::
+
+ if mode server:
+ socket-flags TCP_NODELAY
+ push "socket-flags TCP_NODELAY"
diff --git a/doc/man-sections/log-options.rst b/doc/man-sections/log-options.rst
new file mode 100644
index 0000000..e385d18
--- /dev/null
+++ b/doc/man-sections/log-options.rst
@@ -0,0 +1,73 @@
+Log options
+-----------
+
+--echo parms
+ Echo ``parms`` to log output.
+
+ Designed to be used to send messages to a controlling application which
+ is receiving the OpenVPN log output.
+
+--errors-to-stderr
+ Output errors to stderr instead of stdout unless log output is
+ redirected by one of the ``--log`` options.
+
+--log file
+ Output logging messages to ``file``, including output to stdout/stderr
+ which is generated by called scripts. If ``file`` already exists it will
+ be truncated. This option takes effect immediately when it is parsed in
+ the command line and will supersede syslog output if ``--daemon`` or
+ ``--inetd`` is also specified. This option is persistent over the entire
+ course of an OpenVPN instantiation and will not be reset by
+ :code:`SIGHUP`, :code:`SIGUSR1`, or ``--ping-restart``.
+
+ Note that on Windows, when OpenVPN is started as a service, logging
+ occurs by default without the need to specify this option.
+
+--log-append file
+ Append logging messages to ``file``. If ``file`` does not exist, it will
+ be created. This option behaves exactly like ``--log`` except that it
+ appends to rather than truncating the log file.
+
+--machine-readable-output
+ Always write timestamps and message flags to log messages, even when
+ they otherwise would not be prefixed. In particular, this applies to log
+ messages sent to stdout.
+
+--mute n
+ Log at most ``n`` consecutive messages in the same category. This is
+ useful to limit repetitive logging of similar message types.
+
+--mute-replay-warnings
+ Silence the output of replay warnings, which are a common false alarm on
+ WiFi networks. This option preserves the security of the replay
+ protection code without the verbosity associated with warnings about
+ duplicate packets.
+
+--suppress-timestamps
+ Avoid writing timestamps to log messages, even when they otherwise would
+ be prepended. In particular, this applies to log messages sent to
+ stdout.
+
+--syslog progname
+ Direct log output to system logger, but do not become a daemon. See
+ ``--daemon`` directive above for description of ``progname`` parameter.
+
+--verb n
+ Set output verbosity to ``n`` (default :code:`1`). Each level shows all
+ info from the previous levels. Level :code:`3` is recommended if you want
+ a good summary of what's happening without being swamped by output.
+
+ :code:`0`
+ No output except fatal errors.
+
+ :code:`1` to :code:`4`
+ Normal usage range.
+
+ :code:`5`
+ Outputs :code:`R` and :code:`W` characters to the console for
+ each packet read and write, uppercase is used for TCP/UDP
+ packets and lowercase is used for TUN/TAP packets.
+
+ :code:`6` to :code:`11`
+ Debug info range (see :code:`errlevel.h` in the source code for
+ additional information on debug levels).
diff --git a/doc/man-sections/management-options.rst b/doc/man-sections/management-options.rst
new file mode 100644
index 0000000..de0d47e
--- /dev/null
+++ b/doc/man-sections/management-options.rst
@@ -0,0 +1,135 @@
+Management Interface Options
+----------------------------
+OpenVPN provides a feature rich socket based management interface for both
+server and client mode operations.
+
+--management args
+ Enable a management server on a ``socket-name`` Unix socket on those
+ platforms supporting it, or on a designated TCP port.
+
+ Valid syntaxes:
+ ::
+
+ management socket-name unix #
+ management socket-name unix pw-file # (recommended)
+ management IP port # (INSECURE)
+ management IP port pw-file #
+
+ ``pw-file``, if specified, is a password file where the password must
+ be on first line. Instead of a filename it can use the keyword stdin
+ which will prompt the user for a password to use when OpenVPN is
+ starting.
+
+ For unix sockets, the default behaviour is to create a unix domain
+ socket that may be connected to by any process. Use the
+ ``--management-client-user`` and ``--management-client-group``
+ directives to restrict access.
+
+ The management interface provides a special mode where the TCP
+ management link can operate over the tunnel itself. To enable this mode,
+ set IP to ``tunnel``. Tunnel mode will cause the management interface to
+ listen for a TCP connection on the local VPN address of the TUN/TAP
+ interface.
+
+ ***BEWARE*** of enabling the management interface over TCP. In these cases
+ you should *ALWAYS* make use of ``pw-file`` to password protect the
+ management interface. Any user who can connect to this TCP ``IP:port``
+ will be able to manage and control (and interfere with) the OpenVPN
+ process. It is also strongly recommended to set IP to 127.0.0.1
+ (localhost) to restrict accessibility of the management server to local
+ clients.
+
+ While the management port is designed for programmatic control of
+ OpenVPN by other applications, it is possible to telnet to the port,
+ using a telnet client in "raw" mode. Once connected, type :code:`help`
+ for a list of commands.
+
+ For detailed documentation on the management interface, see the
+ *management-notes.txt* file in the management folder of the OpenVPN
+ source distribution.
+
+--management-client
+ Management interface will connect as a TCP/unix domain client to
+ ``IP:port`` specified by ``--management`` rather than listen as a TCP
+ server or on a unix domain socket.
+
+ If the client connection fails to connect or is disconnected, a SIGTERM
+ signal will be generated causing OpenVPN to quit.
+
+--management-client-auth
+ Gives management interface client the responsibility to authenticate
+ clients after their client certificate has been verified. See
+ :code:`management-notes.txt` in OpenVPN distribution for detailed notes.
+
+--management-client-group g
+ When the management interface is listening on a unix domain socket, only
+ allow connections from group ``g``.
+
+--management-client-pf
+ Management interface clients must specify a packet filter file for each
+ connecting client. See :code:`management-notes.txt` in OpenVPN
+ distribution for detailed notes.
+
+--management-client-user u
+ When the management interface is listening on a unix domain socket, only
+ allow connections from user ``u``.
+
+--management-external-cert certificate-hint
+ Allows usage for external certificate instead of ``--cert`` option
+ (client-only). ``certificate-hint`` is an arbitrary string which is
+ passed to a management interface client as an argument of
+ *NEED-CERTIFICATE* notification. Requires ``--management-external-key``.
+
+--management-external-key args
+ Allows usage for external private key file instead of ``--key`` option
+ (client-only).
+
+ Valid syntaxes:
+ ::
+
+ management-external-key
+ management-external-key nopadding
+ management-external-key pkcs1
+ management-external-key nopadding pkcs1
+
+ The optional parameters :code:`nopadding` and :code:`pkcs1` signal
+ support for different padding algorithms. See
+ :code:`doc/mangement-notes.txt` for a complete description of this
+ feature.
+
+--management-forget-disconnect
+ Make OpenVPN forget passwords when management session disconnects.
+
+ This directive does not affect the ``--http-proxy`` username/password.
+ It is always cached.
+
+--management-hold
+ Start OpenVPN in a hibernating state, until a client of the management
+ interface explicitly starts it with the :code:`hold release` command.
+
+--management-log-cache n
+ Cache the most recent ``n`` lines of log file history for usage by the
+ management channel.
+
+--management-query-passwords
+ Query management channel for private key password and
+ ``--auth-user-pass`` username/password. Only query the management
+ channel for inputs which ordinarily would have been queried from the
+ console.
+
+--management-query-proxy
+ Query management channel for proxy server information for a specific
+ ``--remote`` (client-only).
+
+--management-query-remote
+ Allow management interface to override ``--remote`` directives
+ (client-only).
+
+--management-signal
+ Send SIGUSR1 signal to OpenVPN if management session disconnects. This
+ is useful when you wish to disconnect an OpenVPN session on user logoff.
+ For ``--management-client`` this option is not needed since a disconnect
+ will always generate a :code:`SIGTERM`.
+
+--management-up-down
+ Report tunnel up/down events to management interface.
diff --git a/doc/man-sections/network-config.rst b/doc/man-sections/network-config.rst
new file mode 100644
index 0000000..04b30aa
--- /dev/null
+++ b/doc/man-sections/network-config.rst
@@ -0,0 +1,10 @@
+NETWORK CONFIGURATION
+=====================
+
+OpenVPN consists of two sides of network configuration. One side is the
+*link* between the local and remote side, the other side is the *virtual
+network adapter* (tun/tap device).
+
+.. include:: link-options.rst
+.. include:: vpn-network-options.rst
+.. include:: virtual-routing-and-forwarding.rst
diff --git a/doc/man-sections/pkcs11-options.rst b/doc/man-sections/pkcs11-options.rst
new file mode 100644
index 0000000..c064aca
--- /dev/null
+++ b/doc/man-sections/pkcs11-options.rst
@@ -0,0 +1,80 @@
+PKCS#11 / SmartCard options
+---------------------------
+
+--pkcs11-cert-private args
+ Set if access to certificate object should be performed after login.
+ Every provider has its own setting.
+
+ Valid syntaxes:
+ ::
+
+ pkcs11-cert-private 0
+ pkcs11-cert-private 1
+
+--pkcs11-id name
+ Specify the serialized certificate id to be used. The id can be gotten
+ by the standalone ``--show-pkcs11-ids`` option.
+
+--pkcs11-id-management
+ Acquire PKCS#11 id from management interface. In this case a
+ :code:`NEED-STR 'pkcs11-id-request'` real-time message will be triggered,
+ application may use pkcs11-id-count command to retrieve available number of
+ certificates, and pkcs11-id-get command to retrieve certificate id and
+ certificate body.
+
+--pkcs11-pin-cache seconds
+ Specify how many seconds the PIN can be cached, the default is until the
+ token is removed.
+
+--pkcs11-private-mode mode
+ Specify which method to use in order to perform private key operations.
+ A different mode can be specified for each provider. Mode is encoded as
+ hex number, and can be a mask one of the following:
+
+ :code:`0` (default) Try to determine automatically.
+
+ :code:`1` Use sign.
+
+ :code:`2` Use sign recover.
+
+ :code:`4` Use decrypt.
+
+ :code:`8` Use unwrap.
+
+--pkcs11-protected-authentication args
+ Use PKCS#11 protected authentication path, useful for biometric and
+ external keypad devices. Every provider has its own setting.
+
+ Valid syntaxes:
+ ::
+
+ pkcs11-protected-authentication 0
+ pkcs11-protected-authentication 1
+
+--pkcs11-providers provider
+ Specify an RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ (Cryptoki) providers to load. This option can be used instead of
+ ``--cert``, ``--key`` and ``--pkcs12``.
+
+ If p11-kit is present on the system, its :code:`p11-kit-proxy.so` module
+ will be loaded by default if either the ``--pkcs11-id`` or
+ ``--pkcs11-id-management`` options are specified without
+ ``--pkcs11-provider`` being given.
+
+--show-pkcs11-ids args
+ (Standalone) Show PKCS#11 token object list.
+
+ Valid syntax:
+ ::
+
+ show-pkcs11 [provider] [cert_private]
+
+ Specify ``cert_private`` as :code:`1` if certificates are stored as
+ private objects.
+
+ If *p11-kit* is present on the system, the ``provider`` argument is
+ optional; if omitted the default :code:`p11-kit-proxy.so` module will be
+ queried.
+
+ ``--verb`` option can be used BEFORE this option to produce debugging
+ information.
diff --git a/doc/man-sections/plugin-options.rst b/doc/man-sections/plugin-options.rst
new file mode 100644
index 0000000..51c574f
--- /dev/null
+++ b/doc/man-sections/plugin-options.rst
@@ -0,0 +1,57 @@
+Plug-in Interface Options
+-------------------------
+
+OpenVPN can be extended by loading external plug-in modules at runtime. These
+plug-ins must be prebuilt and adhere to the OpenVPN Plug-In API.
+
+--plugin args
+ Loads an OpenVPN plug-in module.
+
+ Valid syntax:
+ ::
+
+ plugin module-name
+ plugin module-name "arguments"
+
+ The ``module-name`` needs to be the first
+ argument, indicating the plug-in to load. The second argument is an
+ optional init string which will be passed directly to the plug-in.
+ If the init consists of multiple arguments it must be enclosed in
+ double-quotes (\"). Multiple plugin modules may be loaded into one
+ OpenVPN process.
+
+ The ``module-name`` argument can be just a filename or a filename
+ with a relative or absolute path. The format of the filename and path
+ defines if the plug-in will be loaded from a default plug-in directory
+ or outside this directory.
+ ::
+
+ --plugin path Effective directory used
+ ===================== =============================
+ myplug.so DEFAULT_DIR/myplug.so
+ subdir/myplug.so DEFAULT_DIR/subdir/myplug.so
+ ./subdir/myplug.so CWD/subdir/myplug.so
+ /usr/lib/my/plug.so /usr/lib/my/plug.so
+
+
+ ``DEFAULT_DIR`` is replaced by the default plug-in directory, which is
+ configured at the build time of OpenVPN. ``CWD`` is the current directory
+ where OpenVPN was started or the directory OpenVPN have switched into
+ via the ``--cd`` option before the ``--plugin`` option.
+
+ For more information and examples on how to build OpenVPN plug-in
+ modules, see the README file in the ``plugin`` folder of the OpenVPN
+ source distribution.
+
+ If you are using an RPM install of OpenVPN, see
+ :code:`/usr/share/openvpn/plugin`. The documentation is in ``doc`` and
+ the actual plugin modules are in ``lib``.
+
+ Multiple plugin modules can be cascaded, and modules can be used in
+ tandem with scripts. The modules will be called by OpenVPN in the order
+ that they are declared in the config file. If both a plugin and script
+ are configured for the same callback, the script will be called last. If
+ the return code of the module/script controls an authentication function
+ (such as tls-verify, auth-user-pass-verify, or client-connect), then
+ every module and script must return success (:code:`0`) in order for the
+ connection to be authenticated.
diff --git a/doc/man-sections/protocol-options.rst b/doc/man-sections/protocol-options.rst
new file mode 100644
index 0000000..e9d5d63
--- /dev/null
+++ b/doc/man-sections/protocol-options.rst
@@ -0,0 +1,281 @@
+Protocol options
+----------------
+Options in this section affect features available in the OpenVPN wire
+protocol. Many of these options also define the encryption options
+of the data channel in the OpenVPN wire protocol. These options must be
+configured in a compatible way between both the local and remote side.
+
+--allow-compression mode
+ As described in the ``--compress`` option, compression is a potentially
+ dangerous option. This option allows controlling the behaviour of
+ OpenVPN when compression is used and allowed.
+
+ Valid syntaxes:
+ ::
+
+ allow-compression
+ allow-compression mode
+
+ The ``mode`` argument can be one of the following values:
+
+ :code:`asym` (default)
+ OpenVPN will only *decompress downlink packets* but *not compress
+ uplink packets*. This also allows migrating to disable compression
+ when changing both server and client configurations to remove
+ compression at the same time is not a feasible option.
+
+ :code:`no`
+ OpenVPN will refuse any non-stub compression.
+
+ :code:`yes`
+ OpenVPN will send and receive compressed packets.
+
+--auth alg
+ Authenticate data channel packets and (if enabled) ``tls-auth`` control
+ channel packets with HMAC using message digest algorithm ``alg``. (The
+ default is ``SHA1`` ). HMAC is a commonly used message authentication
+ algorithm (MAC) that uses a data string, a secure hash algorithm and a
+ key to produce a digital signature.
+
+ The OpenVPN data channel protocol uses encrypt-then-mac (i.e. first
+ encrypt a packet then HMAC the resulting ciphertext), which prevents
+ padding oracle attacks.
+
+ If an AEAD cipher mode (e.g. GCM) is chosen then the specified ``--auth``
+ algorithm is ignored for the data channel and the authentication method
+ of the AEAD cipher is used instead. Note that ``alg`` still specifies
+ the digest used for ``tls-auth``.
+
+ In static-key encryption mode, the HMAC key is included in the key file
+ generated by ``--genkey``. In TLS mode, the HMAC key is dynamically
+ generated and shared between peers via the TLS control channel. If
+ OpenVPN receives a packet with a bad HMAC it will drop the packet. HMAC
+ usually adds 16 or 20 bytes per packet. Set ``alg=none`` to disable
+ authentication.
+
+ For more information on HMAC see
+ http://www.cs.ucsd.edu/users/mihir/papers/hmac.html
+
+--cipher alg
+ This option is deprecated for server-client mode. ``--data-ciphers``
+ or possibly `--data-ciphers-fallback`` should be used instead.
+
+ Encrypt data channel packets with cipher algorithm ``alg``.
+
+ The default is :code:`BF-CBC`, an abbreviation for Blowfish in Cipher
+ Block Chaining mode. When cipher negotiation (NCP) is allowed,
+ OpenVPN 2.4 and newer on both client and server side will automatically
+ upgrade to :code:`AES-256-GCM`. See ``--data-ciphers`` and
+ ``--ncp-disable`` for more details on NCP.
+
+ Using :code:`BF-CBC` is no longer recommended, because of its 64-bit
+ block size. This small block size allows attacks based on collisions, as
+ demonstrated by SWEET32. See
+ https://community.openvpn.net/openvpn/wiki/SWEET32
+ for details. Due to this, support for :code:`BF-CBC`, :code:`DES`,
+ :code:`CAST5`, :code:`IDEA` and :code:`RC2` ciphers will be removed in
+ OpenVPN 2.6.
+
+ To see other ciphers that are available with OpenVPN, use the
+ ``--show-ciphers`` option.
+
+ Set ``alg`` to :code:`none` to disable encryption.
+
+--compress algorithm
+ **DEPRECATED** Enable a compression algorithm. Compression is generally
+ not recommended. VPN tunnels which use compression are susceptible to
+ the VORALCE attack vector.
+
+ The ``algorithm`` parameter may be :code:`lzo`, :code:`lz4`,
+ :code:`lz4-v2`, :code:`stub`, :code:`stub-v2` or empty.
+ LZO and LZ4 are different compression algorithms, with LZ4 generally
+ offering the best performance with least CPU usage.
+
+ The :code:`lz4-v2` and :code:`stub-v2` variants implement a better
+ framing that does not add overhead when packets cannot be compressed. All
+ other variants always add one extra framing byte compared to no
+ compression framing.
+
+ If the ``algorithm`` parameter is :code:`stub`, :code:`stub-v2` or empty,
+ compression will be turned off, but the packet framing for compression
+ will still be enabled, allowing a different setting to be pushed later.
+ Additionally, :code:`stub` and :code:`stub-v2` wil disable announcing
+ ``lzo`` and ``lz4`` compression support via *IV_* variables to the
+ server.
+
+ Note: the :code:`stub` (or empty) option is NOT compatible with the older
+ option ``--comp-lzo no``.
+
+ ***Security Considerations***
+
+ Compression and encryption is a tricky combination. If an attacker knows
+ or is able to control (parts of) the plain-text of packets that contain
+ secrets, the attacker might be able to extract the secret if compression
+ is enabled. See e.g. the *CRIME* and *BREACH* attacks on TLS and
+ *VORACLE* on VPNs which also leverage to break encryption. If you are not
+ entirely sure that the above does not apply to your traffic, you are
+ advised to *not* enable compression.
+
+--comp-lzo mode
+ **DEPRECATED** Enable LZO compression algorithm. Compression is
+ generally not recommended. VPN tunnels which uses compression are
+ suspectible to the VORALCE attack vector.
+
+ Use LZO compression -- may add up to 1 byte per packet for incompressible
+ data. ``mode`` may be :code:`yes`, :code:`no`, or :code:`adaptive`
+ (default).
+
+ In a server mode setup, it is possible to selectively turn compression
+ on or off for individual clients.
+
+ First, make sure the client-side config file enables selective
+ compression by having at least one ``--comp-lzo`` directive, such as
+ ``--comp-lzo no``. This will turn off compression by default, but allow
+ a future directive push from the server to dynamically change the
+ :code:`on`/:code:`off`/:code:`adaptive` setting.
+
+ Next in a ``--client-config-dir`` file, specify the compression setting
+ for the client, for example:
+ ::
+
+ comp-lzo yes
+ push "comp-lzo yes"
+
+ The first line sets the ``comp-lzo`` setting for the server side of the
+ link, the second sets the client side.
+
+--comp-noadapt
+ **DEPRECATED** When used in conjunction with ``--comp-lzo``, this option
+ will disable OpenVPN's adaptive compression algorithm. Normally, adaptive
+ compression is enabled with ``--comp-lzo``.
+
+ Adaptive compression tries to optimize the case where you have
+ compression enabled, but you are sending predominantly incompressible
+ (or pre-compressed) packets over the tunnel, such as an FTP or rsync
+ transfer of a large, compressed file. With adaptive compression, OpenVPN
+ will periodically sample the compression process to measure its
+ efficiency. If the data being sent over the tunnel is already
+ compressed, the compression efficiency will be very low, triggering
+ openvpn to disable compression for a period of time until the next
+ re-sample test.
+
+--key-direction
+ Alternative way of specifying the optional direction parameter for the
+ ``--tls-auth`` and ``--secret`` options. Useful when using inline files
+ (See section on inline files).
+
+--keysize n
+ **DEPRECATED** This option will be removed in OpenVPN 2.6.
+
+ Size of cipher key in bits (optional). If unspecified, defaults to
+ cipher-specific default. The ``--show-ciphers`` option (see below) shows
+ all available OpenSSL ciphers, their default key sizes, and whether the
+ key size can be changed. Use care in changing a cipher's default key
+ size. Many ciphers have not been extensively cryptanalyzed with
+ non-standard key lengths, and a larger key may offer no real guarantee
+ of greater security, or may even reduce security.
+
+--data-ciphers cipher-list
+ Restrict the allowed ciphers to be negotiated to the ciphers in
+ ``cipher-list``. ``cipher-list`` is a colon-separated list of ciphers,
+ and defaults to :code:`AES-256-GCM:AES-128-GCM`.
+
+ For servers, the first cipher from ``cipher-list`` that is also
+ supported by the client will be pushed to clients that support cipher
+ negotiation.
+
+ Cipher negotiation is enabled in client-server mode only. I.e. if
+ ``--mode`` is set to 'server' (server-side, implied by setting
+ ``--server`` ), or if ``--pull`` is specified (client-side, implied by
+ setting --client).
+
+ If no common cipher is found during cipher negotiation, the connection
+ is terminated. To support old clients/old servers that do not provide any
+ cipher negotiation support see ``--data-ciphers-fallback``.
+
+ Additionally, to allow for more smooth transition, if NCP is enabled,
+ OpenVPN will inherit the cipher of the peer if that cipher is different
+ from the local ``--cipher`` setting, but the peer cipher is one of the
+ ciphers specified in ``--data-ciphers``. E.g. a non-NCP client (<=v2.3,
+ or with --ncp-disabled set) connecting to a NCP server (v2.4+) with
+ ``--cipher BF-CBC`` and ``--data-ciphers AES-256-GCM:AES-256-CBC`` set can
+ either specify ``--cipher BF-CBC`` or ``--cipher AES-256-CBC`` and both
+ will work.
+
+ Note for using NCP with an OpenVPN 2.4 peer: This list must include the
+ :code:`AES-256-GCM` and :code:`AES-128-GCM` ciphers.
+
+ This list is restricted to be 127 chars long after conversion to OpenVPN
+ ciphers.
+
+ This option was called ``--ncp-ciphers`` in OpenVPN 2.4 but has been renamed
+ to ``--data-ciphers`` in OpenVPN 2.5 to more accurately reflect its meaning.
+
+--data-ciphers-fallback alg
+
+ Configure a cipher that is used to fall back to if we could not determine
+ which cipher the peer is willing to use.
+
+ This option should only be needed to
+ connect to peers that are running OpenVPN 2.3 and older version, and
+ have been configured with `--enable-small`
+ (typically used on routers or other embedded devices).
+
+--ncp-disable
+ **DEPRECATED** Disable "Negotiable Crypto Parameters". This completely
+ disables cipher negotiation.
+
+--secret args
+ Enable Static Key encryption mode (non-TLS). Use pre-shared secret
+ ``file`` which was generated with ``--genkey``.
+
+ Valid syntaxes:
+ ::
+
+ secret file
+ secret file direction
+
+ The optional ``direction`` parameter enables the use of 4 distinct keys
+ (HMAC-send, cipher-encrypt, HMAC-receive, cipher-decrypt), so that each
+ data flow direction has a different set of HMAC and cipher keys. This
+ has a number of desirable security properties including eliminating
+ certain kinds of DoS and message replay attacks.
+
+ When the ``direction`` parameter is omitted, 2 keys are used
+ bidirectionally, one for HMAC and the other for encryption/decryption.
+
+ The ``direction`` parameter should always be complementary on either
+ side of the connection, i.e. one side should use :code:`0` and the other
+ should use :code:`1`, or both sides should omit it altogether.
+
+ The ``direction`` parameter requires that ``file`` contains a 2048 bit
+ key. While pre-1.5 versions of OpenVPN generate 1024 bit key files, any
+ version of OpenVPN which supports the ``direction`` parameter, will also
+ support 2048 bit key file generation using the ``--genkey`` option.
+
+ Static key encryption mode has certain advantages, the primary being
+ ease of configuration.
+
+ There are no certificates or certificate authorities or complicated
+ negotiation handshakes and protocols. The only requirement is that you
+ have a pre-existing secure channel with your peer (such as ``ssh``) to
+ initially copy the key. This requirement, along with the fact that your
+ key never changes unless you manually generate a new one, makes it
+ somewhat less secure than TLS mode (see below). If an attacker manages
+ to steal your key, everything that was ever encrypted with it is
+ compromised. Contrast that to the perfect forward secrecy features of
+ TLS mode (using Diffie Hellman key exchange), where even if an attacker
+ was able to steal your private key, he would gain no information to help
+ him decrypt past sessions.
+
+ Another advantageous aspect of Static Key encryption mode is that it is
+ a handshake-free protocol without any distinguishing signature or
+ feature (such as a header or protocol handshake sequence) that would
+ mark the ciphertext packets as being generated by OpenVPN. Anyone
+ eavesdropping on the wire would see nothing but random-looking data.
+
+--tran-window n
+ Transition window -- our old key can live this many seconds after a new
+ a key renegotiation begins (default :code:`3600` seconds). This feature
+ allows for a graceful transition from old to new key, and removes the key
+ renegotiation sequence from the critical path of tunnel data forwarding.
diff --git a/doc/man-sections/proxy-options.rst b/doc/man-sections/proxy-options.rst
new file mode 100644
index 0000000..465bea0
--- /dev/null
+++ b/doc/man-sections/proxy-options.rst
@@ -0,0 +1,65 @@
+--show-proxy-settings
+ Show sensed HTTP or SOCKS proxy settings. Currently, only Windows
+ clients support this option.
+
+--http-proxy args
+ Connect to remote host through an HTTP proxy. This requires at least an
+ address ``server`` and ``port`` argument. If HTTP Proxy-Authenticate
+ is required, a file name to an ``authfile`` file containing a username
+ and password on 2 lines can be given, or :code:`stdin` to prompt from
+ console. Its content can also be specified in the config file with the
+ ``--http-proxy-user-pass`` option. (See section on inline files)
+
+ The last optional argument is an ``auth-method`` which should be one
+ of :code:`none`, :code:`basic`, or :code:`ntlm`.
+
+ HTTP Digest authentication is supported as well, but only via the
+ :code:`auto` or :code:`auto-nct` flags (below). This must replace
+ the ``authfile`` argument.
+
+ The :code:`auto` flag causes OpenVPN to automatically determine the
+ ``auth-method`` and query stdin or the management interface for
+ username/password credentials, if required. This flag exists on OpenVPN
+ 2.1 or higher.
+
+ The ``auto-nct`` flag (no clear-text auth) instructs OpenVPN to
+ automatically determine the authentication method, but to reject weak
+ authentication protocols such as HTTP Basic Authentication.
+
+ Examples:
+ ::
+
+ http-proxy proxy.example.net 3128
+ http-proxy proxy.example.net 3128 authfile.txt
+ http-proxy proxy.example.net 3128 stdin
+ http-proxy proxy.example.net 3128 auto basic
+ http-proxy proxy.example.net 3128 auto-nct ntlm
+
+--http-proxy-option args
+ Set extended HTTP proxy options. Requires an option ``type`` as argument
+ and an optional ``parameter`` to the type. Repeat to set multiple
+ options.
+
+ :code:`VERSION` ``version``
+ Set HTTP version number to ``version`` (default :code:`1.0`).
+
+ :code:`AGENT` ``user-agent``
+ Set HTTP "User-Agent" string to ``user-agent``.
+
+ :code:`CUSTOM-HEADER` ``name`` ``content``
+ Adds the custom Header with ``name`` as name and ``content`` as
+ the content of the custom HTTP header.
+
+ Examples:
+ ::
+
+ http-proxy-option VERSION 1.1
+ http-proxy-option AGENT OpenVPN/2.4
+ http-proxy-option X-Proxy-Flag some-flags
+
+--socks-proxy args
+ Connect to remote host through a Socks5 proxy. A required ``server``
+ argument is needed. Optionally a ``port`` (default :code:`1080`) and
+ ``authfile`` can be given. The ``authfile`` is a file containing a
+ username and password on 2 lines, or :code:`stdin` can be used to
+ prompt from console.
diff --git a/doc/man-sections/renegotiation.rst b/doc/man-sections/renegotiation.rst
new file mode 100644
index 0000000..c548440
--- /dev/null
+++ b/doc/man-sections/renegotiation.rst
@@ -0,0 +1,52 @@
+Data Channel Renegotiation
+--------------------------
+
+When running OpenVPN in client/server mode, the data channel will use a
+separate ephemeral encryption key which is rotated at regular intervals.
+
+--reneg-bytes n
+ Renegotiate data channel key after ``n`` bytes sent or received
+ (disabled by default with an exception, see below). OpenVPN allows the
+ lifetime of a key to be expressed as a number of bytes
+ encrypted/decrypted, a number of packets, or a number of seconds. A key
+ renegotiation will be forced if any of these three criteria are met by
+ either peer.
+
+ If using ciphers with cipher block sizes less than 128-bits,
+ ``--reneg-bytes`` is set to 64MB by default, unless it is explicitly
+ disabled by setting the value to :code:`0`, but this is
+ **HIGHLY DISCOURAGED** as this is designed to add some protection against
+ the SWEET32 attack vector. For more information see the ``--cipher``
+ option.
+
+--reneg-pkts n
+ Renegotiate data channel key after **n** packets sent and received
+ (disabled by default).
+
+--reneg-sec args
+ Renegotiate data channel key after at most ``max`` seconds
+ (default :code:`3600`) and at least ``min`` seconds (default is 90% of
+ ``max`` for servers, and equal to ``max`` for clients).
+ ::
+
+ reneg-sec max [min]
+
+ The effective ``--reneg-sec`` value used is per session
+ pseudo-uniform-randomized between ``min`` and ``max``.
+
+ With the default value of :code:`3600` this results in an effective per
+ session value in the range of :code:`3240` .. :code:`3600` seconds for
+ servers, or just 3600 for clients.
+
+ When using dual-factor authentication, note that this default value may
+ cause the end user to be challenged to reauthorize once per hour.
+
+ Also, keep in mind that this option can be used on both the client and
+ server, and whichever uses the lower value will be the one to trigger
+ the renegotiation. A common mistake is to set ``--reneg-sec`` to a
+ higher value on either the client or server, while the other side of the
+ connection is still using the default value of :code:`3600` seconds,
+ meaning that the renegotiation will still occur once per :code:`3600`
+ seconds. The solution is to increase --reneg-sec on both the client and
+ server, or set it to :code:`0` on one side of the connection (to
+ disable), and to your chosen value on the other side.
diff --git a/doc/man-sections/script-options.rst b/doc/man-sections/script-options.rst
new file mode 100644
index 0000000..03b3dd7
--- /dev/null
+++ b/doc/man-sections/script-options.rst
@@ -0,0 +1,841 @@
+SCRIPTING INTEGRATION
+=====================
+
+OpenVPN can execute external scripts in various phases of the lifetime of
+the OpenVPN process.
+
+
+Script Order of Execution
+-------------------------
+
+#. ``--up``
+
+ Executed after TCP/UDP socket bind and TUN/TAP open.
+
+#. ``--tls-verify``
+
+ Executed when we have a still untrusted remote peer.
+
+#. ``--ipchange``
+
+ Executed after connection authentication, or remote IP address change.
+
+#. ``--client-connect``
+
+ Executed in **--mode server** mode immediately after client
+ authentication.
+
+#. ``--route-up``
+
+ Executed after connection authentication, either immediately after, or
+ some number of seconds after as defined by the **--route-delay** option.
+
+#. ``--route-pre-down``
+
+ Executed right before the routes are removed.
+
+#. ``--client-disconnect``
+
+ Executed in ``--mode server`` mode on client instance shutdown.
+
+#. ``--down``
+
+ Executed after TCP/UDP and TUN/TAP close.
+
+#. ``--learn-address``
+
+ Executed in ``--mode server`` mode whenever an IPv4 address/route or MAC
+ address is added to OpenVPN's internal routing table.
+
+#. ``--auth-user-pass-verify``
+
+ Executed in ``--mode server`` mode on new client connections, when the
+ client is still untrusted.
+
+SCRIPT HOOKS
+------------
+
+--auth-user-pass-verify args
+ Require the client to provide a username/password (possibly in addition
+ to a client certificate) for authentication.
+
+ Valid syntax:
+ ::
+
+ auth-user-pass-verify cmd method
+
+ OpenVPN will run command ``cmd`` to validate the username/password
+ provided by the client.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ If ``method`` is set to :code:`via-env`, OpenVPN will call ``script``
+ with the environmental variables :code:`username` and :code:`password`
+ set to the username/password strings provided by the client. *Beware*
+ that this method is insecure on some platforms which make the environment
+ of a process publicly visible to other unprivileged processes.
+
+ If ``method`` is set to :code:`via-file`, OpenVPN will write the username
+ and password to the first two lines of a temporary file. The filename
+ will be passed as an argument to ``script``, and the file will be
+ automatically deleted by OpenVPN after the script returns. The location
+ of the temporary file is controlled by the ``--tmp-dir`` option, and
+ will default to the current directory if unspecified. For security,
+ consider setting ``--tmp-dir`` to a volatile storage medium such as
+ :code:`/dev/shm` (if available) to prevent the username/password file
+ from touching the hard drive.
+
+ The script should examine the username and password, returning a success
+ exit code (:code:`0`) if the client's authentication request is to be
+ accepted, or a failure code (:code:`1`) to reject the client.
+
+ This directive is designed to enable a plugin-style interface for
+ extending OpenVPN's authentication capabilities.
+
+ To protect against a client passing a maliciously formed username or
+ password string, the username string must consist only of these
+ characters: alphanumeric, underbar (':code:`_`'), dash (':code:`-`'),
+ dot (':code:`.`'), or at (':code:`@`'). The password string can consist
+ of any printable characters except for CR or LF. Any illegal characters
+ in either the username or password string will be converted to
+ underbar (':code:`_`').
+
+ Care must be taken by any user-defined scripts to avoid creating a
+ security vulnerability in the way that these strings are handled. Never
+ use these strings in such a way that they might be escaped or evaluated
+ by a shell interpreter.
+
+ For a sample script that performs PAM authentication, see
+ :code:`sample-scripts/auth-pam.pl` in the OpenVPN source distribution.
+
+--client-connect cmd
+ Run command ``cmd`` on client connection.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ The command is passed the common name and IP address of the
+ just-authenticated client as environmental variables (see environmental
+ variable section below). The command is also passed the pathname of a
+ freshly created temporary file as the last argument (after any arguments
+ specified in ``cmd`` ), to be used by the command to pass dynamically
+ generated config file directives back to OpenVPN.
+
+ If the script wants to generate a dynamic config file to be applied on
+ the server when the client connects, it should write it to the file
+ named by the last argument.
+
+ See the ``--client-config-dir`` option below for options which can be
+ legally used in a dynamically generated config file.
+
+ Note that the return value of ``script`` is significant. If ``script``
+ returns a non-zero error status, it will cause the client to be
+ disconnected.
+
+ If a ``--client-connect`` wants to defer the generating of the
+ configuration then the script needs to use the
+ :code:`client_connect_deferred_file` and
+ :code:`client_connect_config_file` environment variables, and write
+ status accordingly into these files. See the `Environmental Variables`_
+ section for more details.
+
+--client-disconnect cmd
+ Like ``--client-connect`` but called on client instance shutdown. Will
+ not be called unless the ``--client-connect`` script and plugins (if
+ defined) were previously called on this instance with successful (0)
+ status returns.
+
+ The exception to this rule is if the ``--client-disconnect`` command or
+ plugins are cascaded, and at least one client-connect function
+ succeeded, then ALL of the client-disconnect functions for scripts and
+ plugins will be called on client instance object deletion, even in cases
+ where some of the related client-connect functions returned an error
+ status.
+
+ The ``--client-disconnect`` command is not passed any extra arguments
+ (only those arguments specified in cmd, if any).
+
+--down cmd
+ Run command ``cmd`` after TUN/TAP device close (post ``--user`` UID
+ change and/or ``--chroot`` ). ``cmd`` consists of a path to script (or
+ executable program), optionally followed by arguments. The path and
+ arguments may be single- or double-quoted and/or escaped using a
+ backslash, and should be separated by one or more spaces.
+
+ Called with the same parameters and environmental variables as the
+ ``--up`` option above.
+
+ Note that if you reduce privileges by using ``--user`` and/or
+ ``--group``, your ``--down`` script will also run at reduced privilege.
+
+--down-pre
+ Call ``--down`` cmd/script before, rather than after, TUN/TAP close.
+
+--ipchange cmd
+ Run command ``cmd`` when our remote ip-address is initially
+ authenticated or changes.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ When ``cmd`` is executed two arguments are appended after any arguments
+ specified in ``cmd`` , as follows:
+ ::
+
+ cmd ip address port number
+
+ Don't use ``--ipchange`` in ``--mode server`` mode. Use a
+ ``--client-connect`` script instead.
+
+ See the `Environmental Variables`_ section below for additional
+ parameters passed as environmental variables.
+
+ If you are running in a dynamic IP address environment where the IP
+ addresses of either peer could change without notice, you can use this
+ script, for example, to edit the :code:`/etc/hosts` file with the current
+ address of the peer. The script will be run every time the remote peer
+ changes its IP address.
+
+ Similarly if *our* IP address changes due to DHCP, we should configure
+ our IP address change script (see man page for ``dhcpcd``\(8)) to
+ deliver a ``SIGHUP`` or ``SIGUSR1`` signal to OpenVPN. OpenVPN will
+ then re-establish a connection with its most recently authenticated
+ peer on its new IP address.
+
+--learn-address cmd
+ Run command ``cmd`` to validate client virtual addresses or routes.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ Three arguments will be appended to any arguments in ``cmd`` as follows:
+
+ :code:`$1` - [operation]
+ :code:`"add"`, :code:`"update"`, or :code:`"delete"` based on whether
+ or not the address is being added to, modified, or deleted from
+ OpenVPN's internal routing table.
+
+ :code:`$2` - [address]
+ The address being learned or unlearned. This can be an IPv4 address
+ such as :code:`"198.162.10.14"`, an IPv4 subnet such as
+ :code:`"198.162.10.0/24"`, or an ethernet MAC address (when
+ ``--dev tap`` is being used) such as :code:`"00:FF:01:02:03:04"`.
+
+ :code:`$3` - [common name]
+ The common name on the certificate associated with the client linked
+ to this address. Only present for :code:`"add"` or :code:`"update"`
+ operations, not :code:`"delete"`.
+
+ On :code:`"add"` or :code:`"update"` methods, if the script returns
+ a failure code (non-zero), OpenVPN will reject the address and will not
+ modify its internal routing table.
+
+ Normally, the ``cmd`` script will use the information provided above to
+ set appropriate firewall entries on the VPN TUN/TAP interface. Since
+ OpenVPN provides the association between virtual IP or MAC address and
+ the client's authenticated common name, it allows a user-defined script
+ to configure firewall access policies with regard to the client's
+ high-level common name, rather than the low level client virtual
+ addresses.
+
+--route-up cmd
+ Run command ``cmd`` after routes are added, subject to ``--route-delay``.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ See the `Environmental Variables`_ section below for additional
+ parameters passed as environmental variables.
+
+--route-pre-down cmd
+ Run command ``cmd`` before routes are removed upon disconnection.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ See the `Environmental Variables`_ section below for additional
+ parameters passed as environmental variables.
+
+--setenv args
+ Set a custom environmental variable :code:`name=value` to pass to script.
+
+ Valid syntaxes:
+ ::
+
+ setenv name value
+ setenv FORWARD_COMPATIBLE 1
+ setenv opt config_option
+
+ By setting :code:`FORWARD_COMPATIBLE` to :code:`1`, the config file
+ syntax checking is relaxed so that unknown directives will trigger a
+ warning but not a fatal error, on the assumption that a given unknown
+ directive might be valid in future OpenVPN versions.
+
+ This option should be used with caution, as there are good security
+ reasons for having OpenVPN fail if it detects problems in a config file.
+ Having said that, there are valid reasons for wanting new software
+ features to gracefully degrade when encountered by older software
+ versions.
+
+ It is also possible to tag a single directive so as not to trigger a
+ fatal error if the directive isn't recognized. To do this, prepend the
+ following before the directive: ``setenv opt``
+
+ Versions prior to OpenVPN 2.3.3 will always ignore options set with the
+ ``setenv opt`` directive.
+
+ See also ``--ignore-unknown-option``
+
+--setenv-safe args
+ Set a custom environmental variable :code:`OPENVPN_name` to :code:`value`
+ to pass to scripts.
+
+ Valid syntaxes:
+ ::
+
+ setenv-safe name value
+
+ This directive is designed to be pushed by the server to clients, and
+ the prepending of :code:`OPENVPN_` to the environmental variable is a
+ safety precaution to prevent a :code:`LD_PRELOAD` style attack from a
+ malicious or compromised server.
+
+--tls-verify cmd
+ Run command ``cmd`` to verify the X509 name of a pending TLS connection
+ that has otherwise passed all other tests of certification (except for
+ revocation via ``--crl-verify`` directive; the revocation test occurs
+ after the ``--tls-verify`` test).
+
+ ``cmd`` should return :code:`0` to allow the TLS handshake to proceed,
+ or :code:`1` to fail.
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ When ``cmd`` is executed two arguments are appended after any arguments
+ specified in ``cmd``, as follows:
+ ::
+
+ cmd certificate_depth subject
+
+ These arguments are, respectively, the current certificate depth and the
+ X509 subject distinguished name (dn) of the peer.
+
+ This feature is useful if the peer you want to trust has a certificate
+ which was signed by a certificate authority who also signed many other
+ certificates, where you don't necessarily want to trust all of them, but
+ rather be selective about which peer certificate you will accept. This
+ feature allows you to write a script which will test the X509 name on a
+ certificate and decide whether or not it should be accepted. For a
+ simple perl script which will test the common name field on the
+ certificate, see the file ``verify-cn`` in the OpenVPN distribution.
+
+ See the `Environmental Variables`_ section below for additional
+ parameters passed as environmental variables.
+
+--up cmd
+ Run command ``cmd`` after successful TUN/TAP device open (pre ``--user``
+ UID change).
+
+ ``cmd`` consists of a path to a script (or executable program), optionally
+ followed by arguments. The path and arguments may be single- or
+ double-quoted and/or escaped using a backslash, and should be separated
+ by one or more spaces.
+
+ The up command is useful for specifying route commands which route IP
+ traffic destined for private subnets which exist at the other end of the
+ VPN connection into the tunnel.
+
+ For ``--dev tun`` execute as:
+ ::
+
+ cmd tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_remote_ip [init | restart]
+
+ For ``--dev tap`` execute as:
+ ::
+
+ cmd tap_dev tap_mtu link_mtu ifconfig_local_ip ifconfig_netmask [init | restart]
+
+ See the `Environmental Variables`_ section below for additional
+ parameters passed as environmental variables.
+
+ Note that if ``cmd`` includes arguments, all OpenVPN-generated arguments
+ will be appended to them to build an argument list with which the
+ executable will be called.
+
+ Typically, ``cmd`` will run a script to add routes to the tunnel.
+
+ Normally the up script is called after the TUN/TAP device is opened. In
+ this context, the last command line parameter passed to the script will
+ be *init.* If the ``--up-restart`` option is also used, the up script
+ will be called for restarts as well. A restart is considered to be a
+ partial reinitialization of OpenVPN where the TUN/TAP instance is
+ preserved (the ``--persist-tun`` option will enable such preservation).
+ A restart can be generated by a SIGUSR1 signal, a ``--ping-restart``
+ timeout, or a connection reset when the TCP protocol is enabled with the
+ ``--proto`` option. If a restart occurs, and ``--up-restart`` has been
+ specified, the up script will be called with *restart* as the last
+ parameter.
+
+ *NOTE:*
+ On restart, OpenVPN will not pass the full set of environment
+ variables to the script. Namely, everything related to routing and
+ gateways will not be passed, as nothing needs to be done anyway - all
+ the routing setup is already in place. Additionally, the up-restart
+ script will run with the downgraded UID/GID settings (if configured).
+
+ The following standalone example shows how the ``--up`` script can be
+ called in both an initialization and restart context. (*NOTE:* for
+ security reasons, don't run the following example unless UDP port 9999
+ is blocked by your firewall. Also, the example will run indefinitely, so
+ you should abort with control-c).
+
+ ::
+
+ openvpn --dev tun --port 9999 --verb 4 --ping-restart 10 \
+ --up 'echo up' --down 'echo down' --persist-tun \
+ --up-restart
+
+ Note that OpenVPN also provides the ``--ifconfig`` option to
+ automatically ifconfig the TUN device, eliminating the need to define an
+ ``--up`` script, unless you also want to configure routes in the
+ ``--up`` script.
+
+ If ``--ifconfig`` is also specified, OpenVPN will pass the ifconfig
+ local and remote endpoints on the command line to the ``--up`` script so
+ that they can be used to configure routes such as:
+
+ ::
+
+ route add -net 10.0.0.0 netmask 255.255.255.0 gw $5
+
+--up-delay
+ Delay TUN/TAP open and possible ``--up`` script execution until after
+ TCP/UDP connection establishment with peer.
+
+ In ``--proto udp`` mode, this option normally requires the use of
+ ``--ping`` to allow connection initiation to be sensed in the absence of
+ tunnel data, since UDP is a "connectionless" protocol.
+
+ On Windows, this option will delay the TAP-Win32 media state
+ transitioning to "connected" until connection establishment, i.e. the
+ receipt of the first authenticated packet from the peer.
+
+--up-restart
+ Enable the ``--up`` and ``--down`` scripts to be called for restarts as
+ well as initial program start. This option is described more fully above
+ in the ``--up`` option documentation.
+
+String Types and Remapping
+--------------------------
+
+In certain cases, OpenVPN will perform remapping of characters in
+strings. Essentially, any characters outside the set of permitted
+characters for each string type will be converted to underbar ('\_').
+
+*Q: Why is string remapping necessary?*
+ It's an important security feature to prevent the malicious
+ coding of strings from untrusted sources to be passed as parameters to
+ scripts, saved in the environment, used as a common name, translated to
+ a filename, etc.
+
+*Q: Can string remapping be disabled?*
+ Yes, by using the ``--no-name-remapping`` option, however this
+ should be considered an advanced option.
+
+Here is a brief rundown of OpenVPN's current string types and the
+permitted character class for each string:
+
+*X509 Names*
+ Alphanumeric, underbar ('\_'), dash ('-'), dot ('.'), at
+ ('@'), colon (':'), slash ('/'), and equal ('='). Alphanumeric is
+ defined as a character which will cause the C library isalnum() function
+ to return true.
+
+*Common Names*
+ Alphanumeric, underbar ('\_'), dash ('-'), dot ('.'), and at ('@').
+
+*--auth-user-pass username*
+ Same as Common Name, with one exception:
+ starting with OpenVPN 2.0.1, the username is passed to the
+ :code:`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY` plugin in its raw form,
+ without string remapping.
+
+*--auth-user-pass password*
+ Any "printable" character except CR or LF. Printable is defined to be
+ a character which will cause the C library isprint() function to
+ return true.
+
+*--client-config-dir filename as derived from common name or`username*
+ Alphanumeric, underbar ('\_'), dash ('-'), and dot ('.') except for "."
+ or ".." as standalone strings. As of v2.0.1-rc6, the at ('@') character
+ has been added as well for compatibility with the common name character
+ class.
+
+*Environmental variable names*
+ Alphanumeric or underbar ('\_').
+
+*Environmental variable values*
+ Any printable character.
+
+For all cases, characters in a string which are not members of the legal
+character class for that string type will be remapped to underbar
+('\_').  
+
+
+Environmental Variables
+-----------------------
+
+Once set, a variable is persisted indefinitely until it is reset by a
+new value or a restart,
+
+As of OpenVPN 2.0-beta12, in server mode, environmental variables set by
+OpenVPN are scoped according to the client objects they are associated
+with, so there should not be any issues with scripts having access to
+stale, previously set variables which refer to different client
+instances.
+
+:code:`bytes_received`
+ Total number of bytes received from client during VPN session. Set prior
+ to execution of the ``--client-disconnect`` script.
+
+:code:`bytes_sent`
+ Total number of bytes sent to client during VPN session. Set prior to
+ execution of the ``--client-disconnect`` script.
+
+:code:`client_connect_config_file`
+ The path to the configuration file that should be written to by the
+ ``--client-connect`` script (optional, if per-session configuration
+ is desired). This is the same file name as passed via command line
+ argument on the call to the ``--client-connect`` script.
+
+:code:`client_connect_deferred_file`
+ This file can be optionally written to in order to to communicate a
+ status code of the ``--client-connect`` script or plgin. Only the
+ first character in the file is relevant. It must be either :code:`1`
+ to indicate normal script execution, :code:`0` indicates an error (in
+ the same way that a non zero exit status does) or :code:`2` to indicate
+ that the script deferred returning the config file.
+
+ For deferred (background) handling, the script or plugin MUST write
+ :code:`2` to the file to indicate the deferral and then return with
+ exit code :code:`0` to signal ``deferred handler started OK``.
+
+ A background process or similar must then take care of writing the
+ configuration to the file indicated by the
+ :code:`client_connect_config_file` environment variable and when
+ finished, write the a :code:`1` to this file (or :code:`0` in case of
+ an error).
+
+ The absence of any character in the file when the script finishes
+ executing is interpreted the same as :code:`1`. This allows scripts
+ that are not written to support the defer mechanism to be used
+ unmodified.
+
+:code:`common_name`
+ The X509 common name of an authenticated client. Set prior to execution
+ of ``--client-connect``, ``--client-disconnect`` and
+ ``--auth-user-pass-verify`` scripts.
+
+:code:`config`
+ Name of first ``--config`` file. Set on program initiation and reset on
+ SIGHUP.
+
+:code:`daemon`
+ Set to "1" if the ``--daemon`` directive is specified, or "0" otherwise.
+ Set on program initiation and reset on SIGHUP.
+
+:code:`daemon_log_redirect`
+ Set to "1" if the ``--log`` or ``--log-append`` directives are
+ specified, or "0" otherwise. Set on program initiation and reset on
+ SIGHUP.
+
+:code:`dev`
+ The actual name of the TUN/TAP device, including a unit number if it
+ exists. Set prior to ``--up`` or ``--down`` script execution.
+
+:code:`dev_idx`
+ On Windows, the device index of the TUN/TAP adapter (to be used in
+ netsh.exe calls which sometimes just do not work right with interface
+ names). Set prior to ``--up`` or ``--down`` script execution.
+
+:code:`foreign_option_{n}`
+ An option pushed via ``--push`` to a client which does not natively
+ support it, such as ``--dhcp-option`` on a non-Windows system, will be
+ recorded to this environmental variable sequence prior to ``--up``
+ script execution.
+
+:code:`ifconfig_broadcast`
+ The broadcast address for the virtual ethernet segment which is derived
+ from the ``--ifconfig`` option when ``--dev tap`` is used. Set prior to
+ OpenVPN calling the :code:`ifconfig` or :code:`netsh` (windows version
+ of ifconfig) commands which normally occurs prior to ``--up`` script
+ execution.
+
+:code:`ifconfig_ipv6_local`
+ The local VPN endpoint IPv6 address specified in the
+ ``--ifconfig-ipv6`` option (first parameter). Set prior to OpenVPN
+ calling the :code:`ifconfig` or code:`netsh` (windows version of
+ ifconfig) commands which normally occurs prior to ``--up`` script
+ execution.
+
+:code:`ifconfig_ipv6_netbits`
+ The prefix length of the IPv6 network on the VPN interface. Derived
+ from the /nnn parameter of the IPv6 address in the ``--ifconfig-ipv6``
+ option (first parameter). Set prior to OpenVPN calling the
+ :code:`ifconfig` or :code:`netsh` (windows version of ifconfig)
+ commands which normally occurs prior to ``--up`` script execution.
+
+:code:`ifconfig_ipv6_remote`
+ The remote VPN endpoint IPv6 address specified in the
+ ``--ifconfig-ipv6`` option (second parameter). Set prior to OpenVPN
+ calling the :code:`ifconfig` or :code:`netsh` (windows version of
+ ifconfig) commands which normally occurs prior to ``--up`` script
+ execution.
+
+:code:`ifconfig_local`
+ The local VPN endpoint IP address specified in the ``--ifconfig``
+ option (first parameter). Set prior to OpenVPN calling the
+ :code:`ifconfig` or :code:`netsh` (windows version of ifconfig)
+ commands which normally occurs prior to ``--up`` script execution.
+
+:code:`ifconfig_remote`
+ The remote VPN endpoint IP address specified in the ``--ifconfig``
+ option (second parameter) when ``--dev tun`` is used. Set prior to
+ OpenVPN calling the :code:`ifconfig` or :code:`netsh` (windows version
+ of ifconfig) commands which normally occurs prior to ``--up`` script
+ execution.
+
+:code:`ifconfig_netmask`
+ The subnet mask of the virtual ethernet segment that is specified as
+ the second parameter to ``--ifconfig`` when ``--dev tap`` is being
+ used. Set prior to OpenVPN calling the :code:`ifconfig` or
+ :code:`netsh` (windows version of ifconfig) commands which normally
+ occurs prior to ``--up`` script execution.
+
+:code:`ifconfig_pool_local_ip`
+ The local virtual IP address for the TUN/TAP tunnel taken from an
+ ``--ifconfig-push`` directive if specified, or otherwise from the
+ ifconfig pool (controlled by the ``--ifconfig-pool`` config file
+ directive). Only set for ``--dev tun`` tunnels. This option is set on
+ the server prior to execution of the ``--client-connect`` and
+ ``--client-disconnect`` scripts.
+
+:code:`ifconfig_pool_netmask`
+ The virtual IP netmask for the TUN/TAP tunnel taken from an
+ ``--ifconfig-push`` directive if specified, or otherwise from the
+ ifconfig pool (controlled by the ``--ifconfig-pool`` config file
+ directive). Only set for ``--dev tap`` tunnels. This option is set on
+ the server prior to execution of the ``--client-connect`` and
+ ``--client-disconnect`` scripts.
+
+:code:`ifconfig_pool_remote_ip`
+ The remote virtual IP address for the TUN/TAP tunnel taken from an
+ ``--ifconfig-push`` directive if specified, or otherwise from the
+ ifconfig pool (controlled by the ``--ifconfig-pool`` config file
+ directive). This option is set on the server prior to execution of the
+ ``--client-connect`` and ``--client-disconnect`` scripts.
+
+:code:`link_mtu`
+ The maximum packet size (not including the IP header) of tunnel data in
+ UDP tunnel transport mode. Set prior to ``--up`` or ``--down`` script
+ execution.
+
+:code:`local`
+ The ``--local`` parameter. Set on program initiation and reset on
+ SIGHUP.
+
+:code:`local_port`
+ The local port number or name, specified by ``--port`` or ``--lport``.
+ Set on program initiation and reset on SIGHUP.
+
+:code:`password`
+ The password provided by a connecting client. Set prior to
+ ``--auth-user-pass-verify`` script execution only when the ``via-env``
+ modifier is specified, and deleted from the environment after the script
+ returns.
+
+:code:`proto`
+ The ``--proto`` parameter. Set on program initiation and reset on
+ SIGHUP.
+
+:code:`remote_{n}`
+ The ``--remote`` parameter. Set on program initiation and reset on
+ SIGHUP.
+
+:code:`remote_port_{n}`
+ The remote port number, specified by ``--port`` or ``--rport``. Set on
+ program initiation and reset on SIGHUP.
+
+:code:`route_net_gateway`
+ The pre-existing default IP gateway in the system routing table. Set
+ prior to ``--up`` script execution.
+
+:code:`route_vpn_gateway`
+ The default gateway used by ``--route`` options, as specified in either
+ the ``--route-gateway`` option or the second parameter to
+ ``--ifconfig`` when ``--dev tun`` is specified. Set prior to ``--up``
+ script execution.
+
+:code:`route_{parm}_{n}`
+ A set of variables which define each route to be added, and are set
+ prior to ``--up`` script execution.
+
+ ``parm`` will be one of :code:`network`, :code:`netmask"`,
+ :code:`gateway`, or :code:`metric`.
+
+ ``n`` is the OpenVPN route number, starting from 1.
+
+ If the network or gateway are resolvable DNS names, their IP address
+ translations will be recorded rather than their names as denoted on the
+ command line or configuration file.
+
+:code:`route_ipv6_{parm}_{n}`
+ A set of variables which define each IPv6 route to be added, and are
+ set prior to **--up** script execution.
+
+ ``parm`` will be one of :code:`network`, :code:`gateway` or
+ :code:`metric`. ``route_ipv6_network_{n}`` contains :code:`netmask`
+ as :code:`/nnn`, unlike IPv4 where it is passed in a separate environment
+ variable.
+
+ ``n`` is the OpenVPN route number, starting from 1.
+
+ If the network or gateway are resolvable DNS names, their IP address
+ translations will be recorded rather than their names as denoted on the
+ command line or configuration file.
+
+:code:`peer_cert`
+ Temporary file name containing the client certificate upon connection.
+ Useful in conjunction with ``--tls-verify``.
+
+:code:`script_context`
+ Set to "init" or "restart" prior to up/down script execution. For more
+ information, see documentation for ``--up``.
+
+:code:`script_type`
+ Prior to execution of any script, this variable is set to the type of
+ script being run. It can be one of the following: :code:`up`,
+ :code:`down`, :code:`ipchange`, :code:`route-up`, :code:`tls-verify`,
+ :code:`auth-user-pass-verify`, :code:`client-connect`,
+ :code:`client-disconnect` or :code:`learn-address`. Set prior to
+ execution of any script.
+
+:code:`signal`
+ The reason for exit or restart. Can be one of :code:`sigusr1`,
+ :code:`sighup`, :code:`sigterm`, :code:`sigint`, :code:`inactive`
+ (controlled by ``--inactive`` option), :code:`ping-exit` (controlled
+ by ``--ping-exit`` option), :code:`ping-restart` (controlled by
+ ``--ping-restart`` option), :code:`connection-reset` (triggered on TCP
+ connection reset), :code:`error` or :code:`unknown` (unknown signal).
+ This variable is set just prior to down script execution.
+
+:code:`time_ascii`
+ Client connection timestamp, formatted as a human-readable time string.
+ Set prior to execution of the ``--client-connect`` script.
+
+:code:`time_duration`
+ The duration (in seconds) of the client session which is now
+ disconnecting. Set prior to execution of the ``--client-disconnect``
+ script.
+
+:code:`time_unix`
+ Client connection timestamp, formatted as a unix integer date/time
+ value. Set prior to execution of the ``--client-connect`` script.
+
+:code:`tls_digest_{n}` / :code:`tls_digest_sha256_{n}`
+ Contains the certificate SHA1 / SHA256 fingerprint, where ``n`` is the
+ verification level. Only set for TLS connections. Set prior to execution
+ of ``--tls-verify`` script.
+
+:code:`tls_id_{n}`
+ A series of certificate fields from the remote peer, where ``n`` is the
+ verification level. Only set for TLS connections. Set prior to execution
+ of ``--tls-verify`` script.
+
+:code:`tls_serial_{n}`
+ The serial number of the certificate from the remote peer, where ``n``
+ is the verification level. Only set for TLS connections. Set prior to
+ execution of ``--tls-verify`` script. This is in the form of a decimal
+ string like "933971680", which is suitable for doing serial-based OCSP
+ queries (with OpenSSL, do not prepend "0x" to the string) If something
+ goes wrong while reading the value from the certificate it will be an
+ empty string, so your code should check that. See the
+ :code:`contrib/OCSP_check/OCSP_check.sh` script for an example.
+
+:code:`tls_serial_hex_{n}`
+ Like :code:`tls_serial_{n}`, but in hex form (e.g.
+ :code:`12:34:56:78:9A`).
+
+:code:`tun_mtu`
+ The MTU of the TUN/TAP device. Set prior to ``--up`` or ``--down``
+ script execution.
+
+:code:`trusted_ip` / :code:`trusted_ip6`)
+ Actual IP address of connecting client or peer which has been
+ authenticated. Set prior to execution of ``--ipchange``,
+ ``--client-connect`` and ``--client-disconnect`` scripts. If using ipv6
+ endpoints (udp6, tcp6), :code:`trusted_ip6` will be set instead.
+
+:code:`trusted_port`
+ Actual port number of connecting client or peer which has been
+ authenticated. Set prior to execution of ``--ipchange``,
+ ``--client-connect`` and ``--client-disconnect`` scripts.
+
+:code:`untrusted_ip` / :code:`untrusted_ip6`
+ Actual IP address of connecting client or peer which has not been
+ authenticated yet. Sometimes used to *nmap* the connecting host in a
+ ``--tls-verify`` script to ensure it is firewalled properly. Set prior
+ to execution of ``--tls-verify`` and ``--auth-user-pass-verify``
+ scripts. If using ipv6 endpoints (udp6, tcp6), :code:`untrusted_ip6`
+ will be set instead.
+
+:code:`untrusted_port`
+ Actual port number of connecting client or peer which has not been
+ authenticated yet. Set prior to execution of ``--tls-verify`` and
+ ``--auth-user-pass-verify`` scripts.
+
+:code:`username`
+ The username provided by a connecting client. Set prior to
+ ``--auth-user-pass-verify`` script execution only when the
+ :code:`via-env` modifier is specified.
+
+:code:`X509_{n}_{subject_field}`
+ An X509 subject field from the remote peer certificate, where ``n`` is
+ the verification level. Only set for TLS connections. Set prior to
+ execution of ``--tls-verify`` script. This variable is similar to
+ :code:`tls_id_{n}` except the component X509 subject fields are broken
+ out, and no string remapping occurs on these field values (except for
+ remapping of control characters to ":code:`_`"). For example, the
+ following variables would be set on the OpenVPN server using the sample
+ client certificate in sample-keys (client.crt). Note that the
+ verification level is 0 for the client certificate and 1 for the CA
+ certificate.
+
+ ::
+
+ X509_0_emailAddress=me@myhost.mydomain
+ X509_0_CN=Test-Client
+ X509_0_O=OpenVPN-TEST
+ X509_0_ST=NA
+ X509_0_C=KG
+ X509_1_emailAddress=me@myhost.mydomain
+ X509_1_O=OpenVPN-TEST
+ X509_1_L=BISHKEK
+ X509_1_ST=NA
+ X509_1_C=KG
diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst
new file mode 100644
index 0000000..ac0df55
--- /dev/null
+++ b/doc/man-sections/server-options.rst
@@ -0,0 +1,812 @@
+Server Options
+--------------
+Starting with OpenVPN 2.0, a multi-client TCP/UDP server mode is
+supported, and can be enabled with the ``--mode server`` option. In
+server mode, OpenVPN will listen on a single port for incoming client
+connections. All client connections will be routed through a single tun
+or tap interface. This mode is designed for scalability and should be
+able to support hundreds or even thousands of clients on sufficiently
+fast hardware. SSL/TLS authentication must be used in this mode.
+
+--auth-gen-token args
+ Returns an authentication token to successfully authenticated clients.
+
+ Valid syntax:
+ ::
+
+ auth-gen-token [lifetime] [external-auth]
+
+ After successful user/password authentication, the OpenVPN server will
+ with this option generate a temporary authentication token and push that
+ to the client. On the following renegotiations, the OpenVPN client will pass
+ this token instead of the users password. On the server side the server
+ will do the token authentication internally and it will NOT do any
+ additional authentications against configured external user/password
+ authentication mechanisms.
+
+ The tokens implemented by this mechanism include an initial timestamp and
+ a renew timestamp and are secured by HMAC.
+
+ The ``lifetime`` argument defines how long the generated token is valid.
+ The lifetime is defined in seconds. If lifetime is not set or it is set
+ to :code:`0`, the token will never expire.
+
+ The token will expire either after the configured ``lifetime`` of the
+ token is reached or after not being renewed for more than 2 \*
+ ``reneg-sec`` seconds. Clients will be sent renewed tokens on every TLS
+ renogiation to keep the client's token updated. This is done to
+ invalidate a token if a client is disconnected for a sufficently long
+ time, while at the same time permitting much longer token lifetimes for
+ active clients.
+
+ This feature is useful for environments which are configured to use One
+ Time Passwords (OTP) as part of the user/password authentications and
+ that authentication mechanism does not implement any auth-token support.
+
+ When the :code:`external-auth` keyword is present the normal
+ authentication method will always be called even if auth-token succeeds.
+ Normally other authentications method are skipped if auth-token
+ verification suceeds or fails.
+
+ This option postpones this decision to the external authentication
+ methods and checks the validity of the account and do other checks.
+
+ In this mode the environment will have a ``session_id`` variable that
+ holds the session id from auth-gen-token. Also an environment variable
+ ``session_state`` is present. This variable indicates whether the
+ auth-token has succeeded or not. It can have the following values:
+
+ :code:`Initial`
+ No token from client.
+
+ :code:`Authenticated`
+ Token is valid and not expired.
+
+ :code:`Expired`
+ Token is valid but has expired.
+
+ :code:`Invalid`
+ Token is invalid (failed HMAC or wrong length)
+
+ :code:`AuthenticatedEmptyUser` / :code:`ExpiredEmptyUser`
+ The token is not valid with the username sent from the client but
+ would be valid (or expired) if we assume an empty username was
+ used instead. These two cases are a workaround for behaviour in
+ OpenVPN 3. If this workaround is not needed these two cases should
+ be handled in the same way as :code:`Invalid`.
+
+ **Warning:** Use this feature only if you want your authentication
+ method called on every verification. Since the external authentication
+ is called it needs to also indicate a success or failure of the
+ authentication. It is strongly recommended to return an authentication
+ failure in the case of the Invalid/Expired auth-token with the
+ external-auth option unless the client could authenticate in another
+ acceptable way (e.g. client certificate), otherwise returning success
+ will lead to authentication bypass (as does returning success on a wrong
+ password from a script).
+
+--auth-gen-token-secret file
+ Specifies a file that holds a secret for the HMAC used in
+ ``--auth-gen-token`` If ``file`` is not present OpenVPN will generate a
+ random secret on startup. This file should be used if auth-token should
+ validate after restarting a server or if client should be able to roam
+ between multiple OpenVPN servers with their auth-token.
+
+--auth-user-pass-optional
+ Allow connections by clients that do not specify a username/password.
+ Normally, when ``--auth-user-pass-verify`` or
+ ``--management-client-auth`` are specified (or an authentication plugin
+ module), the OpenVPN server daemon will require connecting clients to
+ specify a username and password. This option makes the submission of a
+ username/password by clients optional, passing the responsibility to the
+ user-defined authentication module/script to accept or deny the client
+ based on other factors (such as the setting of X509 certificate fields).
+ When this option is used, and a connecting client does not submit a
+ username/password, the user-defined authentication module/script will
+ see the username and password as being set to empty strings (""). The
+ authentication module/script MUST have logic to detect this condition
+ and respond accordingly.
+
+--ccd-exclusive
+ Require, as a condition of authentication, that a connecting client has
+ a ``--client-config-dir`` file.
+
+--client-config-dir dir
+ Specify a directory ``dir`` for custom client config files. After a
+ connecting client has been authenticated, OpenVPN will look in this
+ directory for a file having the same name as the client's X509 common
+ name. If a matching file exists, it will be opened and parsed for
+ client-specific configuration options. If no matching file is found,
+ OpenVPN will instead try to open and parse a default file called
+ "DEFAULT", which may be provided but is not required. Note that the
+ configuration files must be readable by the OpenVPN process after it has
+ dropped it's root privileges.
+
+ This file can specify a fixed IP address for a given client using
+ ``--ifconfig-push``, as well as fixed subnets owned by the client using
+ ``--iroute``.
+
+ One of the useful properties of this option is that it allows client
+ configuration files to be conveniently created, edited, or removed while
+ the server is live, without needing to restart the server.
+
+ The following options are legal in a client-specific context: ``--push``,
+ ``--push-reset``, ``--push-remove``, ``--iroute``, ``--ifconfig-push``,
+ ``--vlan-pvid`` and ``--config``.
+
+--client-to-client
+ Because the OpenVPN server mode handles multiple clients through a
+ single tun or tap interface, it is effectively a router. The
+ ``--client-to-client`` flag tells OpenVPN to internally route
+ client-to-client traffic rather than pushing all client-originating
+ traffic to the TUN/TAP interface.
+
+ When this option is used, each client will "see" the other clients which
+ are currently connected. Otherwise, each client will only see the
+ server. Don't use this option if you want to firewall tunnel traffic
+ using custom, per-client rules.
+
+--disable
+ Disable a particular client (based on the common name) from connecting.
+ Don't use this option to disable a client due to key or password
+ compromise. Use a CRL (certificate revocation list) instead (see the
+ ``--crl-verify`` option).
+
+ This option must be associated with a specific client instance, which
+ means that it must be specified either in a client instance config file
+ using ``--client-config-dir`` or dynamically generated using a
+ ``--client-connect`` script.
+
+--connect-freq args
+ Allow a maximum of ``n`` new connections per ``sec`` seconds from
+ clients.
+
+ Valid syntax:
+ ::
+
+ connect-freq n sec
+
+ This is designed to contain DoS attacks which flood the server
+ with connection requests using certificates which will ultimately fail
+ to authenticate.
+
+ This is an imperfect solution however, because in a real DoS scenario,
+ legitimate connections might also be refused.
+
+ For the best protection against DoS attacks in server mode, use
+ ``--proto udp`` and either ``--tls-auth`` or ``--tls-crypt``.
+
+--duplicate-cn
+ Allow multiple clients with the same common name to concurrently
+ connect. In the absence of this option, OpenVPN will disconnect a client
+ instance upon connection of a new client having the same common name.
+
+--ifconfig-pool args
+ Set aside a pool of subnets to be dynamically allocated to connecting
+ clients, similar to a DHCP server.
+
+ Valid syntax:
+ ::
+
+ ifconfig-pool start-IP end-IP [netmask]
+
+ For tun-style tunnels, each client
+ will be given a /30 subnet (for interoperability with Windows clients).
+ For tap-style tunnels, individual addresses will be allocated, and the
+ optional ``netmask`` parameter will also be pushed to clients.
+
+--ifconfig-ipv6-pool args
+ Specify an IPv6 address pool for dynamic assignment to clients.
+
+ Valid args:
+ ::
+
+ ifconfig-ipv6-pool ipv6addr/bits
+
+ The pool starts at ``ipv6addr`` and matches the offset determined from
+ the start of the IPv4 pool. If the host part of the given IPv6
+ address is ``0``, the pool starts at ``ipv6addr`` +1.
+
+--ifconfig-pool-persist args
+ Persist/unpersist ifconfig-pool data to ``file``, at ``seconds``
+ intervals (default :code:`600`), as well as on program startup and shutdown.
+
+ Valid syntax:
+ ::
+
+ ifconfig-pool-persist file [seconds]
+
+ The goal of this option is to provide a long-term association between
+ clients (denoted by their common name) and the virtual IP address
+ assigned to them from the ifconfig-pool. Maintaining a long-term
+ association is good for clients because it allows them to effectively
+ use the ``--persist-tun`` option.
+
+ ``file`` is a comma-delimited ASCII file, formatted as
+ :code:`<Common-Name>,<IP-address>`.
+
+ If ``seconds`` = :code:`0`, ``file`` will be treated as read-only. This
+ is useful if you would like to treat ``file`` as a configuration file.
+
+ Note that the entries in this file are treated by OpenVPN as
+ *suggestions* only, based on past associations between a common name and
+ IP address. They do not guarantee that the given common name will always
+ receive the given IP address. If you want guaranteed assignment, use
+ ``--ifconfig-push``
+
+--ifconfig-push args
+ Push virtual IP endpoints for client tunnel, overriding the
+ ``--ifconfig-pool`` dynamic allocation.
+
+ Valid syntax:
+ ::
+
+ ifconfig-push local remote-netmask [alias]
+
+ The parameters ``local`` and ``remote-netmask`` are set according to the
+ ``--ifconfig`` directive which you want to execute on the client machine
+ to configure the remote end of the tunnel. Note that the parameters
+ ``local`` and ``remote-netmask`` are from the perspective of the client,
+ not the server. They may be DNS names rather than IP addresses, in which
+ case they will be resolved on the server at the time of client
+ connection.
+
+ The optional ``alias`` parameter may be used in cases where NAT causes
+ the client view of its local endpoint to differ from the server view. In
+ this case ``local/remote-netmask`` will refer to the server view while
+ ``alias/remote-netmask`` will refer to the client view.
+
+ This option must be associated with a specific client instance, which
+ means that it must be specified either in a client instance config file
+ using ``--client-config-dir`` or dynamically generated using a
+ ``--client-connect`` script.
+
+ Remember also to include a ``--route`` directive in the main OpenVPN
+ config file which encloses ``local``, so that the kernel will know to
+ route it to the server's TUN/TAP interface.
+
+ OpenVPN's internal client IP address selection algorithm works as
+ follows:
+
+ 1. Use ``--client-connect script`` generated file for static IP
+ (first choice).
+
+ 2. Use ``--client-config-dir`` file for static IP (next choice).
+
+ 3. Use ``--ifconfig-pool`` allocation for dynamic IP (last
+ choice).
+
+--ifconfig-ipv6-push args
+ for ``--client-config-dir`` per-client static IPv6 interface
+ configuration, see ``--client-config-dir`` and ``--ifconfig-push`` for
+ more details.
+
+ Valid syntax:
+ ::
+
+ ifconfig-ipv6-push ipv6addr/bits ipv6remote
+
+--inetd args
+ Valid syntaxes:
+ ::
+
+ inetd
+ inetd wait
+ inetd nowait
+ inetd wait progname
+
+ Use this option when OpenVPN is being run from the inetd or ``xinetd``\(8)
+ server.
+
+ The :code:`wait` and :code:`nowait` option must match what is specified
+ in the inetd/xinetd config file. The :code:`nowait` mode can only be used
+ with ``--proto tcp-server`` The default is :code:`wait`. The
+ :code:`nowait` mode can be used to instantiate the OpenVPN daemon as a
+ classic TCP server, where client connection requests are serviced on a
+ single port number. For additional information on this kind of
+ configuration, see the OpenVPN FAQ:
+ https://community.openvpn.net/openvpn/wiki/325-openvpn-as-a--forking-tcp-server-which-can-service-multiple-clients-over-a-single-tcp-port
+
+ This option precludes the use of ``--daemon``, ``--local`` or
+ ``--remote``. Note that this option causes message and error output to
+ be handled in the same way as the ``--daemon`` option. The optional
+ ``progname`` parameter is also handled exactly as in ``--daemon``.
+
+ Also note that in ``wait`` mode, each OpenVPN tunnel requires a separate
+ TCP/UDP port and a separate inetd or xinetd entry. See the OpenVPN 1.x
+ HOWTO for an example on using OpenVPN with xinetd:
+ https://openvpn.net/community-resources/1xhowto/
+
+--multihome
+ Configure a multi-homed UDP server. This option needs to be used when a
+ server has more than one IP address (e.g. multiple interfaces, or
+ secondary IP addresses), and is not using ``--local`` to force binding
+ to one specific address only. This option will add some extra lookups to
+ the packet path to ensure that the UDP reply packets are always sent
+ from the address that the client is talking to. This is not supported on
+ all platforms, and it adds more processing, so it's not enabled by
+ default.
+
+ *Notes:*
+ - This option is only relevant for UDP servers.
+ - If you do an IPv6+IPv4 dual-stack bind on a Linux machine with
+ multiple IPv4 address, connections to IPv4 addresses will not
+ work right on kernels before 3.15, due to missing kernel
+ support for the IPv4-mapped case (some distributions have
+ ported this to earlier kernel versions, though).
+
+--iroute args
+ Generate an internal route to a specific client. The ``netmask``
+ parameter, if omitted, defaults to :code:`255.255.255.255`.
+
+ Valid syntax:
+ ::
+
+ iroute network [netmask]
+
+ This directive can be used to route a fixed subnet from the server to a
+ particular client, regardless of where the client is connecting from.
+ Remember that you must also add the route to the system routing table as
+ well (such as by using the ``--route`` directive). The reason why two
+ routes are needed is that the ``--route`` directive routes the packet
+ from the kernel to OpenVPN. Once in OpenVPN, the ``--iroute`` directive
+ routes to the specific client.
+
+ This option must be specified either in a client instance config file
+ using ``--client-config-dir`` or dynamically generated using a
+ ``--client-connect`` script.
+
+ The ``--iroute`` directive also has an important interaction with
+ ``--push "route ..."``. ``--iroute`` essentially defines a subnet which
+ is owned by a particular client (we will call this client *A*). If you
+ would like other clients to be able to reach *A*'s subnet, you can use
+ ``--push "route ..."`` together with ``--client-to-client`` to effect
+ this. In order for all clients to see *A*'s subnet, OpenVPN must push
+ this route to all clients EXCEPT for *A*, since the subnet is already
+ owned by *A*. OpenVPN accomplishes this by not not pushing a route to
+ a client if it matches one of the client's iroutes.
+
+--iroute-ipv6 args
+ for ``--client-config-dir`` per-client static IPv6 route configuration,
+ see ``--iroute`` for more details how to setup and use this, and how
+ ``--iroute`` and ``--route`` interact.
+
+ Valid syntax:
+ ::
+
+ iroute-ipv6 ipv6addr/bits
+
+--max-clients n
+ Limit server to a maximum of ``n`` concurrent clients.
+
+--max-routes-per-client n
+ Allow a maximum of ``n`` internal routes per client (default
+ :code:`256`). This is designed to help contain DoS attacks where an
+ authenticated client floods the server with packets appearing to come
+ from many unique MAC addresses, forcing the server to deplete virtual
+ memory as its internal routing table expands. This directive can be used
+ in a ``--client-config-dir`` file or auto-generated by a
+ ``--client-connect`` script to override the global value for a particular
+ client.
+
+ Note that this directive affects OpenVPN's internal routing table, not
+ the kernel routing table.
+
+--opt-verify
+ Clients that connect with options that are incompatible with those of the
+ server will be disconnected.
+
+ Options that will be compared for compatibility include ``dev-type``,
+ ``link-mtu``, ``tun-mtu``, ``proto``, ``ifconfig``,
+ ``comp-lzo``, ``fragment``, ``keydir``, ``cipher``,
+ ``auth``, ``keysize``, ``secret``, ``no-replay``,
+ ``tls-auth``, ``key-method``, ``tls-server``
+ and ``tls-client``.
+
+ This option requires that ``--disable-occ`` NOT be used.
+
+--port-share args
+ Share OpenVPN TCP with another service
+
+ Valid syntax:
+ ::
+
+ port-share host port [dir]
+
+ When run in TCP server mode, share the OpenVPN port with another
+ application, such as an HTTPS server. If OpenVPN senses a connection to
+ its port which is using a non-OpenVPN protocol, it will proxy the
+ connection to the server at ``host``:``port``. Currently only designed to
+ work with HTTP/HTTPS, though it would be theoretically possible to
+ extend to other protocols such as ssh.
+
+ ``dir`` specifies an optional directory where a temporary file with name
+ N containing content C will be dynamically generated for each proxy
+ connection, where N is the source IP:port of the client connection and C
+ is the source IP:port of the connection to the proxy receiver. This
+ directory can be used as a dictionary by the proxy receiver to determine
+ the origin of the connection. Each generated file will be automatically
+ deleted when the proxied connection is torn down.
+
+ Not implemented on Windows.
+
+--push option
+ Push a config file option back to the client for remote execution. Note
+ that ``option`` must be enclosed in double quotes (:code:`""`). The
+ client must specify ``--pull`` in its config file. The set of options
+ which can be pushed is limited by both feasibility and security. Some
+ options such as those which would execute scripts are banned, since they
+ would effectively allow a compromised server to execute arbitrary code
+ on the client. Other options such as TLS or MTU parameters cannot be
+ pushed because the client needs to know them before the connection to the
+ server can be initiated.
+
+ This is a partial list of options which can currently be pushed:
+ ``--route``, ``--route-gateway``, ``--route-delay``,
+ ``--redirect-gateway``, ``--ip-win32``, ``--dhcp-option``,
+ ``--inactive``, ``--ping``, ``--ping-exit``, ``--ping-restart``,
+ ``--setenv``, ``--auth-token``, ``--persist-key``, ``--persist-tun``,
+ ``--echo``, ``--comp-lzo``, ``--socket-flags``, ``--sndbuf``,
+ ``--rcvbuf``
+
+--push-peer-info
+ Push additional information about the client to server. The following
+ data is always pushed to the server:
+
+ :code:`IV_VER=<version>`
+ The client OpenVPN version
+
+ :code:`IV_PLAT=[linux|solaris|openbsd|mac|netbsd|freebsd|win]`
+ The client OS platform
+
+ :code:`IV_LZO_STUB=1`
+ If client was built with LZO stub capability
+
+ :code:`IV_LZ4=1`
+ If the client supports LZ4 compressions.
+
+ :code:`IV_PROTO`
+ Details about protocol extensions that the peer supports. The
+ variable is a bitfield and the bits are defined as follows
+ (starting a bit 0 for the first (unused) bit:
+
+ - bit 1: The peer supports peer-id floating mechanism
+ - bit 2: The client expects a push-reply and the server may
+ send this reply without waiting for a push-request first.
+
+ :code:`IV_NCP=2`
+ Negotiable ciphers, client supports ``--cipher`` pushed by
+ the server, a value of 2 or greater indicates client supports
+ *AES-GCM-128* and *AES-GCM-256*.
+
+ :code:`IV_CIPHERS=<ncp-ciphers>`
+ The client announces the list of supported ciphers configured with the
+ ``--data-ciphers`` option to the server.
+
+ :code:`IV_GUI_VER=<gui_id> <version>`
+ The UI version of a UI if one is running, for example
+ :code:`de.blinkt.openvpn 0.5.47` for the Android app.
+
+ :code:`IV_SSO=[crtext,][openurl,][proxy_url]`
+ Additional authentication methods supported by the client.
+ This may be set by the client UI/GUI using ``--setenv``
+
+ When ``--push-peer-info`` is enabled the additional information consists
+ of the following data:
+
+ :code:`IV_HWADDR=<string>`
+ This is intended to be a unique and persistent ID of the client.
+ The string value can be any readable ASCII string up to 64 bytes.
+ OpenVPN 2.x and some other implementations use the MAC address of
+ the client's interface used to reach the default gateway. If this
+ string is generated by the client, it should be consistent and
+ preserved across independent session and preferably
+ re-installations and upgrades.
+
+ :code:`IV_SSL=<version string>`
+ The ssl version used by the client, e.g.
+ :code:`OpenSSL 1.0.2f 28 Jan 2016`.
+
+ :code:`IV_PLAT_VER=x.y`
+ The version of the operating system, e.g. 6.1 for Windows 7.
+
+ :code:`UV_<name>=<value>`
+ Client environment variables whose names start with
+ :code:`UV_`
+
+--push-remove opt
+ Selectively remove all ``--push`` options matching "opt" from the option
+ list for a client. ``opt`` is matched as a substring against the whole
+ option string to-be-pushed to the client, so ``--push-remove route``
+ would remove all ``--push route ...`` and ``--push route-ipv6 ...``
+ statements, while ``--push-remove "route-ipv6 2001:"`` would only remove
+ IPv6 routes for :code:`2001:...` networks.
+
+ ``--push-remove`` can only be used in a client-specific context, like in
+ a ``--client-config-dir`` file, or ``--client-connect`` script or plugin
+ -- similar to ``--push-reset``, just more selective.
+
+ *NOTE*: to *change* an option, ``--push-remove`` can be used to first
+ remove the old value, and then add a new ``--push`` option with the new
+ value.
+
+ *NOTE 2*: due to implementation details, 'ifconfig' and 'ifconfig-ipv6'
+ can only be removed with an exact match on the option (
+ :code:`push-remove ifconfig`), no substring matching and no matching on
+ the IPv4/IPv6 address argument is possible.
+
+--push-reset
+ Don't inherit the global push list for a specific client instance.
+ Specify this option in a client-specific context such as with a
+ ``--client-config-dir`` configuration file. This option will ignore
+ ``--push`` options at the global config file level.
+
+ *NOTE*: ``--push-reset`` is very thorough: it will remove almost
+ all options from the list of to-be-pushed options. In many cases,
+ some of these options will need to be re-configured afterwards -
+ specifically, ``--topology subnet`` and ``--route-gateway`` will get
+ lost and this will break client configs in many cases. Thus, for most
+ purposes, ``--push-remove`` is better suited to selectively remove
+ push options for individual clients.
+
+--server args
+ A helper directive designed to simplify the configuration of OpenVPN's
+ server mode. This directive will set up an OpenVPN server which will
+ allocate addresses to clients out of the given network/netmask. The
+ server itself will take the :code:`.1` address of the given network for
+ use as the server-side endpoint of the local TUN/TAP interface. If the
+ optional :code:`nopool` flag is given, no dynamic IP address pool will
+ prepared for VPN clients.
+
+ Valid syntax:
+ ::
+
+ server network netmask [nopool]
+
+ For example, ``--server 10.8.0.0 255.255.255.0`` expands as follows:
+ ::
+
+ mode server
+ tls-server
+ push "topology [topology]"
+
+ if dev tun AND (topology == net30 OR topology == p2p):
+ ifconfig 10.8.0.1 10.8.0.2
+ if !nopool:
+ ifconfig-pool 10.8.0.4 10.8.0.251
+ route 10.8.0.0 255.255.255.0
+ if client-to-client:
+ push "route 10.8.0.0 255.255.255.0"
+ else if topology == net30:
+ push "route 10.8.0.1"
+
+ if dev tap OR (dev tun AND topology == subnet):
+ ifconfig 10.8.0.1 255.255.255.0
+ if !nopool:
+ ifconfig-pool 10.8.0.2 10.8.0.253 255.255.255.0
+ push "route-gateway 10.8.0.1"
+ if route-gateway unset:
+ route-gateway 10.8.0.2
+
+ Don't use ``--server`` if you are ethernet bridging. Use
+ ``--server-bridge`` instead.
+
+--server-bridge args
+ A helper directive similar to ``--server`` which is designed to simplify
+ the configuration of OpenVPN's server mode in ethernet bridging
+ configurations.
+
+ Valid syntaxes:
+ ::
+
+ server-bridge gateway netmask pool-start-IP pool-end-IP
+ server-bridge [nogw]
+
+ If ``--server-bridge`` is used without any parameters, it will enable a
+ DHCP-proxy mode, where connecting OpenVPN clients will receive an IP
+ address for their TAP adapter from the DHCP server running on the
+ OpenVPN server-side LAN. Note that only clients that support the binding
+ of a DHCP client with the TAP adapter (such as Windows) can support this
+ mode. The optional :code:`nogw` flag (advanced) indicates that gateway
+ information should not be pushed to the client.
+
+ To configure ethernet bridging, you must first use your OS's bridging
+ capability to bridge the TAP interface with the ethernet NIC interface.
+ For example, on Linux this is done with the :code:`brctl` tool, and with
+ Windows XP it is done in the Network Connections Panel by selecting the
+ ethernet and TAP adapters and right-clicking on "Bridge Connections".
+
+ Next you you must manually set the IP/netmask on the bridge interface.
+ The ``gateway`` and ``netmask`` parameters to ``--server-bridge`` can be
+ set to either the IP/netmask of the bridge interface, or the IP/netmask
+ of the default gateway/router on the bridged subnet.
+
+ Finally, set aside a IP range in the bridged subnet, denoted by
+ ``pool-start-IP`` and ``pool-end-IP``, for OpenVPN to allocate to
+ connecting clients.
+
+ For example, ``server-bridge 10.8.0.4 255.255.255.0 10.8.0.128
+ 10.8.0.254`` expands as follows:
+ ::
+
+ mode server
+ tls-server
+
+ ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
+ push "route-gateway 10.8.0.4"
+
+ In another example, ``--server-bridge`` (without parameters) expands as
+ follows:
+ ::
+
+ mode server
+ tls-server
+
+ push "route-gateway dhcp"
+
+ Or ``--server-bridge nogw`` expands as follows:
+ ::
+
+ mode server
+ tls-server
+
+--server-ipv6 args
+ Convenience-function to enable a number of IPv6 related options at once,
+ namely ``--ifconfig-ipv6``, ``--ifconfig-ipv6-pool`` and
+ ``--push tun-ipv6``.
+
+ Valid syntax:
+ ::
+
+ server-ipv6 ipv6addr/bits
+
+ Pushing of the ``--tun-ipv6`` directive is done for older clients which
+ require an explicit ``--tun-ipv6`` in their configuration.
+
+--stale-routes-check args
+ Remove routes which haven't had activity for ``n`` seconds (i.e. the ageing
+ time). This check is run every ``t`` seconds (i.e. check interval).
+
+ Valid syntax:
+ ::
+
+ stale-routes-check n [t]
+
+ If ``t`` is not present it defaults to ``n``.
+
+ This option helps to keep the dynamic routing table small. See also
+ ``--max-routes-per-client``
+
+--username-as-common-name
+ Use the authenticated username as the common-name, rather than the
+ common-name from the client certificate. Requires that some form of
+ ``--auth-user-pass`` verification is in effect. As the replacement happens
+ after ``--auth-user-pass`` verification, the verification script or
+ plugin will still receive the common-name from the certificate.
+
+ The common_name environment variable passed to scripts and plugins invoked
+ after authentication (e.g, client-connect script) and file names parsed in
+ client-config directory will match the username.
+
+--verify-client-cert mode
+ Specify whether the client is required to supply a valid certificate.
+
+ Possible ``mode`` options are:
+
+ :code:`none`
+ A client certificate is not required. the client needs to
+ authenticate using username/password only. Be aware that using this
+ directive is less secure than requiring certificates from all
+ clients.
+
+ If you use this directive, the entire responsibility of authentication
+ will rest on your ``--auth-user-pass-verify`` script, so keep in mind
+ that bugs in your script could potentially compromise the security of
+ your VPN.
+
+ ``--verify-client-cert none`` is functionally equivalent to
+ ``--client-cert-not-required``.
+
+ :code:`optional`
+ A client may present a certificate but it is not required to do so.
+ When using this directive, you should also use a
+ ``--auth-user-pass-verify`` script to ensure that clients are
+ authenticated using a certificate, a username and password, or
+ possibly even both.
+
+ Again, the entire responsibility of authentication will rest on your
+ ``--auth-user-pass-verify`` script, so keep in mind that bugs in your
+ script could potentially compromise the security of your VPN.
+
+ :code:`require`
+ This is the default option. A client is required to present a
+ certificate, otherwise VPN access is refused.
+
+ If you don't use this directive (or use ``--verify-client-cert require``)
+ but you also specify an ``--auth-user-pass-verify`` script, then OpenVPN
+ will perform double authentication. The client certificate verification
+ AND the ``--auth-user-pass-verify`` script will need to succeed in order
+ for a client to be authenticated and accepted onto the VPN.
+
+--vlan-tagging
+ Server-only option. Turns the OpenVPN server instance into a switch that
+ understands VLAN-tagging, based on IEEE 802.1Q.
+
+ The server TAP device and each of the connecting clients is seen as a
+ port of the switch. All client ports are in untagged mode and the server
+ TAP device is VLAN-tagged, untagged or accepts both, depending on the
+ ``--vlan-accept`` setting.
+
+ Ethernet frames with a prepended 802.1Q tag are called "tagged". If the
+ VLAN Identifier (VID) field in such a tag is non-zero, the frame is
+ called "VLAN-tagged". If the VID is zero, but the Priority Control Point
+ (PCP) field is non-zero, the frame is called "prio-tagged". If there is
+ no 802.1Q tag, the frame is "untagged".
+
+ Using the ``--vlan-pvid v`` option once per client (see
+ --client-config-dir), each port can be associated with a certain VID.
+ Packets can only be forwarded between ports having the same VID.
+ Therefore, clients with differing VIDs are completely separated from
+ one-another, even if ``--client-to-client`` is activated.
+
+ The packet filtering takes place in the OpenVPN server. Clients should
+ not have any VLAN tagging configuration applied.
+
+ The ``--vlan-tagging`` option is off by default. While turned off,
+ OpenVPN accepts any Ethernet frame and does not perform any special
+ processing for VLAN-tagged packets.
+
+ This option can only be activated in ``--dev tap mode``.
+
+--vlan-accept args
+ Configure the VLAN tagging policy for the server TAP device.
+
+ Valid syntax:
+ ::
+
+ vlan-accept all|tagged|untagged
+
+ The following modes are available:
+
+ :code:`tagged`
+ Admit only VLAN-tagged frames. Only VLAN-tagged packets are accepted,
+ while untagged or priority-tagged packets are dropped when entering
+ the server TAP device.
+
+ :code:`untagged`
+ Admit only untagged and prio-tagged frames. VLAN-tagged packets are
+ not accepted, while untagged or priority-tagged packets entering the
+ server TAP device are tagged with the value configured for the global
+ ``--vlan-pvid`` setting.
+
+ :code:`all` (default)
+ Admit all frames. All packets are admitted and then treated like
+ untagged or tagged mode respectively.
+
+ *Note*:
+ Some vendors refer to switch ports running in :code:`tagged` mode
+ as "trunk ports" and switch ports running in :code:`untagged` mode
+ as "access ports".
+
+ Packets forwarded from clients to the server are VLAN-tagged with the
+ originating client's PVID, unless the VID matches the global
+ ``--vlan-pvid``, in which case the tag is removed.
+
+ If no *PVID* is configured for a given client (see --vlan-pvid) packets
+ are tagged with 1 by default.
+
+--vlan-pvid v
+ Specifies which VLAN identifier a "port" is associated with. Only valid
+ when ``--vlan-tagging`` is speficied.
+
+ In the client context, the setting specifies which VLAN ID a client is
+ associated with. In the global context, the VLAN ID of the server TAP
+ device is set. The latter only makes sense for ``--vlan-accept
+ untagged`` and ``--vlan-accept all`` modes.
+
+ Valid values for ``v`` go from :code:`1` through to :code:`4094`. The
+ global value defaults to :code:`1`. If no ``--vlan-pvid`` is specified in
+ the client context, the global value is inherited.
+
+ In some switch implementations, the *PVID* is also referred to as "Native
+ VLAN".
diff --git a/doc/man-sections/signals.rst b/doc/man-sections/signals.rst
new file mode 100644
index 0000000..63611b3
--- /dev/null
+++ b/doc/man-sections/signals.rst
@@ -0,0 +1,30 @@
+SIGNALS
+=======
+
+:code:`SIGHUP`
+ Cause OpenVPN to close all TUN/TAP and network connections, restart,
+ re-read the configuration file (if any), and reopen TUN/TAP and network
+ connections.
+
+:code:`SIGUSR1`
+ Like :code:`SIGHUP``, except don't re-read configuration file, and
+ possibly don't close and reopen TUN/TAP device, re-read key files,
+ preserve local IP address/port, or preserve most recently authenticated
+ remote IP address/port based on ``--persist-tun``, ``--persist-key``,
+ ``--persist-local-ip`` and ``--persist-remote-ip`` options respectively
+ (see above).
+
+ This signal may also be internally generated by a timeout condition,
+ governed by the ``--ping-restart`` option.
+
+ This signal, when combined with ``--persist-remote-ip``, may be sent
+ when the underlying parameters of the host's network interface change
+ such as when the host is a DHCP client and is assigned a new IP address.
+ See ``--ipchange`` for more information.
+
+:code:`SIGUSR2`
+ Causes OpenVPN to display its current statistics (to the syslog file if
+ ``--daemon`` is used, or stdout otherwise).
+
+:code:`SIGINT`, :code:`SIGTERM`
+ Causes OpenVPN to exit gracefully.
diff --git a/doc/man-sections/tls-options.rst b/doc/man-sections/tls-options.rst
new file mode 100644
index 0000000..f0b6d3d
--- /dev/null
+++ b/doc/man-sections/tls-options.rst
@@ -0,0 +1,668 @@
+TLS Mode Options
+----------------
+
+TLS mode is the most powerful crypto mode of OpenVPN in both security
+and flexibility. TLS mode works by establishing control and data
+channels which are multiplexed over a single TCP/UDP port. OpenVPN
+initiates a TLS session over the control channel and uses it to exchange
+cipher and HMAC keys to protect the data channel. TLS mode uses a robust
+reliability layer over the UDP connection for all control channel
+communication, while the data channel, over which encrypted tunnel data
+passes, is forwarded without any mediation. The result is the best of
+both worlds: a fast data channel that forwards over UDP with only the
+overhead of encrypt, decrypt, and HMAC functions, and a control channel
+that provides all of the security features of TLS, including
+certificate-based authentication and Diffie Hellman forward secrecy.
+
+To use TLS mode, each peer that runs OpenVPN should have its own local
+certificate/key pair (``--cert`` and ``--key``), signed by the root
+certificate which is specified in ``--ca``.
+
+When two OpenVPN peers connect, each presents its local certificate to
+the other. Each peer will then check that its partner peer presented a
+certificate which was signed by the master root certificate as specified
+in ``--ca``.
+
+If that check on both peers succeeds, then the TLS negotiation will
+succeed, both OpenVPN peers will exchange temporary session keys, and
+the tunnel will begin passing data.
+
+The OpenVPN project provides a set of scripts for managing RSA
+certificates and keys: https://github.com/OpenVPN/easy-rsa
+
+--askpass file
+ Get certificate password from console or ``file`` before we daemonize.
+
+ Valid syntaxes:
+ ::
+
+ askpass
+ askpass file
+
+ For the extremely security conscious, it is possible to protect your
+ private key with a password. Of course this means that every time the
+ OpenVPN daemon is started you must be there to type the password. The
+ ``--askpass`` option allows you to start OpenVPN from the command line.
+ It will query you for a password before it daemonizes. To protect a
+ private key with a password you should omit the ``-nodes`` option when
+ you use the ``openssl`` command line tool to manage certificates and
+ private keys.
+
+ If ``file`` is specified, read the password from the first line of
+ ``file``. Keep in mind that storing your password in a file to a certain
+ extent invalidates the extra security provided by using an encrypted
+ key.
+
+--ca file
+ Certificate authority (CA) file in .pem format, also referred to as the
+ *root* certificate. This file can have multiple certificates in .pem
+ format, concatenated together. You can construct your own certificate
+ authority certificate and private key by using a command such as:
+ ::
+
+ openssl req -nodes -new -x509 -keyout ca.key -out ca.crt
+
+ Then edit your openssl.cnf file and edit the ``certificate`` variable to
+ point to your new root certificate ``ca.crt``.
+
+ For testing purposes only, the OpenVPN distribution includes a sample CA
+ certificate (ca.crt). Of course you should never use the test
+ certificates and test keys distributed with OpenVPN in a production
+ environment, since by virtue of the fact that they are distributed with
+ OpenVPN, they are totally insecure.
+
+--capath dir
+ Directory containing trusted certificates (CAs and CRLs). Not available
+ with mbed TLS.
+
+ CAs in the capath directory are expected to be named <hash>.<n>. CRLs
+ are expected to be named <hash>.r<n>. See the ``-CApath`` option of
+ ``openssl verify``, and the ``-hash`` option of ``openssl x509``,
+ ``openssl crl`` and ``X509_LOOKUP_hash_dir()``\(3)
+ for more information.
+
+ Similar to the ``--crl-verify`` option, CRLs are not mandatory -
+ OpenVPN will log the usual warning in the logs if the relevant CRL is
+ missing, but the connection will be allowed.
+
+--cert file
+ Local peer's signed certificate in .pem format -- must be signed by a
+ certificate authority whose certificate is in ``--ca file``. Each peer
+ in an OpenVPN link running in TLS mode should have its own certificate
+ and private key file. In addition, each certificate should have been
+ signed by the key of a certificate authority whose public key resides in
+ the ``--ca`` certificate authority file. You can easily make your own
+ certificate authority (see above) or pay money to use a commercial
+ service such as thawte.com (in which case you will be helping to finance
+ the world's second space tourist :). To generate a certificate, you can
+ use a command such as:
+ ::
+
+ openssl req -nodes -new -keyout mycert.key -out mycert.csr
+
+ If your certificate authority private key lives on another machine, copy
+ the certificate signing request (mycert.csr) to this other machine (this
+ can be done over an insecure channel such as email). Now sign the
+ certificate with a command such as:
+ ::
+
+ openssl ca -out mycert.crt -in mycert.csr
+
+ Now copy the certificate (mycert.crt) back to the peer which initially
+ generated the .csr file (this can be over a public medium). Note that
+ the ``openssl ca`` command reads the location of the certificate
+ authority key from its configuration file such as
+ :code:`/usr/share/ssl/openssl.cnf` -- note also that for certificate
+ authority functions, you must set up the files :code:`index.txt` (may be
+ empty) and :code:`serial` (initialize to :code:`01`).
+
+--crl-verify args
+ Check peer certificate against a Certificate Revocation List.
+
+ Valid syntax:
+ ::
+
+ crl-verify file/directory flag
+
+ Examples:
+ ::
+
+ crl-verify crl-file.pem
+ crl-verify /etc/openvpn/crls dir
+
+ A CRL (certificate revocation list) is used when a particular key is
+ compromised but when the overall PKI is still intact.
+
+ Suppose you had a PKI consisting of a CA, root certificate, and a number
+ of client certificates. Suppose a laptop computer containing a client
+ key and certificate was stolen. By adding the stolen certificate to the
+ CRL file, you could reject any connection which attempts to use it,
+ while preserving the overall integrity of the PKI.
+
+ The only time when it would be necessary to rebuild the entire PKI from
+ scratch would be if the root certificate key itself was compromised.
+
+ The option is not mandatory - if the relevant CRL is missing, OpenVPN
+ will log a warning in the logs - e.g.
+ ::
+
+ VERIFY WARNING: depth=0, unable to get certificate CRL
+
+ but the connection will be allowed. If the optional :code:`dir` flag
+ is specified, enable a different mode where the ``crl-verify`` is
+ pointed at a directory containing files named as revoked serial numbers
+ (the files may be empty, the contents are never read). If a client
+ requests a connection, where the client certificate serial number
+ (decimal string) is the name of a file present in the directory, it will
+ be rejected.
+
+ *Note:*
+ As the crl file (or directory) is read every time a peer
+ connects, if you are dropping root privileges with
+ ``--user``, make sure that this user has sufficient
+ privileges to read the file.
+
+
+--dh file
+ File containing Diffie Hellman parameters in .pem format (required for
+ ``--tls-server`` only).
+
+ Set ``file`` to :code:`none` to disable Diffie Hellman key exchange (and
+ use ECDH only). Note that this requires peers to be using an SSL library
+ that supports ECDH TLS cipher suites (e.g. OpenSSL 1.0.1+, or
+ mbed TLS 2.0+).
+
+ Use ``openssl dhparam -out dh2048.pem 2048`` to generate 2048-bit DH
+ parameters. Diffie Hellman parameters may be considered public.
+
+--ecdh-curve name
+ Specify the curve to use for elliptic curve Diffie Hellman. Available
+ curves can be listed with ``--show-curves``. The specified curve will
+ only be used for ECDH TLS-ciphers.
+
+ This option is not supported in mbed TLS builds of OpenVPN.
+
+--extra-certs file
+ Specify a ``file`` containing one or more PEM certs (concatenated
+ together) that complete the local certificate chain.
+
+ This option is useful for "split" CAs, where the CA for server certs is
+ different than the CA for client certs. Putting certs in this file
+ allows them to be used to complete the local certificate chain without
+ trusting them to verify the peer-submitted certificate, as would be the
+ case if the certs were placed in the ``ca`` file.
+
+--hand-window n
+ Handshake Window -- the TLS-based key exchange must finalize within
+ ``n`` seconds of handshake initiation by any peer (default :code:`60`
+ seconds). If the handshake fails we will attempt to reset our connection
+ with our peer and try again. Even in the event of handshake failure we
+ will still use our expiring key for up to ``--tran-window`` seconds to
+ maintain continuity of transmission of tunnel data.
+
+--key file
+ Local peer's private key in .pem format. Use the private key which was
+ generated when you built your peer's certificate (see ``--cert file``
+ above).
+
+--pkcs12 file
+ Specify a PKCS #12 file containing local private key, local certificate,
+ and root CA certificate. This option can be used instead of ``--ca``,
+ ``--cert``, and ``--key``. Not available with mbed TLS.
+
+--remote-cert-eku oid
+ Require that peer certificate was signed with an explicit *extended key
+ usage*.
+
+ This is a useful security option for clients, to ensure that the host
+ they connect to is a designated server.
+
+ The extended key usage should be encoded in *oid notation*, or *OpenSSL
+ symbolic representation*.
+
+--remote-cert-ku key-usage
+ Require that peer certificate was signed with an explicit
+ ``key-usage``.
+
+ If present in the certificate, the :code:`keyUsage` value is validated by
+ the TLS library during the TLS handshake. Specifying this option without
+ arguments requires this extension to be present (so the TLS library will
+ verify it).
+
+ If ``key-usage`` is a list of usage bits, the :code:`keyUsage` field
+ must have *at least* the same bits set as the bits in *one of* the values
+ supplied in the ``key-usage`` list.
+
+ The ``key-usage`` values in the list must be encoded in hex, e.g.
+ ::
+
+ remote-cert-ku a0
+
+--remote-cert-tls type
+ Require that peer certificate was signed with an explicit *key usage*
+ and *extended key usage* based on RFC3280 TLS rules.
+
+ Valid syntaxes:
+ ::
+
+ remote-cert-tls server
+ remote-cert-tls client
+
+ This is a useful security option for clients, to ensure that the host
+ they connect to is a designated server. Or the other way around; for a
+ server to verify that only hosts with a client certificate can connect.
+
+ The ``--remote-cert-tls client`` option is equivalent to
+ ::
+
+ remote-cert-ku
+ remote-cert-eku "TLS Web Client Authentication"
+
+ The ``--remote-cert-tls server`` option is equivalent to
+ ::
+
+ remote-cert-ku
+ remote-cert-eku "TLS Web Server Authentication"
+
+ This is an important security precaution to protect against a
+ man-in-the-middle attack where an authorized client attempts to connect
+ to another client by impersonating the server. The attack is easily
+ prevented by having clients verify the server certificate using any one
+ of ``--remote-cert-tls``, ``--verify-x509-name``, or ``--tls-verify``.
+
+--tls-auth args
+ Add an additional layer of HMAC authentication on top of the TLS control
+ channel to mitigate DoS attacks and attacks on the TLS stack.
+
+ Valid syntaxes:
+ ::
+
+ tls-auth file
+ tls-auth file 0
+ tls-auth file 1
+
+ In a nutshell, ``--tls-auth`` enables a kind of "HMAC firewall" on
+ OpenVPN's TCP/UDP port, where TLS control channel packets bearing an
+ incorrect HMAC signature can be dropped immediately without response.
+
+ ``file`` (required) is a file in OpenVPN static key format which can be
+ generated by ``--genkey``.
+
+ Older versions (up to OpenVPN 2.3) supported a freeform passphrase file.
+ This is no longer supported in newer versions (v2.4+).
+
+ See the ``--secret`` option for more information on the optional
+ ``direction`` parameter.
+
+ ``--tls-auth`` is recommended when you are running OpenVPN in a mode
+ where it is listening for packets from any IP address, such as when
+ ``--remote`` is not specified, or ``--remote`` is specified with
+ ``--float``.
+
+ The rationale for this feature is as follows. TLS requires a
+ multi-packet exchange before it is able to authenticate a peer. During
+ this time before authentication, OpenVPN is allocating resources (memory
+ and CPU) to this potential peer. The potential peer is also exposing
+ many parts of OpenVPN and the OpenSSL library to the packets it is
+ sending. Most successful network attacks today seek to either exploit
+ bugs in programs (such as buffer overflow attacks) or force a program to
+ consume so many resources that it becomes unusable. Of course the first
+ line of defense is always to produce clean, well-audited code. OpenVPN
+ has been written with buffer overflow attack prevention as a top
+ priority. But as history has shown, many of the most widely used network
+ applications have, from time to time, fallen to buffer overflow attacks.
+
+ So as a second line of defense, OpenVPN offers this special layer of
+ authentication on top of the TLS control channel so that every packet on
+ the control channel is authenticated by an HMAC signature and a unique
+ ID for replay protection. This signature will also help protect against
+ DoS (Denial of Service) attacks. An important rule of thumb in reducing
+ vulnerability to DoS attacks is to minimize the amount of resources a
+ potential, but as yet unauthenticated, client is able to consume.
+
+ ``--tls-auth`` does this by signing every TLS control channel packet
+ with an HMAC signature, including packets which are sent before the TLS
+ level has had a chance to authenticate the peer. The result is that
+ packets without the correct signature can be dropped immediately upon
+ reception, before they have a chance to consume additional system
+ resources such as by initiating a TLS handshake. ``--tls-auth`` can be
+ strengthened by adding the ``--replay-persist`` option which will keep
+ OpenVPN's replay protection state in a file so that it is not lost
+ across restarts.
+
+ It should be emphasized that this feature is optional and that the key
+ file used with ``--tls-auth`` gives a peer nothing more than the power
+ to initiate a TLS handshake. It is not used to encrypt or authenticate
+ any tunnel data.
+
+ Use ``--tls-crypt`` instead if you want to use the key file to not only
+ authenticate, but also encrypt the TLS control channel.
+
+--tls-groups list
+ A list of allowable groups/curves in order of preference.
+
+ Set the allowed elliptic curves/groups for the TLS session.
+ These groups are allowed to be used in signatures and key exchange.
+
+ mbedTLS currently allows all known curves per default.
+
+ OpenSSL 1.1+ restricts the list per default to
+ ::
+
+ "X25519:secp256r1:X448:secp521r1:secp384r1".
+
+ If you use certificates that use non-standard curves, you
+ might need to add them here. If you do not force the ecdh curve
+ by using ``--ecdh-curve``, the groups for ecdh will also be picked
+ from this list.
+
+ OpenVPN maps the curve name `secp256r1` to `prime256v1` to allow
+ specifying the same tls-groups option for mbedTLS and OpenSSL.
+
+ Warning: this option not only affects elliptic curve certificates
+ but also the key exchange in TLS 1.3 and using this option improperly
+ will disable TLS 1.3.
+
+--tls-cert-profile profile
+ Set the allowed cryptographic algorithms for certificates according to
+ ``profile``.
+
+ The following profiles are supported:
+
+ :code:`legacy` (default)
+ SHA1 and newer, RSA 2048-bit+, any elliptic curve.
+
+ :code:`preferred`
+ SHA2 and newer, RSA 2048-bit+, any elliptic curve.
+
+ :code:`suiteb`
+ SHA256/SHA384, ECDSA with P-256 or P-384.
+
+ This option is only fully supported for mbed TLS builds. OpenSSL builds
+ use the following approximation:
+
+ :code:`legacy` (default)
+ sets "security level 1"
+
+ :code:`preferred`
+ sets "security level 2"
+
+ :code:`suiteb`
+ sets "security level 3" and ``--tls-cipher "SUITEB128"``.
+
+ OpenVPN will migrate to 'preferred' as default in the future. Please
+ ensure that your keys already comply.
+
+*WARNING:* ``--tls-ciphers``, ``--tls-ciphersuites`` and ``tls-groups``
+ These options are expert features, which - if used correctly - can
+ improve the security of your VPN connection. But it is also easy to
+ unwittingly use them to carefully align a gun with your foot, or just
+ break your connection. Use with care!
+
+--tls-cipher l
+ A list ``l`` of allowable TLS ciphers delimited by a colon (":code:`:`").
+
+ These setting can be used to ensure that certain cipher suites are used
+ (or not used) for the TLS connection. OpenVPN uses TLS to secure the
+ control channel, over which the keys that are used to protect the actual
+ VPN traffic are exchanged.
+
+ The supplied list of ciphers is (after potential OpenSSL/IANA name
+ translation) simply supplied to the crypto library. Please see the
+ OpenSSL and/or mbed TLS documentation for details on the cipher list
+ interpretation.
+
+ For OpenSSL, the ``--tls-cipher`` is used for TLS 1.2 and below.
+
+ Use ``--show-tls`` to see a list of TLS ciphers supported by your crypto
+ library.
+
+ The default for ``--tls-cipher`` is to use mbed TLS's default cipher list
+ when using mbed TLS or
+ :code:`DEFAULT:!EXP:!LOW:!MEDIUM:!kDH:!kECDH:!DSS:!PSK:!SRP:!kRSA` when
+ using OpenSSL.
+
+--tls-ciphersuites l
+ Same as ``--tls-cipher`` but for TLS 1.3 and up. mbed TLS has no
+ TLS 1.3 support yet and only the ``--tls-cipher`` setting is used.
+
+ The default for `--tls-ciphersuites` is to use the crypto library's
+ default.
+
+--tls-client
+ Enable TLS and assume client role during TLS handshake.
+
+--tls-crypt keyfile
+ Encrypt and authenticate all control channel packets with the key from
+ ``keyfile``. (See ``--tls-auth`` for more background.)
+
+ Encrypting (and authenticating) control channel packets:
+
+ * provides more privacy by hiding the certificate used for the TLS
+ connection,
+
+ * makes it harder to identify OpenVPN traffic as such,
+
+ * provides "poor-man's" post-quantum security, against attackers who will
+ never know the pre-shared key (i.e. no forward secrecy).
+
+ In contrast to ``--tls-auth``, ``--tls-crypt`` does *not* require the
+ user to set ``--key-direction``.
+
+ **Security Considerations**
+
+ All peers use the same ``--tls-crypt`` pre-shared group key to
+ authenticate and encrypt control channel messages. To ensure that IV
+ collisions remain unlikely, this key should not be used to encrypt more
+ than 2^48 client-to-server or 2^48 server-to-client control channel
+ messages. A typical initial negotiation is about 10 packets in each
+ direction. Assuming both initial negotiation and renegotiations are at
+ most 2^16 (65536) packets (to be conservative), and (re)negotiations
+ happen each minute for each user (24/7), this limits the tls-crypt key
+ lifetime to 8171 years divided by the number of users. So a setup with
+ 1000 users should rotate the key at least once each eight years. (And a
+ setup with 8000 users each year.)
+
+ If IV collisions were to occur, this could result in the security of
+ ``--tls-crypt`` degrading to the same security as using ``--tls-auth``.
+ That is, the control channel still benefits from the extra protection
+ against active man-in-the-middle-attacks and DoS attacks, but may no
+ longer offer extra privacy and post-quantum security on top of what TLS
+ itself offers.
+
+ For large setups or setups where clients are not trusted, consider using
+ ``--tls-crypt-v2`` instead. That uses per-client unique keys, and
+ thereby improves the bounds to 'rotate a client key at least once per
+ 8000 years'.
+
+--tls-crypt-v2 keyfile
+ Use client-specific tls-crypt keys.
+
+ For clients, ``keyfile`` is a client-specific tls-crypt key. Such a key
+ can be generated using the :code:`--genkey tls-crypt-v2-client` option.
+
+ For servers, ``keyfile`` is used to unwrap client-specific keys supplied
+ by the client during connection setup. This key must be the same as the
+ key used to generate the client-specific key (see :code:`--genkey
+ tls-crypt-v2-client`).
+
+ On servers, this option can be used together with the ``--tls-auth`` or
+ ``--tls-crypt`` option. In that case, the server will detect whether the
+ client is using client-specific keys, and automatically select the right
+ mode.
+
+--tls-crypt-v2-verify cmd
+ Run command ``cmd`` to verify the metadata of the client-specific
+ tls-crypt-v2 key of a connecting client. This allows server
+ administrators to reject client connections, before exposing the TLS
+ stack (including the notoriously dangerous X.509 and ASN.1 stacks) to
+ the connecting client.
+
+ OpenVPN supplies the following environment variables to the command:
+
+ * :code:`script_type` is set to :code:`tls-crypt-v2-verify`
+
+ * :code:`metadata_type` is set to :code:`0` if the metadata was user
+ supplied, or :code:`1` if it's a 64-bit unix timestamp representing
+ the key creation time.
+
+ * :code:`metadata_file` contains the filename of a temporary file that
+ contains the client metadata.
+
+ The command can reject the connection by exiting with a non-zero exit
+ code.
+
+--tls-exit
+ Exit on TLS negotiation failure.
+
+--tls-export-cert directory
+ Store the certificates the clients use upon connection to this
+ directory. This will be done before ``--tls-verify`` is called. The
+ certificates will use a temporary name and will be deleted when the
+ tls-verify script returns. The file name used for the certificate is
+ available via the ``peer_cert`` environment variable.
+
+--tls-server
+ Enable TLS and assume server role during TLS handshake. Note that
+ OpenVPN is designed as a peer-to-peer application. The designation of
+ client or server is only for the purpose of negotiating the TLS control
+ channel.
+
+--tls-timeout n
+ Packet retransmit timeout on TLS control channel if no acknowledgment
+ from remote within ``n`` seconds (default :code:`2`). When OpenVPN sends
+ a control packet to its peer, it will expect to receive an
+ acknowledgement within ``n`` seconds or it will retransmit the packet,
+ subject to a TCP-like exponential backoff algorithm. This parameter only
+ applies to control channel packets. Data channel packets (which carry
+ encrypted tunnel data) are never acknowledged, sequenced, or
+ retransmitted by OpenVPN because the higher level network protocols
+ running on top of the tunnel such as TCP expect this role to be left to
+ them.
+
+--tls-version-min args
+ Sets the minimum TLS version we will accept from the peer (default is
+ "1.0").
+
+ Valid syntax:
+ ::
+
+ tls-version-min version ['or-highest']
+
+ Examples for version include :code:`1.0`, :code:`1.1`, or :code:`1.2`. If
+ :code:`or-highest` is specified and version is not recognized, we will
+ only accept the highest TLS version supported by the local SSL
+ implementation.
+
+--tls-version-max version
+ Set the maximum TLS version we will use (default is the highest version
+ supported). Examples for version include :code:`1.0`, :code:`1.1`, or
+ :code:`1.2`.
+
+--verify-hash args
+ Specify SHA1 or SHA256 fingerprint for level-1 cert.
+
+ Valid syntax:
+ ::
+
+ verify-hash hash [algo]
+
+ The level-1 cert is the CA (or intermediate cert) that signs the leaf
+ certificate, and is one removed from the leaf certificate in the
+ direction of the root. When accepting a connection from a peer, the
+ level-1 cert fingerprint must match ``hash`` or certificate verification
+ will fail. Hash is specified as XX:XX:... For example:
+ ::
+
+ AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+
+ The ``algo`` flag can be either :code:`SHA1` or :code:`SHA256`. If not
+ provided, it defaults to :code:`SHA1`.
+
+--verify-x509-name args
+ Accept connections only if a host's X.509 name is equal to **name.** The
+ remote host must also pass all other tests of verification.
+
+ Valid syntax:
+ ::
+
+ verify-x509 name type
+
+ Which X.509 name is compared to ``name`` depends on the setting of type.
+ ``type`` can be :code:`subject` to match the complete subject DN
+ (default), :code:`name` to match a subject RDN or :code:`name-prefix` to
+ match a subject RDN prefix. Which RDN is verified as name depends on the
+ ``--x509-username-field`` option. But it defaults to the common name
+ (CN), e.g. a certificate with a subject DN
+ ::
+
+ C=KG, ST=NA, L=Bishkek, CN=Server-1
+
+ would be matched by:
+ ::
+
+ verify-x509-name 'C=KG, ST=NA, L=Bishkek, CN=Server-1'
+ verify-x509-name Server-1 name
+ verify-x509-name Server- name-prefix
+
+ The last example is useful if you want a client to only accept
+ connections to :code:`Server-1`, :code:`Server-2`, etc.
+
+ ``--verify-x509-name`` is a useful replacement for the ``--tls-verify``
+ option to verify the remote host, because ``--verify-x509-name`` works
+ in a ``--chroot`` environment without any dependencies.
+
+ Using a name prefix is a useful alternative to managing a CRL
+ (Certificate Revocation List) on the client, since it allows the client
+ to refuse all certificates except for those associated with designated
+ servers.
+
+ *NOTE:*
+ Test against a name prefix only when you are using OpenVPN
+ with a custom CA certificate that is under your control. Never use
+ this option with type :code:`name-prefix` when your client
+ certificates are signed by a third party, such as a commercial
+ web CA.
+
+--x509-track attribute
+ Save peer X509 **attribute** value in environment for use by plugins and
+ management interface. Prepend a :code:`+` to ``attribute`` to save values
+ from full cert chain. Values will be encoded as
+ :code:`X509_<depth>_<attribute>=<value>`. Multiple ``--x509-track``
+ options can be defined to track multiple attributes.
+
+--x509-username-field args
+ Field in the X.509 certificate subject to be used as the username
+ (default :code:`CN`).
+
+ Valid syntax:
+ ::
+
+ x509-username-field [ext:]fieldname
+
+ Typically, this option is specified with **fieldname** as
+ either of the following:
+ ::
+
+ x509-username-field emailAddress
+ x509-username-field ext:subjectAltName
+
+ The first example uses the value of the :code:`emailAddress` attribute
+ in the certificate's Subject field as the username. The second example
+ uses the :code:`ext:` prefix to signify that the X.509 extension
+ ``fieldname`` :code:`subjectAltName` be searched for an rfc822Name
+ (email) field to be used as the username. In cases where there are
+ multiple email addresses in :code:`ext:fieldname`, the last occurrence
+ is chosen.
+
+ When this option is used, the ``--verify-x509-name`` option will match
+ against the chosen ``fieldname`` instead of the Common Name.
+
+ Only the :code:`subjectAltName` and :code:`issuerAltName` X.509
+ extensions are supported.
+
+ **Please note:** This option has a feature which will convert an
+ all-lowercase ``fieldname`` to uppercase characters, e.g.,
+ :code:`ou` -> :code:`OU`. A mixed-case ``fieldname`` or one having the
+ :code:`ext:` prefix will be left as-is. This automatic upcasing feature is
+ deprecated and will be removed in a future release.
diff --git a/doc/man-sections/unsupported-options.rst b/doc/man-sections/unsupported-options.rst
new file mode 100644
index 0000000..05ba3ca
--- /dev/null
+++ b/doc/man-sections/unsupported-options.rst
@@ -0,0 +1,32 @@
+
+UNSUPPORTED OPTIONS
+===================
+
+Options listed in this section have been removed from OpenVPN and are no
+longer supported
+
+--client-cert-not-required
+ Removed in OpenVPN 2.5. This should be replaxed with
+ ``--verify-client-cert none``.
+
+--ifconfig-pool-linear
+ Removed in OpenVPN 2.5. This should be replaced with ``--topology p2p``.
+
+--key-method
+ Removed in OpenVPN 2.5. This option should not be used, as using the old
+ ``key-method`` weakens the VPN tunnel security. The old ``key-method``
+ was also only needed when the remote side was older than OpenVPN 2.0.
+
+--no-iv
+ Removed in OpenVPN 2.5. This option should not be used as it weakens the
+ VPN tunnel security. This has been a NOOP option since OpenVPN 2.4.
+
+--no-replay
+ Removed in OpenVPN 2.5. This option should not be used as it weakens the
+ VPN tunnel security.
+
+--ns-cert-type
+ Removed in OpenVPN 2.5. The ``nsCertType`` field is no longer supported
+ in recent SSL/TLS libraries. If your certificates does not include *key
+ usage* and *extended key usage* fields, they must be upgraded and the
+ ``--remote-cert-tls`` option should be used instead.
diff --git a/doc/man-sections/virtual-routing-and-forwarding.rst b/doc/man-sections/virtual-routing-and-forwarding.rst
new file mode 100644
index 0000000..28c13ee
--- /dev/null
+++ b/doc/man-sections/virtual-routing-and-forwarding.rst
@@ -0,0 +1,78 @@
+Virtual Routing and Forwarding
+------------------------------
+
+Options in this section relates to configuration of virtual routing and
+forwarding in combination with the underlying operating system.
+
+As of today this is only supported on Linux, a kernel >= 4.9 is
+recommended.
+
+This could come in handy when for example the external network should be
+only used as a means to connect to some VPN endpoints and all regular
+traffic should only be routed through any tunnel(s). This could be
+achieved by setting up a VRF and configuring the interface connected to
+the external network to be part of the VRF. The examples below will cover
+this setup.
+
+Another option would be to put the tun/tap interface into a VRF. This could
+be done by an up-script which uses the :code:`ip link set` command shown
+below.
+
+
+VRF setup with iproute2
+```````````````````````
+
+Create VRF :code:`vrf_external` and map it to routing table :code:`1023`
+::
+
+ ip link add vrf_external type vrf table 1023
+
+Move :code:`eth0` into :code:`vrf_external`
+::
+
+ ip link set master vrf_external dev eth0
+
+Any prefixes configured on :code:`eth0` will be moved from the :code`main`
+routing table into routing table `1023`
+
+
+VRF setup with ifupdown
+```````````````````````
+
+For Debian based Distributions :code:`ifupdown2` provides an almost drop-in
+replacement for :code:`ifupdown` including VRFs and other features.
+A configuration for an interface :code:`eth0` being part of VRF
+code:`vrf_external` could look like this:
+::
+
+ auto eth0
+ iface eth0
+ address 192.0.2.42/24
+ address 2001:db8:08:15::42/64
+ gateway 192.0.2.1
+ gateway 2001:db8:08:15::1
+ vrf vrf_external
+
+ auto vrf_external
+ iface vrf_external
+ vrf-table 1023
+
+
+OpenVPN configuration
+`````````````````````
+The OpenVPN configuration needs to contain this line:
+::
+
+ bind-dev vrf_external
+
+
+Further reading
+```````````````
+
+Wikipedia has nice page one VRFs: https://en.wikipedia.org/wiki/Virtual_routing_and_forwarding
+
+This talk from the Network Track of FrOSCon 2018 provides an overview about
+advanced layer 2 and layer 3 features of Linux
+
+ - Slides: https://www.slideshare.net/BarbarossaTM/l2l3-fr-fortgeschrittene-helle-und-dunkle-magie-im-linuxnetzwerkstack
+ - Video (german): https://media.ccc.de/v/froscon2018-2247-l2\_l3\_fur\_fortgeschrittene\_-\_helle\_und\_dunkle\_magie\_im\_linux-netzwerkstack
diff --git a/doc/man-sections/vpn-network-options.rst b/doc/man-sections/vpn-network-options.rst
new file mode 100644
index 0000000..029834a
--- /dev/null
+++ b/doc/man-sections/vpn-network-options.rst
@@ -0,0 +1,556 @@
+Virtual Network Adapter (VPN interface)
+---------------------------------------
+
+Options in this section relates to configuration of the virtual tun/tap
+network interface, including setting the VPN IP address and network
+routing.
+
+--bind-dev device
+ (Linux only) Set ``device`` to bind the server socket to a
+ `Virtual Routing and Forwarding`_ device
+
+--block-ipv6
+ On the client, instead of sending IPv6 packets over the VPN tunnel, all
+ IPv6 packets are answered with an ICMPv6 no route host message. On the
+ server, all IPv6 packets from clients are answered with an ICMPv6 no
+ route to host message. This options is intended for cases when IPv6
+ should be blocked and other options are not available. ``--block-ipv6``
+ will use the remote IPv6 as source address of the ICMPv6 packets if set,
+ otherwise will use :code:`fe80::7` as source address.
+
+ For this option to make sense you actually have to route traffic to the
+ tun interface. The following example config block would send all IPv6
+ traffic to OpenVPN and answer all requests with no route to host,
+ effectively blocking IPv6 (to avoid IPv6 connections from dual-stacked
+ clients leaking around IPv4-only VPN services).
+
+ **Client config**
+ ::
+
+ --ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1
+ --redirect-gateway ipv6
+ --block-ipv6
+
+ **Server config**
+ Push a "valid" ipv6 config to the client and block on the server
+ ::
+
+ --push "ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1"
+ --push "redirect-gateway ipv6"
+ --block-ipv6
+
+ Note: this option does not influence traffic sent from the server
+ towards the client (neither on the server nor on the client side).
+ This is not seen as necessary, as such traffic can be most easily
+ avoided by not configuring IPv6 on the server tun, or setting up a
+ server-side firewall rule.
+
+--dev device
+ TUN/TAP virtual network device which can be :code:`tunX`, :code:`tapX`,
+ :code:`null` or an arbitrary name string (:code:`X` can be omitted for
+ a dynamic device.)
+
+ See examples section below for an example on setting up a TUN device.
+
+ You must use either tun devices on both ends of the connection or tap
+ devices on both ends. You cannot mix them, as they represent different
+ underlying network layers:
+
+ :code:`tun`
+ devices encapsulate IPv4 or IPv6 (OSI Layer 3)
+
+ :code:`tap`
+ devices encapsulate Ethernet 802.3 (OSI Layer 2).
+
+ Valid syntaxes:
+ ::
+
+ dev tun2
+ dev tap4
+ dev ovpn
+
+ When the device name starts with :code:`tun` or :code:`tap`, the device
+ type is extracted automatically. Otherwise the ``--dev-type`` option
+ needs to be added as well.
+
+--dev-node node
+ Explicitly set the device node rather than using :code:`/dev/net/tun`,
+ :code:`/dev/tun`, :code:`/dev/tap`, etc. If OpenVPN cannot figure out
+ whether ``node`` is a TUN or TAP device based on the name, you should
+ also specify ``--dev-type tun`` or ``--dev-type tap``.
+
+ Under Mac OS X this option can be used to specify the default tun
+ implementation. Using ``--dev-node utun`` forces usage of the native
+ Darwin tun kernel support. Use ``--dev-node utunN`` to select a specific
+ utun instance. To force using the :code:`tun.kext` (:code:`/dev/tunX`)
+ use ``--dev-node tun``. When not specifying a ``--dev-node`` option
+ openvpn will first try to open utun, and fall back to tun.kext.
+
+ On Windows systems, select the TAP-Win32 adapter which is named ``node``
+ in the Network Connections Control Panel or the raw GUID of the adapter
+ enclosed by braces. The ``--show-adapters`` option under Windows can
+ also be used to enumerate all available TAP-Win32 adapters and will show
+ both the network connections control panel name and the GUID for each
+ TAP-Win32 adapter.
+
+--dev-type device-type
+ Which device type are we using? ``device-type`` should be :code:`tun`
+ (OSI Layer 3) or :code:`tap` (OSI Layer 2). Use this option only if
+ the TUN/TAP device used with ``--dev`` does not begin with :code:`tun`
+ or :code:`tap`.
+
+--dhcp-option args
+ Set additional network parameters on supported platforms. May be specified
+ on the client or pushed from the server. On Windows these options are
+ handled by the ``tap-windows6`` driver by default or directly by OpenVPN
+ if dhcp is disabled or the ``wintun`` driver is in use. The
+ ``OpenVPN for Android`` client also handles them internally.
+
+ On all other platforms these options are only saved in the client's
+ environment under the name :code:`foreign_options_{n}` before the
+ ``--up`` script is called. A plugin or an ``--up`` script must be used to
+ pick up and interpret these as required. Many Linux distributions include
+ such scripts and some third-party user interfaces such as tunnelblick also
+ come with scripts that process these options.
+
+ Valid syntax:
+ ::
+
+ dhcp-options type [parm]
+
+ :code:`DOMAIN` ``name``
+ Set Connection-specific DNS Suffix to :code:`name`.
+
+ :code:`ADAPTER_DOMAIN_SUFFIX` ``name``
+ Alias to :code:`DOMAIN`. This is a compatibility option, it
+ should not be used in new deployments.
+
+ :code:`DOMAIN-SEARCH` ``name``
+ Add :code:`name` to the domain search list.
+ Repeat this option to add more entries. Up to
+ 10 domains are supported.
+
+ :code:`DNS` ``address``
+ Set primary domain name server IPv4 or IPv6 address.
+ Repeat this option to set secondary DNS server addresses.
+
+ Note: DNS IPv6 servers are currently set using netsh (the existing
+ DHCP code can only do IPv4 DHCP, and that protocol only permits
+ IPv4 addresses anywhere). The option will be put into the
+ environment, so an ``--up`` script could act upon it if needed.
+
+ :code:`WINS` ``address``
+ Set primary WINS server address (NetBIOS over TCP/IP Name Server).
+ Repeat this option to set secondary WINS server addresses.
+
+ :code:`NBDD` ``address``
+ Set primary NBDD server address (NetBIOS over TCP/IP Datagram
+ Distribution Server). Repeat this option to set secondary NBDD
+ server addresses.
+
+ :code:`NTP` ``address``
+ Set primary NTP server address (Network Time Protocol).
+ Repeat this option to set secondary NTP server addresses.
+
+ :code:`NBT` ``type``
+ Set NetBIOS over TCP/IP Node type. Possible options:
+
+ :code:`1`
+ b-node (broadcasts)
+
+ :code:`2`
+ p-node (point-to-point name queries to a WINS server)
+
+ :code:`4`
+ m-node (broadcast then query name server)
+
+ :code:`8`
+ h-node (query name server, then broadcast).
+
+ :code:`NBS` ``scope-id``
+ Set NetBIOS over TCP/IP Scope. A NetBIOS Scope ID provides an
+ extended naming service for the NetBIOS over TCP/IP (Known as NBT)
+ module. The primary purpose of a NetBIOS scope ID is to isolate
+ NetBIOS traffic on a single network to only those nodes with the
+ same NetBIOS scope ID. The NetBIOS scope ID is a character string
+ that is appended to the NetBIOS name. The NetBIOS scope ID on two
+ hosts must match, or the two hosts will not be able to communicate.
+ The NetBIOS Scope ID also allows computers to use the same computer
+ name, as they have different scope IDs. The Scope ID becomes a part
+ of the NetBIOS name, making the name unique. (This description of
+ NetBIOS scopes courtesy of NeonSurge@abyss.com)
+
+ :code:`DISABLE-NBT`
+ Disable Netbios-over-TCP/IP.
+
+--ifconfig args
+ Set TUN/TAP adapter parameters. It requires the *IP address* of the local
+ VPN endpoint. For TUN devices in point-to-point mode, the next argument
+ must be the VPN IP address of the remote VPN endpoint. For TAP devices,
+ or TUN devices used with ``--topology subnet``, the second argument
+ is the subnet mask of the virtual network segment which is being created
+ or connected to.
+
+ For TUN devices, which facilitate virtual point-to-point IP connections
+ (when used in ``--topology net30`` or ``p2p`` mode), the proper usage of
+ ``--ifconfig`` is to use two private IP addresses which are not a member
+ of any existing subnet which is in use. The IP addresses may be
+ consecutive and should have their order reversed on the remote peer.
+ After the VPN is established, by pinging ``rn``, you will be pinging
+ across the VPN.
+
+ For TAP devices, which provide the ability to create virtual ethernet
+ segments, or TUN devices in ``--topology subnet`` mode (which create
+ virtual "multipoint networks"), ``--ifconfig`` is used to set an IP
+ address and subnet mask just as a physical ethernet adapter would be
+ similarly configured. If you are attempting to connect to a remote
+ ethernet bridge, the IP address and subnet should be set to values which
+ would be valid on the the bridged ethernet segment (note also that DHCP
+ can be used for the same purpose).
+
+ This option, while primarily a proxy for the ``ifconfig``\(8) command,
+ is designed to simplify TUN/TAP tunnel configuration by providing a
+ standard interface to the different ifconfig implementations on
+ different platforms.
+
+ ``--ifconfig`` parameters which are IP addresses can also be specified
+ as a DNS or /etc/hosts file resolvable name.
+
+ For TAP devices, ``--ifconfig`` should not be used if the TAP interface
+ will be getting an IP address lease from a DHCP server.
+
+ Examples:
+ ::
+
+ # tun device in net30/p2p mode
+ ifconfig 10.8.0.2 10.8.0.1
+
+ # tun/tap device in subnet mode
+ ifconfig 10.8.0.2 255.255.255.0
+
+--ifconfig-ipv6 args
+ Configure an IPv6 address on the *tun* device.
+
+ Valid syntax:
+ ::
+
+ ifconfig-ipv6 ipv6addr/bits [ipv6remote]
+
+ The ``ipv6addr/bits`` argument is the IPv6 address to use. The
+ second parameter is used as route target for ``--route-ipv6`` if no
+ gateway is specified.
+
+ The ``--topology`` option has no influence with ``--ifconfig-ipv6``
+
+--ifconfig-noexec
+ Don't actually execute ifconfig/netsh commands, instead pass
+ ``--ifconfig`` parameters to scripts using environmental variables.
+
+--ifconfig-nowarn
+ Don't output an options consistency check warning if the ``--ifconfig``
+ option on this side of the connection doesn't match the remote side.
+ This is useful when you want to retain the overall benefits of the
+ options consistency check (also see ``--disable-occ`` option) while only
+ disabling the ifconfig component of the check.
+
+ For example, if you have a configuration where the local host uses
+ ``--ifconfig`` but the remote host does not, use ``--ifconfig-nowarn``
+ on the local host.
+
+ This option will also silence warnings about potential address conflicts
+ which occasionally annoy more experienced users by triggering "false
+ positive" warnings.
+
+--lladdr address
+ Specify the link layer address, more commonly known as the MAC address.
+ Only applied to TAP devices.
+
+--persist-tun
+ Don't close and reopen TUN/TAP device or run up/down scripts across
+ :code:`SIGUSR1` or ``--ping-restart`` restarts.
+
+ :code:`SIGUSR1` is a restart signal similar to :code:`SIGHUP`, but which
+ offers finer-grained control over reset options.
+
+--redirect-gateway flags
+ Automatically execute routing commands to cause all outgoing IP traffic
+ to be redirected over the VPN. This is a client-side option.
+
+ This option performs three steps:
+
+ (1) Create a static route for the ``--remote`` address which
+ forwards to the pre-existing default gateway. This is done so that
+ ``(3)`` will not create a routing loop.
+
+ (2) Delete the default gateway route.
+
+ (3) Set the new default gateway to be the VPN endpoint address
+ (derived either from ``--route-gateway`` or the second parameter to
+ ``--ifconfig`` when ``--dev tun`` is specified).
+
+ When the tunnel is torn down, all of the above steps are reversed so
+ that the original default route is restored.
+
+ Option flags:
+
+ :code:`local`
+ Add the :code:`local` flag if both OpenVPN peers are directly
+ connected via a common subnet, such as with wireless. The
+ :code:`local` flag will cause step ``(1)`` above to be omitted.
+
+ :code:`autolocal`
+ Try to automatically determine whether to enable :code:`local`
+ flag above.
+
+ :code:`def1`
+ Use this flag to override the default gateway by using
+ :code:`0.0.0.0/1` and :code:`128.0.0.0/1` rather than
+ :code:`0.0.0.0/0`. This has the benefit of overriding but not
+ wiping out the original default gateway.
+
+ :code:`bypass-dhcp`
+ Add a direct route to the DHCP server (if it is non-local) which
+ bypasses the tunnel (Available on Windows clients, may not be
+ available on non-Windows clients).
+
+ :code:`bypass-dns`
+ Add a direct route to the DNS server(s) (if they are non-local)
+ which bypasses the tunnel (Available on Windows clients, may
+ not be available on non-Windows clients).
+
+ :code:`block-local`
+ Block access to local LAN when the tunnel is active, except for
+ the LAN gateway itself. This is accomplished by routing the local
+ LAN (except for the LAN gateway address) into the tunnel.
+
+ :code:`ipv6`
+ Redirect IPv6 routing into the tunnel. This works similar to
+ the :code:`def1` flag, that is, more specific IPv6 routes are added
+ (:code:`2000::/4`, :code:`3000::/4`), covering the whole IPv6
+ unicast space.
+
+ :code:`!ipv4`
+ Do not redirect IPv4 traffic - typically used in the flag pair
+ :code:`ipv6 !ipv4` to redirect IPv6-only.
+
+--redirect-private flags
+ Like ``--redirect-gateway``, but omit actually changing the default gateway.
+ Useful when pushing private subnets.
+
+--route args
+ Add route to routing table after connection is established. Multiple
+ routes can be specified. Routes will be automatically torn down in
+ reverse order prior to TUN/TAP device close.
+
+ Valid syntaxes:
+ ::
+
+ route network/IP
+ route network/IP netmask
+ route network/IP netmask gateway
+ route network/IP netmask gateway metric
+
+ This option is intended as a convenience proxy for the ``route``\(8)
+ shell command, while at the same time providing portable semantics
+ across OpenVPN's platform space.
+
+ ``netmask``
+ defaults to :code:`255.255.255.255` when not given
+
+ ``gateway``
+ default taken from ``--route-gateway`` or the second
+ parameter to ``--ifconfig`` when ``--dev tun`` is specified.
+
+ ``metric``
+ default taken from ``--route-metric`` if set, otherwise :code:`0`.
+
+ The default can be specified by leaving an option blank or setting it to
+ :code:`default`.
+
+ The ``network`` and ``gateway`` parameters can also be specified as a
+ DNS or :code:`/etc/hosts` file resolvable name, or as one of three special
+ keywords:
+
+ :code:`vpn_gateway`
+ The remote VPN endpoint address (derived either from
+ ``--route-gateway`` or the second parameter to ``--ifconfig``
+ when ``--dev tun`` is specified).
+
+ :code:`net_gateway`
+ The pre-existing IP default gateway, read from the
+ routing table (not supported on all OSes).
+
+ :code:`remote_host`
+ The ``--remote`` address if OpenVPN is being run in
+ client mode, and is undefined in server mode.
+
+--route-delay args
+ Valid syntaxes:
+ ::
+
+ route-delay
+ route-delay n
+ route-delay n m
+
+ Delay ``n`` seconds (default :code:`0`) after connection establishment,
+ before adding routes. If ``n`` is :code:`0`, routes will be added
+ immediately upon connection establishment. If ``--route-delay`` is
+ omitted, routes will be added immediately after TUN/TAP device open and
+ ``--up`` script execution, before any ``--user`` or ``--group`` privilege
+ downgrade (or ``--chroot`` execution.)
+
+ This option is designed to be useful in scenarios where DHCP is used to
+ set tap adapter addresses. The delay will give the DHCP handshake time
+ to complete before routes are added.
+
+ On Windows, ``--route-delay`` tries to be more intelligent by waiting
+ ``w`` seconds (default :code:`30` by default) for the TAP-Win32 adapter
+ to come up before adding routes.
+
+--route-ipv6 args
+ Setup IPv6 routing in the system to send the specified IPv6 network into
+ OpenVPN's *tun*.
+
+ Valid syntax:
+ ::
+
+ route-ipv6 ipv6addr/bits [gateway] [metric]
+
+ The gateway parameter is only used for IPv6 routes across *tap* devices,
+ and if missing, the ``ipv6remote`` field from ``--ifconfig-ipv6`` or
+ ``--route-ipv6-gateway`` is used.
+
+--route-gateway arg
+ Specify a default *gateway* for use with ``--route``.
+
+ If :code:`dhcp` is specified as the parameter, the gateway address will
+ be extracted from a DHCP negotiation with the OpenVPN server-side LAN.
+
+ Valid syntaxes:
+ ::
+
+ route-gateway gateway
+ route-gateway dhcp
+
+--route-ipv6-gateway gw
+ Specify a default gateway ``gw`` for use with ``--route-ipv6``.
+
+--route-metric m
+ Specify a default metric ``m`` for use with ``--route``.
+
+--route-noexec
+ Don't add or remove routes automatically. Instead pass routes to
+ ``--route-up`` script using environmental variables.
+
+--route-nopull
+ When used with ``--client`` or ``--pull``, accept options pushed by
+ server EXCEPT for routes, block-outside-dns and dhcp options like DNS
+ servers.
+
+ When used on the client, this option effectively bars the server from
+ adding routes to the client's routing table, however note that this
+ option still allows the server to set the TCP/IP properties of the
+ client's TUN/TAP interface.
+
+--topology mode
+ Configure virtual addressing topology when running in ``--dev tun``
+ mode. This directive has no meaning in ``--dev tap`` mode, which always
+ uses a :code:`subnet` topology.
+
+ If you set this directive on the server, the ``--server`` and
+ ``--server-bridge`` directives will automatically push your chosen
+ topology setting to clients as well. This directive can also be manually
+ pushed to clients. Like the ``--dev`` directive, this directive must
+ always be compatible between client and server.
+
+ ``mode`` can be one of:
+
+ :code:`net30`
+ Use a point-to-point topology, by allocating one /30 subnet
+ per client. This is designed to allow point-to-point semantics when some
+ or all of the connecting clients might be Windows systems. This is the
+ default on OpenVPN 2.0.
+
+ :code:`p2p`
+ Use a point-to-point topology where the remote endpoint of
+ the client's tun interface always points to the local endpoint of the
+ server's tun interface. This mode allocates a single IP address per
+ connecting client. Only use when none of the connecting clients are
+ Windows systems.
+
+ :code:`subnet`
+ Use a subnet rather than a point-to-point topology by
+ configuring the tun interface with a local IP address and subnet mask,
+ similar to the topology used in ``--dev tap`` and ethernet bridging
+ mode. This mode allocates a single IP address per connecting client and
+ works on Windows as well. Only available when server and clients are
+ OpenVPN 2.1 or higher, or OpenVPN 2.0.x which has been manually patched
+ with the ``--topology`` directive code. When used on Windows, requires
+ version 8.2 or higher of the TAP-Win32 driver. When used on \*nix,
+ requires that the tun driver supports an ``ifconfig``\(8) command which
+ sets a subnet instead of a remote endpoint IP address.
+
+ *Note:* Using ``--topology subnet`` changes the interpretation of the
+ arguments of ``--ifconfig`` to mean "address netmask", no longer "local
+ remote".
+
+--tun-mtu n
+ Take the TUN device MTU to be **n** and derive the link MTU from it
+ (default :code:`1500`). In most cases, you will probably want to leave
+ this parameter set to its default value.
+
+ The MTU (Maximum Transmission Units) is the maximum datagram size in
+ bytes that can be sent unfragmented over a particular network path.
+ OpenVPN requires that packets on the control and data channels be sent
+ unfragmented.
+
+ MTU problems often manifest themselves as connections which hang during
+ periods of active usage.
+
+ It's best to use the ``--fragment`` and/or ``--mssfix`` options to deal
+ with MTU sizing issues.
+
+--tun-mtu-extra n
+ Assume that the TUN/TAP device might return as many as ``n`` bytes more
+ than the ``--tun-mtu`` size on read. This parameter defaults to 0, which
+ is sufficient for most TUN devices. TAP devices may introduce additional
+ overhead in excess of the MTU size, and a setting of 32 is the default
+ when TAP devices are used. This parameter only controls internal OpenVPN
+ buffer sizing, so there is no transmission overhead associated with
+ using a larger value.
+
+
+TUN/TAP standalone operations
+-----------------------------
+These two standalone operations will require ``--dev`` and optionally
+``--user`` and/or ``--group``.
+
+--mktun
+ (Standalone) Create a persistent tunnel on platforms which support them
+ such as Linux. Normally TUN/TAP tunnels exist only for the period of
+ time that an application has them open. This option takes advantage of
+ the TUN/TAP driver's ability to build persistent tunnels that live
+ through multiple instantiations of OpenVPN and die only when they are
+ deleted or the machine is rebooted.
+
+ One of the advantages of persistent tunnels is that they eliminate the
+ need for separate ``--up`` and ``--down`` scripts to run the appropriate
+ ``ifconfig``\(8) and ``route``\(8) commands. These commands can be
+ placed in the the same shell script which starts or terminates an
+ OpenVPN session.
+
+ Another advantage is that open connections through the TUN/TAP-based
+ tunnel will not be reset if the OpenVPN peer restarts. This can be
+ useful to provide uninterrupted connectivity through the tunnel in the
+ event of a DHCP reset of the peer's public IP address (see the
+ ``--ipchange`` option above).
+
+ One disadvantage of persistent tunnels is that it is harder to
+ automatically configure their MTU value (see ``--link-mtu`` and
+ ``--tun-mtu`` above).
+
+ On some platforms such as Windows, TAP-Win32 tunnels are persistent by
+ default.
+
+--rmtun
+ (Standalone) Remove a persistent tunnel.
diff --git a/doc/man-sections/windows-options.rst b/doc/man-sections/windows-options.rst
new file mode 100644
index 0000000..eacb9af
--- /dev/null
+++ b/doc/man-sections/windows-options.rst
@@ -0,0 +1,244 @@
+Windows-Specific Options
+-------------------------
+
+--allow-nonadmin TAP-adapter
+ (Standalone) Set ``TAP-adapter`` to allow access from non-administrative
+ accounts. If ``TAP-adapter`` is omitted, all TAP adapters on the system
+ will be configured to allow non-admin access. The non-admin access
+ setting will only persist for the length of time that the TAP-Win32
+ device object and driver remain loaded, and will need to be re-enabled
+ after a reboot, or if the driver is unloaded and reloaded. This
+ directive can only be used by an administrator.
+
+--block-outside-dns
+ Block DNS servers on other network adapters to prevent DNS leaks. This
+ option prevents any application from accessing TCP or UDP port 53 except
+ one inside the tunnel. It uses Windows Filtering Platform (WFP) and
+ works on Windows Vista or later.
+
+ This option is considered unknown on non-Windows platforms and
+ unsupported on Windows XP, resulting in fatal error. You may want to use
+ ``--setenv opt`` or ``--ignore-unknown-option`` (not suitable for
+ Windows XP) to ignore said error. Note that pushing unknown options from
+ server does not trigger fatal errors.
+
+--cryptoapicert select-string
+ *(Windows/OpenSSL Only)* Load the certificate and private key from the
+ Windows Certificate System Store.
+
+ Use this option instead of ``--cert`` and ``--key``.
+
+ This makes it possible to use any smart card, supported by Windows, but
+ also any kind of certificate, residing in the Cert Store, where you have
+ access to the private key. This option has been tested with a couple of
+ different smart cards (GemSAFE, Cryptoflex, and Swedish Post Office eID)
+ on the client side, and also an imported PKCS12 software certificate on
+ the server side.
+
+ To select a certificate, based on a substring search in the
+ certificate's subject:
+ ::
+
+ cryptoapicert "SUBJ:Peter Runestig"
+
+ To select a certificate, based on certificate's thumbprint:
+ ::
+
+ cryptoapicert "THUMB:f6 49 24 41 01 b4 ..."
+
+ The thumbprint hex string can easily be copy-and-pasted from the Windows
+ Certificate Store GUI.
+
+--dhcp-release
+ Ask Windows to release the TAP adapter lease on shutdown. This option
+ has no effect now, as it is enabled by default starting with
+ OpenVPN 2.4.1.
+
+--dhcp-renew
+ Ask Windows to renew the TAP adapter lease on startup. This option is
+ normally unnecessary, as Windows automatically triggers a DHCP
+ renegotiation on the TAP adapter when it comes up, however if you set
+ the TAP-Win32 adapter Media Status property to "Always Connected", you
+ may need this flag.
+
+--ip-win32 method
+ When using ``--ifconfig`` on Windows, set the TAP-Win32 adapter IP
+ address and netmask using ``method``. Don't use this option unless you
+ are also using ``--ifconfig``.
+
+ :code:`manual`
+ Don't set the IP address or netmask automatically. Instead
+ output a message to the console telling the user to configure the
+ adapter manually and indicating the IP/netmask which OpenVPN
+ expects the adapter to be set to.
+
+ :code:`dynamic [offset] [lease-time]`
+ Automatically set the IP address and netmask by replying to DHCP
+ query messages generated by the kernel. This mode is probably the
+ "cleanest" solution for setting the TCP/IP properties since it
+ uses the well-known DHCP protocol. There are, however, two
+ prerequisites for using this mode:
+
+ (1) The TCP/IP properties for the TAP-Win32 adapter must be set
+ to "Obtain an IP address automatically", and
+
+ (2) OpenVPN needs to claim an IP address in the subnet for use
+ as the virtual DHCP server address.
+
+ By default in ``--dev tap`` mode, OpenVPN will take the normally
+ unused first address in the subnet. For example, if your subnet is
+ :code:`192.168.4.0 netmask 255.255.255.0`, then OpenVPN will take
+ the IP address :code:`192.168.4.0` to use as the virtual DHCP
+ server address. In ``--dev tun`` mode, OpenVPN will cause the DHCP
+ server to masquerade as if it were coming from the remote endpoint.
+
+ The optional offset parameter is an integer which is > :code:`-256`
+ and < :code:`256` and which defaults to -1. If offset is positive,
+ the DHCP server will masquerade as the IP address at network
+ address + offset. If offset is negative, the DHCP server will
+ masquerade as the IP address at broadcast address + offset.
+
+ The Windows :code:`ipconfig /all` command can be used to show what
+ Windows thinks the DHCP server address is. OpenVPN will "claim"
+ this address, so make sure to use a free address. Having said that,
+ different OpenVPN instantiations, including different ends of
+ the same connection, can share the same virtual DHCP server
+ address.
+
+ The ``lease-time`` parameter controls the lease time of the DHCP
+ assignment given to the TAP-Win32 adapter, and is denoted in
+ seconds. Normally a very long lease time is preferred because it
+ prevents routes involving the TAP-Win32 adapter from being lost
+ when the system goes to sleep. The default lease time is one year.
+
+ :code:`netsh`
+ Automatically set the IP address and netmask using the Windows
+ command-line "netsh" command. This method appears to work correctly
+ on Windows XP but not Windows 2000.
+
+ :code:`ipapi`
+ Automatically set the IP address and netmask using the Windows IP
+ Helper API. This approach does not have ideal semantics, though
+ testing has indicated that it works okay in practice. If you use
+ this option, it is best to leave the TCP/IP properties for the
+ TAP-Win32 adapter in their default state, i.e. "Obtain an IP
+ address automatically."
+
+ :code:`adaptive` (Default)
+ Try :code:`dynamic` method initially and fail over to :code:`netsh`
+ if the DHCP negotiation with the TAP-Win32 adapter does not succeed
+ in 20 seconds. Such failures have been known to occur when certain
+ third-party firewall packages installed on the client machine block
+ the DHCP negotiation used by the TAP-Win32 adapter. Note that if
+ the :code:`netsh` failover occurs, the TAP-Win32 adapter TCP/IP
+ properties will be reset from DHCP to static, and this will cause
+ future OpenVPN startups using the :code:`adaptive` mode to use
+ :code:`netsh` immediately, rather than trying :code:`dynamic` first.
+
+ To "unstick" the :code:`adaptive` mode from using :code:`netsh`,
+ run OpenVPN at least once using the :code:`dynamic` mode to restore
+ the TAP-Win32 adapter TCP/IP properties to a DHCP configuration.
+
+--pause-exit
+ Put up a "press any key to continue" message on the console prior to
+ OpenVPN program exit. This option is automatically used by the Windows
+ explorer when OpenVPN is run on a configuration file using the
+ right-click explorer menu.
+
+--register-dns
+ Run :code:`ipconfig /flushdns` and :code:`ipconfig /registerdns` on
+ connection initiation. This is known to kick Windows into recognizing
+ pushed DNS servers.
+
+--route-method m
+ Which method ``m`` to use for adding routes on Windows?
+
+ :code:`adaptive` (default)
+ Try IP helper API first. If that fails, fall back to the route.exe
+ shell command.
+
+ :code:`ipapi`
+ Use IP helper API.
+
+ :code:`exe`
+ Call the route.exe shell command.
+
+--service args
+ Should be used when OpenVPN is being automatically executed by another
+ program in such a context that no interaction with the user via display
+ or keyboard is possible.
+
+ Valid syntax:
+ ::
+
+ service exit-event [0|1]
+
+ In general, end-users should never need to explicitly use this option,
+ as it is automatically added by the OpenVPN service wrapper when a given
+ OpenVPN configuration is being run as a service.
+
+ ``exit-event`` is the name of a Windows global event object, and OpenVPN
+ will continuously monitor the state of this event object and exit when
+ it becomes signaled.
+
+ The second parameter indicates the initial state of ``exit-event`` and
+ normally defaults to 0.
+
+ Multiple OpenVPN processes can be simultaneously executed with the same
+ ``exit-event`` parameter. In any case, the controlling process can
+ signal ``exit-event``, causing all such OpenVPN processes to exit.
+
+ When executing an OpenVPN process using the ``--service`` directive,
+ OpenVPN will probably not have a console window to output status/error
+ messages, therefore it is useful to use ``--log`` or ``--log-append`` to
+ write these messages to a file.
+
+--show-adapters
+ (Standalone) Show available TAP-Win32 adapters which can be selected
+ using the ``--dev-node`` option. On non-Windows systems, the
+ ``ifconfig``\(8) command provides similar functionality.
+
+--show-net
+ (Standalone) Show OpenVPN's view of the system routing table and network
+ adapter list.
+
+--show-net-up
+ Output OpenVPN's view of the system routing table and network adapter
+ list to the syslog or log file after the TUN/TAP adapter has been
+ brought up and any routes have been added.
+
+--show-valid-subnets
+ (Standalone) Show valid subnets for ``--dev tun`` emulation. Since the
+ TAP-Win32 driver exports an ethernet interface to Windows, and since TUN
+ devices are point-to-point in nature, it is necessary for the TAP-Win32
+ driver to impose certain constraints on TUN endpoint address selection.
+
+ Namely, the point-to-point endpoints used in TUN device emulation must
+ be the middle two addresses of a /30 subnet (netmask 255.255.255.252).
+
+--tap-sleep n
+ Cause OpenVPN to sleep for ``n`` seconds immediately after the TAP-Win32
+ adapter state is set to "connected".
+
+ This option is intended to be used to troubleshoot problems with the
+ ``--ifconfig`` and ``--ip-win32`` options, and is used to give the
+ TAP-Win32 adapter time to come up before Windows IP Helper API
+ operations are applied to it.
+
+--win-sys path
+ Set the Windows system directory pathname to use when looking for system
+ executables such as ``route.exe`` and ``netsh.exe``. By default, if this
+ directive is not specified, OpenVPN will use the SystemRoot environment
+ variable.
+
+ This option has changed behaviour since OpenVPN 2.3. Earlier you had to
+ define ``--win-sys env`` to use the SystemRoot environment variable,
+ otherwise it defaulted to :code:`C:\\WINDOWS`. It is not needed to use
+ the ``env`` keyword any more, and it will just be ignored. A warning is
+ logged when this is found in the configuration file.
+
+--windows-driver drv
+ Specifies which tun driver to use. Values are :code:`tap-windows6`
+ (default) and :code:`wintun`. This is a Windows-only option.
+ :code:`wintun`" requires ``--dev tun`` and the OpenVPN process to run
+ elevated, or be invoked using the Interactive Service.
diff --git a/doc/management-notes.txt b/doc/management-notes.txt
index 96a0d7d..c203442 100644
--- a/doc/management-notes.txt
+++ b/doc/management-notes.txt
@@ -137,6 +137,16 @@ history while simultaneously activating real-time updates:
The size of the echo buffer is currently hardcoded to 100
messages.
+
+Generally speaking, the OpenVPN Core does not understand echo
+messages at all (so a cooperating GUI and Server can use this
+mechanism for arbitrary information transport).
+
+This said, a few echo commands have been agreed upon between the
+community maintained OpenVPN Windows GUI and Tunnelblick for MacOS,
+and documentation of these can be found in doc/gui-notes.txt.
+
+
COMMAND -- exit, quit
---------------------
@@ -189,7 +199,7 @@ Command examples:
COMMAND -- kill
---------------
-In server mode, kill a particlar client instance.
+In server mode, kill a particular client instance.
Command examples:
@@ -397,6 +407,7 @@ RECONNECTING -- A restart has occurred.
EXITING -- A graceful exit is in progress.
RESOLVE -- (Client only) DNS lookup
TCP_CONNECT -- (Client only) Connecting to TCP server
+AUTH_PENDING -- (Client only) Authentication pending
Command examples:
@@ -427,6 +438,11 @@ Fields (e)-(h) are shown for CONNECTED state,
(e) is available starting from OpenVPN 2.1
(f)-(i) are available starting from OpenVPN 2.4
+For AUTH_PENDING, if (c) is present, it would read
+as "timeout number" where number is the number of seconds
+before authentication will timeout. It is printed as an
+unsigned integer (%u).
+
Real-time state notifications will have a ">STATE:" prefix
prepended to them.
@@ -465,8 +481,12 @@ Command examples:
COMMAND -- version
------------------
-Show the current OpenVPN and Management Interface versions.
+Set the version (integer) of Management Interface supported by the
+client or show the current OpenVPN and Management Interface versions.
+Command examples:
+ version 2 -- Change management version of client to 2 (default = 1)
+ version -- Show the version of OpenVPN and its Management Interface
COMMAND -- auth-retry
---------------------
@@ -588,6 +608,127 @@ interface to approve client connections.
CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
notification for more info.
+COMMAND -- client-pending-auth (OpenVPN 2.5 or higher)
+----------------------------------------------------
+
+Instruct OpenVPN server to send AUTH_PENDING and INFO_PRE message
+to signal a pending authenticating to the client. A pending auth means
+that the connecting requires extra authentication like a one time
+password or doing a single sign on via web.
+
+ client-pending-auth {CID} {EXTRA} {TIMEOUT}
+
+The server will send AUTH_PENDING and INFO_PRE,{EXTRA} to the client. If the
+client supports accepting keywords to AUTH_PENDING (announced via IV_PROTO),
+TIMEOUT parameter will be also be announced to the client to allow it to modify
+its own timeout. The client is expected to inform the user that authentication
+is pending and display the extra information and also show the user the
+remaining time to complete the auth if applicable.
+
+Receiving an AUTH_PENDING message will make the client change its timeout to
+the timeout proposed by the server, even if the timeout is shorter.
+If the client does not receive a packet from the server for hand-window the
+connection times out regardless of the timeout. This ensures that the connection
+still times out relatively quickly in case of network problems. The client will
+continuously send PULL_REQUEST messages to the server until the timeout is reached.
+This message also triggers an ACK message from the server that resets the
+hand-window based timeout.
+
+Both client and server limit the maximum timeout to the smaller value of half the
+--tls-reneg minimum time and --hand-window time (defaults to 60s).
+
+For the format of {EXTRA} see below. For OpenVPN server this is a stateless
+operation and needs to be followed by a client-deny/client-auth[-nt] command
+(that is the result of the out of band authentication).
+
+Before issuing a client-pending-auth to a client instead of a
+client-auth/client-deny, the server should check the IV_SSO
+environment variable for whether the method is supported. Currently
+defined methods are crtext for challenge/response using text
+(e.g., TOTP), openurl and proxy_url for opening a URL in the client to
+continue authentication. A client supporting the first two methods would
+set
+
+ setenv IV_SSO openurl,crtext
+
+The variable name IV_SSO is historic as AUTH_PENDING was first used
+to signal single sign on support. To keep compatibility with existing
+implementations the name IV_SSO is kept in lieu of a better name.
+
+The management interface of the client receives notification of
+pending auth via
+
+>STATE:datetime,AUTH_PENDING,[timeout number]
+
+If {EXTRA} is present the client is informed using INFOMSG
+notification as
+
+>INFOMSG:{EXTRA}
+
+where {EXTRA} is formatted as received from the server.
+Currently defined formats for {EXTRA} are detailed below.
+
+openurl
+========
+For a web based extra authentication (like for
+SSO/SAML) {EXTRA} should be
+
+ OPEN_URL:url
+
+and client should ask the user to open the URL to continue.
+
+The space in a control message is limited, so this url should be kept
+short to avoid issues. If a longer url is required a URL that redirects
+to the longer URL should be sent instead.
+
+A complete documentation how URLs should be handled on the client is available
+in the openvpn3 repository:
+
+https://github.com/OpenVPN/openvpn3/blob/master/doc/webauth.md
+
+proxy_url
+========
+This is a variant of openurl that allows opening a url via an
+HTTP proxy. It could be used to avoid issues with OpenVPN connection's
+persist-tun that may cause the web server to be unreachable.
+The client should announce proxy_url in its IV_SSO and parse the
+PROXY_URL message. The format of {EXTRA} in this case is
+
+ PROXY_URL:<proxy>:<proxy_port>:<proxyuser_base64>:<proxy_password_base64>:url
+
+The proxy should be a literal IPv4 address or IPv6 address enclosed in [] to avoid
+ambiguity in parsing. A literal IP address is preferred as DNS might not be
+available when the client needs to open the url. The IP address will usually
+be the address that client uses to connect to the VPN server. For dual-homed
+VPN servers, the server should respond with the same address that the client
+connects to.
+
+This address is also usually excluded from being redirected over the VPN
+by a host route. If the platform (like Android) uses another way of protecting
+the VPN connection from routing loops, the client needs to also exclude the
+connection to the proxy in the same manner.
+
+Should another IP be used, then the VPN configuration should include a route
+statement to exclude that address from being routed over the VPN.
+
+crtext
+=======
+The format of {EXTRA} is similar to the already used two step authentication
+described in Challenge/Response Protocol section of this document. Since
+most of the fields are not necessary or can be inferred, only the <flags>
+and <challenge_text> fields are used:
+
+ CR_TEXT:<flags>:<challenge_text>
+
+<flags>: a series of optional, comma-separated flags:
+ E : echo the response when the user types it.
+ R : a response is required.
+
+<challenge_text>: the challenge text to be shown to the user.
+
+The client should return the response to the crtext challenge
+using the cr-response command.
+
COMMAND -- client-deny (OpenVPN 2.1 or higher)
-----------------------------------------------
@@ -802,34 +943,70 @@ To accept connecting to the host and port directly, use this command:
proxy NONE
-COMMAND -- rsa-sig (OpenVPN 2.3 or higher)
-------------------------------------------
+COMMAND -- cr-response (OpenVPN 2.5 or higher)
+-------------------------------------------------
+Provides support for sending responses to a challenge/response
+query via INFOMSG,CR_TEXT (client-only). The response should
+be base64 encoded:
+
+ cr-response SGFsbG8gV2VsdCE=
+
+This command is intended to be used after the client receives a
+CR_TEXT challenge (see client-pending-auth section). The argument
+to cr-response is the base64 encoded answer to the challenge and
+depends on the challenge itself. For a TOTP challenge this would be
+a number encoded as base64; for a challenge like "what day is it today?"
+it would be a string encoded as base64.
+
+COMMAND -- pk-sig (OpenVPN 2.5 or higher, management version > 1)
+COMMAND -- rsa-sig (OpenVPN 2.3 or higher, management version <= 1)
+-----------------------------------------------------------------
Provides support for external storage of the private key. Requires the
--management-external-key option. This option can be used instead of "key"
in client mode, and allows the client to run without the need to load the
-actual private key. When the SSL protocol needs to perform an RSA sign
+actual private key. When the SSL protocol needs to perform a sign
operation, the data to be signed will be sent to the management interface
via a notification as follows:
->RSA_SIGN:[BASE64_DATA]
+>PK_SIGN:[BASE64_DATA],[ALG] (if client announces support for management version > 2)
+>PK_SIGN:[BASE64_DATA] (if client announces support for management version > 1)
+>RSA_SIGN:[BASE64_DATA] (only older clients will be prompted like this)
-The management interface client should then create a PKCS#1 v1.5 signature of
+The management interface client should then create an appropriate signature of
the (decoded) BASE64_DATA using the private key and return the SSL signature as
follows:
-rsa-sig
+pk-sig (or rsa-sig)
[BASE64_SIG_LINE]
.
.
.
END
-Base64 encoded output of RSA_private_encrypt() (OpenSSL) or mbedtls_pk_sign()
-(mbed TLS) will provide a correct signature.
+Base 64 encoded output of RSA_private_encrypt for RSA or ECDSA_sign()
+for EC using OpenSSL or mbedtls_pk_sign() using mbed TLS will provide a
+correct signature.
+The rsa-sig interface expects PKCS1 padded signatures for RSA keys
+(RSA_PKCS1_PADDING). EC signatures are always unpadded.
This capability is intended to allow the use of arbitrary cryptographic
service providers with OpenVPN via the management interface.
+New and updated clients are expected to use the version command to announce
+a version > 1 and handle '>PK_SIGN' prompt and respond with 'pk-sig'.
+
+The signature algorithm is indicated in the PK_SIGN request only if the
+management client-version is > 2. In particular, to support TLS1.3 and
+TLS1.2 using OpenSSL 1.1.1, unpadded signature support is required and this
+can be indicated in the signing request only if the client version is > 2"
+
+The currently defined padding algorithms are:
+
+ - RSA_PKCS1_PADDING - PKCS1 padding and RSA signature
+ - RSA_NO_PADDING - No padding may be added for the signature
+ - ECDSA - EC signature.
+
+
COMMAND -- certificate (OpenVPN 2.4 or higher)
----------------------------------------------
Provides support for external storage of the certificate. Requires the
@@ -920,6 +1097,9 @@ PASSWORD -- Used to tell the management interface client that OpenVPN
STATE -- Shows the current OpenVPN state, as controlled
by the "state" command.
+INFOMSG -- Authentication related info from server such as
+ CR_TEXT or OPEN_URL. See description under client-pending-auth
+
The CLIENT notification
-----------------------
@@ -969,6 +1149,35 @@ CLIENT notification types:
>CLIENT:ADDRESS,{CID},{ADDR},{PRI}
+(5) Text based challenge/Response
+
+ >CLIENT:CR_RESPONSE,{CID},{KID},{response_base64}
+ >CLIENT:ENV,name1=val1
+ >CLIENT:ENV,name2=val2
+ >CLIENT:ENV,...
+ >CLIENT:ENV,END
+
+ Use of the cr-response command on the client side will trigger this
+ message on the server side.
+
+ CR_RESPONSE notification fulfills the same purpose as the
+ CRV1 response in the traditional challenge/response. See that section
+ below for more details. Since this uses the same cid as in the original
+ client-pending-auth challenge, we do not include the username and opaque
+ session data in this notification. The string {response_base64} only contains
+ the actual response received from the client.
+
+ It is important to note that OpenVPN2 merely passes the authentication
+ information and does not do any further checks. (E.g. if a CR was issued
+ before or if multiple CR responses were sent from the client or if
+ data has a valid base64 encoding)
+
+ This interface should be be sufficient for almost all challenge/response
+ system that can be implemented with a single round and base64 encoding of the
+ response. Mechanisms that need multiple rounds or more complex answers
+ should implement a different response type than CR_RESPONSE.
+
+
Variables:
CID -- Client ID, numerical ID for each connecting client, sequence = 0,1,2,...
diff --git a/doc/openvpn-examples.5 b/doc/openvpn-examples.5
new file mode 100644
index 0000000..c9d5488
--- /dev/null
+++ b/doc/openvpn-examples.5
@@ -0,0 +1,374 @@
+.\" Man page generated from reStructuredText.
+.
+.TH OPENVPN EXAMPLES 5 "" "" "Configuration files"
+.SH NAME
+openvpn examples \- Secure IP tunnel daemon
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH INTRODUCTION
+.sp
+This man page gives a few simple examples to create OpenVPN setups and configuration files.
+.SH EXAMPLES
+.sp
+Prior to running these examples, you should have OpenVPN installed on
+two machines with network connectivity between them. If you have not yet
+installed OpenVPN, consult the INSTALL file included in the OpenVPN
+distribution.
+.SS Firewall Setup:
+.sp
+If firewalls exist between the two machines, they should be set to
+forward the port OpenVPN is configured to use, in both directions.
+The default for OpenVPN is 1194/udp. If you do not have control
+over the firewalls between the two machines, you may still be able to
+use OpenVPN by adding \fB\-\-ping 15\fP to each of the \fBopenvpn\fP commands
+used below in the examples (this will cause each peer to send out a UDP
+ping to its remote peer once every 15 seconds which will cause many
+stateful firewalls to forward packets in both directions without an
+explicit firewall rule).
+.sp
+Please see your operating system guides for how to configure the firewall
+on your systems.
+.SS VPN Address Setup:
+.sp
+For purposes of our example, our two machines will be called
+\fBbob.example.com\fP and \fBalice.example.com\fP\&. If you are constructing a
+VPN over the internet, then replace \fBbob.example.com\fP and
+\fBalice.example.com\fP with the internet hostname or IP address that each
+machine will use to contact the other over the internet.
+.sp
+Now we will choose the tunnel endpoints. Tunnel endpoints are private IP
+addresses that only have meaning in the context of the VPN. Each machine
+will use the tunnel endpoint of the other machine to access it over the
+VPN. In our example, the tunnel endpoint for bob.example.com will be
+10.4.0.1 and for alice.example.com, 10.4.0.2.
+.sp
+Once the VPN is established, you have essentially created a secure
+alternate path between the two hosts which is addressed by using the
+tunnel endpoints. You can control which network traffic passes between
+the hosts (a) over the VPN or (b) independently of the VPN, by choosing
+whether to use (a) the VPN endpoint address or (b) the public internet
+address, to access the remote host. For example if you are on
+bob.example.com and you wish to connect to \fBalice.example.com\fP via
+\fBssh\fP without using the VPN (since \fBssh\fP has its own built\-in security)
+you would use the command \fBssh alice.example.com\fP\&. However in the same
+scenario, you could also use the command \fBtelnet 10.4.0.2\fP to create a
+telnet session with alice.example.com over the VPN, that would use the
+VPN to secure the session rather than \fBssh\fP\&.
+.sp
+You can use any address you wish for the tunnel endpoints but make sure
+that they are private addresses (such as those that begin with 10 or
+192.168) and that they are not part of any existing subnet on the
+networks of either peer, unless you are bridging. If you use an address
+that is part of your local subnet for either of the tunnel endpoints,
+you will get a weird feedback loop.
+.SS Example 1: A simple tunnel without security
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote alice.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 9
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote bob.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 9
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Now verify the tunnel is working by pinging across the tunnel.
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fB\-\-verb 9\fP option will produce verbose output, similar to the
+\fBtcpdump\fP(8) program. Omit the \fB\-\-verb 9\fP option to have OpenVPN run
+quietly.
+.SS Example 2: A tunnel with static\-key security (i.e. using a pre\-shared secret)
+.sp
+First build a static key on bob.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-genkey \-\-secret key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This command will build a key file called \fBkey\fP (in ascii format). Now
+copy \fBkey\fP to \fBalice.example.com\fP over a secure medium such as by using
+the \fBscp\fP(1) program.
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote alice.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 5 \e
+ \-\-secret key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote bob.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 5 \e
+ \-\-secret key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Now verify the tunnel is working by pinging across the tunnel.
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Example 3: A tunnel with full TLS\-based security
+.sp
+For this test, we will designate \fBbob\fP as the TLS client and \fBalice\fP
+as the TLS server.
+.INDENT 0.0
+.TP
+.B \fINote:\fP
+The client or server designation only has
+meaning for the TLS subsystem. It has no bearing on OpenVPN\(aqs
+peer\-to\-peer, UDP\-based communication model.*
+.UNINDENT
+.sp
+First, build a separate certificate/key pair for both bob and alice (see
+above where \fB\-\-cert\fP is discussed for more info). Then construct
+Diffie Hellman parameters (see above where \fB\-\-dh\fP is discussed for
+more info). You can also use the included test files \fBclient.crt\fP,
+\fBclient.key\fP, \fBserver.crt\fP, \fBserver.key\fP and
+\fBca.crt\fP\&. The \fB\&.crt\fP files are certificates/public\-keys, the
+\fB\&.key\fP files are private keys, and \fBca.crt\fP is a certification
+authority who has signed both \fBclient.crt\fP and \fBserver.crt\fP\&.
+For Diffie Hellman parameters you can use the included file
+\fBdh2048.pem\fP\&.
+.INDENT 0.0
+.TP
+.B \fIWARNING:\fP
+All client, server, and certificate authority certificates
+and keys included in the OpenVPN distribution are totally
+insecure and should be used for testing only.
+.UNINDENT
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote alice.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.1 10.4.0.2 \e
+ \-\-tls\-client \-\-ca ca.crt \e
+ \-\-cert client.crt \-\-key client.key \e
+ \-\-reneg\-sec 60 \-\-verb 5
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-remote bob.example.com \-\-dev tun1 \e
+ \-\-ifconfig 10.4.0.2 10.4.0.1 \e
+ \-\-tls\-server \-\-dh dh1024.pem \-\-ca ca.crt \e
+ \-\-cert server.crt \-\-key server.key \e
+ \-\-reneg\-sec 60 \-\-verb 5
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Now verify the tunnel is working by pinging across the tunnel.
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ping 10.4.0.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Notice the \fB\-\-reneg\-sec 60\fP option we used above. That tells OpenVPN
+to renegotiate the data channel keys every minute. Since we used
+\fB\-\-verb 5\fP above, you will see status information on each new key
+negotiation.
+.sp
+For production operations, a key renegotiation interval of 60 seconds is
+probably too frequent. Omit the \fB\-\-reneg\-sec 60\fP option to use
+OpenVPN\(aqs default key renegotiation interval of one hour.
+.SS Routing:
+.sp
+Assuming you can ping across the tunnel, the next step is to route a
+real subnet over the secure tunnel. Suppose that bob and alice have two
+network interfaces each, one connected to the internet, and the other to
+a private network. Our goal is to securely connect both private
+networks. We will assume that bob\(aqs private subnet is \fI10.0.0.0/24\fP and
+alice\(aqs is \fI10.0.1.0/24\fP\&.
+.sp
+First, ensure that IP forwarding is enabled on both peers. On Linux,
+enable routing:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+echo 1 > /proc/sys/net/ipv4/ip_forward
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This setting is not persistent. Please see your operating systems
+documentation how to properly configure IP forwarding, which is also
+persistent through system boots.
+.sp
+If your system is configured with a firewall. Please see your operating
+systems guide on how to configure the firewall. You typically want to
+allow traffic coming from and going to the tun/tap adapter OpenVPN is
+configured to use.
+.sp
+On bob:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route add \-net 10.0.1.0 netmask 255.255.255.0 gw 10.4.0.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+On alice:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route add \-net 10.0.0.0 netmask 255.255.255.0 gw 10.4.0.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Now any machine on the \fI10.0.0.0/24\fP subnet can access any machine on the
+\fI10.0.1.0/24\fP subnet over the secure tunnel (or vice versa).
+.sp
+In a production environment, you could put the route command(s) in a
+script and execute with the \fB\-\-up\fP option.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/openvpn-examples.5.html b/doc/openvpn-examples.5.html
new file mode 100644
index 0000000..a0dac40
--- /dev/null
+++ b/doc/openvpn-examples.5.html
@@ -0,0 +1,582 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.16: http://docutils.sourceforge.net/" />
+<title>openvpn examples</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="openvpn-examples">
+<h1 class="title">openvpn examples</h1>
+<h2 class="subtitle" id="secure-ip-tunnel-daemon">Secure IP tunnel daemon</h2>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="manual-section field"><th class="docinfo-name">Manual section:</th><td class="field-body">5</td>
+</tr>
+<tr class="manual-group field"><th class="docinfo-name">Manual group:</th><td class="field-body">Configuration files</td>
+</tr>
+</tbody>
+</table>
+<div class="section" id="introduction">
+<h1>INTRODUCTION</h1>
+<p>This man page gives a few simple examples to create OpenVPN setups and configuration files.</p>
+</div>
+<div class="section" id="examples">
+<h1>EXAMPLES</h1>
+<p>Prior to running these examples, you should have OpenVPN installed on
+two machines with network connectivity between them. If you have not yet
+installed OpenVPN, consult the INSTALL file included in the OpenVPN
+distribution.</p>
+<div class="section" id="firewall-setup">
+<h2>Firewall Setup:</h2>
+<p>If firewalls exist between the two machines, they should be set to
+forward the port OpenVPN is configured to use, in both directions.
+The default for OpenVPN is 1194/udp. If you do not have control
+over the firewalls between the two machines, you may still be able to
+use OpenVPN by adding <tt class="docutils literal"><span class="pre">--ping</span> 15</tt> to each of the <tt class="docutils literal">openvpn</tt> commands
+used below in the examples (this will cause each peer to send out a UDP
+ping to its remote peer once every 15 seconds which will cause many
+stateful firewalls to forward packets in both directions without an
+explicit firewall rule).</p>
+<p>Please see your operating system guides for how to configure the firewall
+on your systems.</p>
+</div>
+<div class="section" id="vpn-address-setup">
+<h2>VPN Address Setup:</h2>
+<p>For purposes of our example, our two machines will be called
+<tt class="docutils literal">bob.example.com</tt> and <tt class="docutils literal">alice.example.com</tt>. If you are constructing a
+VPN over the internet, then replace <tt class="docutils literal">bob.example.com</tt> and
+<tt class="docutils literal">alice.example.com</tt> with the internet hostname or IP address that each
+machine will use to contact the other over the internet.</p>
+<p>Now we will choose the tunnel endpoints. Tunnel endpoints are private IP
+addresses that only have meaning in the context of the VPN. Each machine
+will use the tunnel endpoint of the other machine to access it over the
+VPN. In our example, the tunnel endpoint for bob.example.com will be
+10.4.0.1 and for alice.example.com, 10.4.0.2.</p>
+<p>Once the VPN is established, you have essentially created a secure
+alternate path between the two hosts which is addressed by using the
+tunnel endpoints. You can control which network traffic passes between
+the hosts (a) over the VPN or (b) independently of the VPN, by choosing
+whether to use (a) the VPN endpoint address or (b) the public internet
+address, to access the remote host. For example if you are on
+bob.example.com and you wish to connect to <tt class="docutils literal">alice.example.com</tt> via
+<tt class="docutils literal">ssh</tt> without using the VPN (since <strong>ssh</strong> has its own built-in security)
+you would use the command <tt class="docutils literal">ssh alice.example.com</tt>. However in the same
+scenario, you could also use the command <tt class="docutils literal">telnet 10.4.0.2</tt> to create a
+telnet session with alice.example.com over the VPN, that would use the
+VPN to secure the session rather than <tt class="docutils literal">ssh</tt>.</p>
+<p>You can use any address you wish for the tunnel endpoints but make sure
+that they are private addresses (such as those that begin with 10 or
+192.168) and that they are not part of any existing subnet on the
+networks of either peer, unless you are bridging. If you use an address
+that is part of your local subnet for either of the tunnel endpoints,
+you will get a weird feedback loop.</p>
+</div>
+<div class="section" id="example-1-a-simple-tunnel-without-security">
+<h2>Example 1: A simple tunnel without security</h2>
+<p>On bob:</p>
+<pre class="literal-block">
+openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 9
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 9
+</pre>
+<p>Now verify the tunnel is working by pinging across the tunnel.</p>
+<p>On bob:</p>
+<pre class="literal-block">
+ping 10.4.0.2
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+ping 10.4.0.1
+</pre>
+<p>The <tt class="docutils literal"><span class="pre">--verb</span> 9</tt> option will produce verbose output, similar to the
+<tt class="docutils literal">tcpdump</tt>(8) program. Omit the <tt class="docutils literal"><span class="pre">--verb</span> 9</tt> option to have OpenVPN run
+quietly.</p>
+</div>
+<div class="section" id="example-2-a-tunnel-with-static-key-security-i-e-using-a-pre-shared-secret">
+<h2>Example 2: A tunnel with static-key security (i.e. using a pre-shared secret)</h2>
+<p>First build a static key on bob.</p>
+<pre class="literal-block">
+openvpn --genkey --secret key
+</pre>
+<p>This command will build a key file called <tt class="docutils literal">key</tt> (in ascii format). Now
+copy <tt class="docutils literal">key</tt> to <tt class="docutils literal">alice.example.com</tt> over a secure medium such as by using
+the <tt class="docutils literal">scp</tt>(1) program.</p>
+<p>On bob:</p>
+<pre class="literal-block">
+openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 5 \
+ --secret key
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 5 \
+ --secret key
+</pre>
+<p>Now verify the tunnel is working by pinging across the tunnel.</p>
+<p>On bob:</p>
+<pre class="literal-block">
+ping 10.4.0.2
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+ping 10.4.0.1
+</pre>
+</div>
+<div class="section" id="example-3-a-tunnel-with-full-tls-based-security">
+<h2>Example 3: A tunnel with full TLS-based security</h2>
+<p>For this test, we will designate <tt class="docutils literal">bob</tt> as the TLS client and <tt class="docutils literal">alice</tt>
+as the TLS server.</p>
+<dl class="docutils">
+<dt><em>Note:</em></dt>
+<dd>The client or server designation only has
+meaning for the TLS subsystem. It has no bearing on OpenVPN's
+peer-to-peer, UDP-based communication model.*</dd>
+</dl>
+<p>First, build a separate certificate/key pair for both bob and alice (see
+above where <tt class="docutils literal"><span class="pre">--cert</span></tt> is discussed for more info). Then construct
+Diffie Hellman parameters (see above where <tt class="docutils literal"><span class="pre">--dh</span></tt> is discussed for
+more info). You can also use the included test files <code>client.crt</code>,
+<code>client.key</code>, <code>server.crt</code>, <code>server.key</code> and
+<code>ca.crt</code>. The <tt class="docutils literal">.crt</tt> files are certificates/public-keys, the
+<tt class="docutils literal">.key</tt> files are private keys, and <code>ca.crt</code> is a certification
+authority who has signed both <code>client.crt</code> and <code>server.crt</code>.
+For Diffie Hellman parameters you can use the included file
+<code>dh2048.pem</code>.</p>
+<dl class="docutils">
+<dt><em>WARNING:</em></dt>
+<dd>All client, server, and certificate authority certificates
+and keys included in the OpenVPN distribution are totally
+insecure and should be used for testing only.</dd>
+</dl>
+<p>On bob:</p>
+<pre class="literal-block">
+openvpn --remote alice.example.com --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 \
+ --tls-client --ca ca.crt \
+ --cert client.crt --key client.key \
+ --reneg-sec 60 --verb 5
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+openvpn --remote bob.example.com --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 \
+ --tls-server --dh dh1024.pem --ca ca.crt \
+ --cert server.crt --key server.key \
+ --reneg-sec 60 --verb 5
+</pre>
+<p>Now verify the tunnel is working by pinging across the tunnel.</p>
+<p>On bob:</p>
+<pre class="literal-block">
+ping 10.4.0.2
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+ping 10.4.0.1
+</pre>
+<p>Notice the <tt class="docutils literal"><span class="pre">--reneg-sec</span> 60</tt> option we used above. That tells OpenVPN
+to renegotiate the data channel keys every minute. Since we used
+<tt class="docutils literal"><span class="pre">--verb</span> 5</tt> above, you will see status information on each new key
+negotiation.</p>
+<p>For production operations, a key renegotiation interval of 60 seconds is
+probably too frequent. Omit the <tt class="docutils literal"><span class="pre">--reneg-sec</span> 60</tt> option to use
+OpenVPN's default key renegotiation interval of one hour.</p>
+</div>
+<div class="section" id="routing">
+<h2>Routing:</h2>
+<p>Assuming you can ping across the tunnel, the next step is to route a
+real subnet over the secure tunnel. Suppose that bob and alice have two
+network interfaces each, one connected to the internet, and the other to
+a private network. Our goal is to securely connect both private
+networks. We will assume that bob's private subnet is <em>10.0.0.0/24</em> and
+alice's is <em>10.0.1.0/24</em>.</p>
+<p>First, ensure that IP forwarding is enabled on both peers. On Linux,
+enable routing:</p>
+<pre class="literal-block">
+echo 1 &gt; /proc/sys/net/ipv4/ip_forward
+</pre>
+<p>This setting is not persistent. Please see your operating systems
+documentation how to properly configure IP forwarding, which is also
+persistent through system boots.</p>
+<p>If your system is configured with a firewall. Please see your operating
+systems guide on how to configure the firewall. You typically want to
+allow traffic coming from and going to the tun/tap adapter OpenVPN is
+configured to use.</p>
+<p>On bob:</p>
+<pre class="literal-block">
+route add -net 10.0.1.0 netmask 255.255.255.0 gw 10.4.0.2
+</pre>
+<p>On alice:</p>
+<pre class="literal-block">
+route add -net 10.0.0.0 netmask 255.255.255.0 gw 10.4.0.1
+</pre>
+<p>Now any machine on the <em>10.0.0.0/24</em> subnet can access any machine on the
+<em>10.0.1.0/24</em> subnet over the secure tunnel (or vice versa).</p>
+<p>In a production environment, you could put the route command(s) in a
+script and execute with the <tt class="docutils literal"><span class="pre">--up</span></tt> option.</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/doc/openvpn-examples.5.rst b/doc/openvpn-examples.5.rst
new file mode 100644
index 0000000..988b602
--- /dev/null
+++ b/doc/openvpn-examples.5.rst
@@ -0,0 +1,17 @@
+===============================
+ openvpn examples
+===============================
+-------------------------
+ Secure IP tunnel daemon
+-------------------------
+
+:Manual section: 5
+:Manual group: Configuration files
+
+
+INTRODUCTION
+============
+
+This man page gives a few simple examples to create OpenVPN setups and configuration files.
+
+.. include:: man-sections/examples.rst
diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 8038e1f..ceb6348 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -1,180 +1,195 @@
-.\" 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.
-.\"
-.\" Manual page for openvpn
-.\"
-.\" SH section heading
-.\" SS subsection heading
-.\" LP paragraph
-.\" IP indented paragraph
-.\" TP hanging label
-.\"
-.\" .nf -- no formatting
-.\" .fi -- resume formatting
-.\" .ft 3 -- boldface
-.\" .ft -- normal face
-.\" .in +|-{n} -- indent
-.\"
-.\" Support macros - this is not present on all platforms
-.\" Continuation line for .TP header.
-.de TQ
-. br
-. ns
-. TP \\$1\" no doublequotes around argument!
-..
-.\" End of TQ macro
-.TH openvpn 8 "28 February 2018"
-.\"*********************************************************
+.\" Man page generated from reStructuredText.
+.
+.TH OPENVPN 8 "" "" "System Manager's Manual"
.SH NAME
-openvpn \- secure IP tunnel daemon.
-.\"*********************************************************
+openvpn \- Secure IP tunnel daemon
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
.SH SYNOPSIS
-.ft 3
-openvpn [ options ... ]
-.ft
-.\"*********************************************************
+.nf
+\fBopenvpn\fP [ options ... ]
+\fBopenvpn\fP \fB\-\-help\fP
+.fi
+.sp
.SH INTRODUCTION
-.LP
-OpenVPN is an open source VPN daemon by James Yonan.
-Because OpenVPN tries to
-be a universal VPN tool offering a great deal of flexibility,
-there are a lot of options on this manual page.
-If you're new to OpenVPN, you might want to skip ahead to the
-examples section where you will see how to construct simple
-VPNs on the command line without even needing a configuration file.
-
-Also note that there's more documentation and examples on
-the OpenVPN web site:
-.I http://openvpn.net/
-
-And if you would like to see a shorter version of this manual,
-see the openvpn usage message which can be obtained by
-running
-.B openvpn
+.sp
+OpenVPN is an open source VPN daemon by James Yonan. Because OpenVPN
+tries to be a universal VPN tool offering a great deal of flexibility,
+there are a lot of options on this manual page. If you\(aqre new to
+OpenVPN, you might want to skip ahead to the examples section where you
+will see how to construct simple VPNs on the command line without even
+needing a configuration file.
+.sp
+Also note that there\(aqs more documentation and examples on the OpenVPN
+web site: \fI\%https://openvpn.net/\fP
+.sp
+And if you would like to see a shorter version of this manual, see the
+openvpn usage message which can be obtained by running \fBopenvpn\fP
without any parameters.
-.\"*********************************************************
.SH DESCRIPTION
-.LP
-OpenVPN is a robust and highly flexible VPN daemon.
-OpenVPN supports SSL/TLS security, ethernet bridging,
-TCP or UDP tunnel transport through proxies or NAT,
-support for dynamic IP addresses and DHCP,
-scalability to hundreds or thousands of users,
-and portability to most major OS platforms.
-
-OpenVPN is tightly bound to the OpenSSL library, and derives much
-of its crypto capabilities from it.
-
-OpenVPN supports
-conventional encryption
-using a pre\-shared secret key
-.B (Static Key mode)
-or
-public key security
-.B (SSL/TLS mode)
-using client & server certificates.
-OpenVPN also
-supports non\-encrypted TCP/UDP tunnels.
-
-OpenVPN is designed to work with the
-.B TUN/TAP
-virtual networking interface that exists on most platforms.
-
+.sp
+OpenVPN is a robust and highly flexible VPN daemon. OpenVPN supports
+SSL/TLS security, ethernet bridging, TCP or UDP tunnel transport through
+proxies or NAT, support for dynamic IP addresses and DHCP, scalability
+to hundreds or thousands of users, and portability to most major OS
+platforms.
+.sp
+OpenVPN is tightly bound to the OpenSSL library, and derives much of its
+crypto capabilities from it.
+.sp
+OpenVPN supports conventional encryption using a pre\-shared secret key
+\fB(Static Key mode)\fP or public key security \fB(SSL/TLS mode)\fP using
+client & server certificates. OpenVPN also supports non\-encrypted
+TCP/UDP tunnels.
+.sp
+OpenVPN is designed to work with the \fBTUN/TAP\fP virtual networking
+interface that exists on most platforms.
+.sp
Overall, OpenVPN aims to offer many of the key features of IPSec but
with a relatively lightweight footprint.
-.\"*********************************************************
.SH OPTIONS
-OpenVPN allows any option to be placed either on the command line
-or in a configuration file. Though all command line options are preceded
-by a double\-leading\-dash ("\-\-"), this prefix can be removed when
-an option is placed in a configuration file.
-.\"*********************************************************
+.sp
+OpenVPN allows any option to be placed either on the command line or in
+a configuration file. Though all command line options are preceded by a
+double\-leading\-dash ("\-\-"), this prefix can be removed when an option is
+placed in a configuration file.
+.SS Generic Options
+.sp
+This section covers generic options which are accessible regardless of
+which mode OpenVPN is configured as.
+.INDENT 0.0
.TP
.B \-\-help
Show options.
-.\"*********************************************************
.TP
-.B \-\-config file
-Load additional config options from
-.B file
-where each line corresponds to one command line option,
-but with the leading '\-\-' removed.
-
-If
-.B \-\-config file
-is the only option to the openvpn command,
-the
-.B \-\-config
-can be removed, and the command can be given as
-.B openvpn file
-
-Note that
-configuration files can be nested to a reasonable depth.
-
-Double quotation or single quotation characters ("", '')
-can be used to enclose single parameters containing whitespace,
-and "#" or ";" characters in the first column
-can be used to denote comments.
-
-Note that OpenVPN 2.0 and higher performs backslash\-based shell
-escaping for characters not in single quotations,
-so the following mappings should be observed:
-
+.B \-\-auth\-nocache
+Don\(aqt cache \fB\-\-askpass\fP or \fB\-\-auth\-user\-pass\fP username/passwords in
+virtual memory.
+.sp
+If specified, this directive will cause OpenVPN to immediately forget
+username/password inputs after they are used. As a result, when OpenVPN
+needs a username/password, it will prompt for input from stdin, which
+may be multiple times during the duration of an OpenVPN session.
+.sp
+When using \fB\-\-auth\-nocache\fP in combination with a user/password file
+and \fB\-\-chroot\fP or \fB\-\-daemon\fP, make sure to use an absolute path.
+.sp
+This directive does not affect the \fB\-\-http\-proxy\fP username/password.
+It is always cached.
+.TP
+.BI \-\-cd \ dir
+Change directory to \fBdir\fP prior to reading any files such as
+configuration files, key files, scripts, etc. \fBdir\fP should be an
+absolute path, with a leading "/", and without any references to the
+current directory such as \fB\&.\fP or \fB\&..\fP\&.
+.sp
+This option is useful when you are running OpenVPN in \fB\-\-daemon\fP mode,
+and you want to consolidate all of your OpenVPN control files in one
+location.
+.TP
+.BI \-\-chroot \ dir
+Chroot to \fBdir\fP after initialization. \fB\-\-chroot\fP essentially
+redefines \fBdir\fP as being the top level directory tree (/). OpenVPN
+will therefore be unable to access any files outside this tree. This can
+be desirable from a security standpoint.
+.sp
+Since the chroot operation is delayed until after initialization, most
+OpenVPN options that reference files will operate in a pre\-chroot
+context.
+.sp
+In many cases, the \fBdir\fP parameter can point to an empty directory,
+however complications can result when scripts or restarts are executed
+after the chroot operation.
+.sp
+Note: The SSL library will probably need /dev/urandom to be available
+inside the chroot directory \fBdir\fP\&. This is because SSL libraries
+occasionally need to collect fresh random. Newer linux kernels and some
+BSDs implement a getrandom() or getentropy() syscall that removes the
+need for /dev/urandom to be available.
+.TP
+.BI \-\-config \ file
+Load additional config options from \fBfile\fP where each line corresponds
+to one command line option, but with the leading \(aq\-\-\(aq removed.
+.sp
+If \fB\-\-config file\fP is the only option to the openvpn command, the
+\fB\-\-config\fP can be removed, and the command can be given as \fBopenvpn
+file\fP
+.sp
+Note that configuration files can be nested to a reasonable depth.
+.sp
+Double quotation or single quotation characters ("", \(aq\(aq) can be used to
+enclose single parameters containing whitespace, and "#" or ";"
+characters in the first column can be used to denote comments.
+.sp
+Note that OpenVPN 2.0 and higher performs backslash\-based shell escaping
+for characters not in single quotations, so the following mappings
+should be observed:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-\\\\ Maps to a single backslash character (\\).
-\\" Pass a literal doublequote character ("), don't
+.ft C
+\e\e Maps to a single backslash character (\e).
+\e" Pass a literal doublequote character ("), don\(aqt
interpret it as enclosing a parameter.
-\\[SPACE] Pass a literal space or tab character, don't
+\e[SPACE] Pass a literal space or tab character, don\(aqt
interpret it as a parameter delimiter.
-.in -4
-.ft
+.ft P
.fi
-
-For example on Windows, use double backslashes to
-represent pathnames:
-
+.UNINDENT
+.UNINDENT
+.sp
+For example on Windows, use double backslashes to represent pathnames:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-secret "c:\\\\OpenVPN\\\\secret.key"
-.in -4
-.ft
+.ft C
+secret "c:\e\eOpenVPN\e\esecret.key"
+.ft P
.fi
-
-For examples of configuration files,
-see
-.I http://openvpn.net/examples.html
-
+.UNINDENT
+.UNINDENT
+.sp
+For examples of configuration files, see
+\fI\%https://openvpn.net/community\-resources/how\-to/\fP
+.sp
Here is an example configuration file:
-
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
+.ft C
#
# Sample OpenVPN configuration file for
# using a pre\-shared static key.
#
-# '#' or ';' may be used to delimit comments.
+# \(aq#\(aq or \(aq;\(aq may be used to delimit comments.
# Use a dynamic tun device.
dev tun
@@ -188,6697 +203,5644 @@ ifconfig 10.1.0.1 10.1.0.2
# Our pre\-shared static key
secret static.key
-.in -4
-.ft
+.ft P
.fi
-.\"*********************************************************
-.SS Tunnel Options:
-.TP
-.B \-\-mode m
-Set OpenVPN major mode. By default, OpenVPN runs in
-point\-to\-point mode ("p2p"). OpenVPN 2.0 introduces
-a new mode ("server") which implements a multi\-client
-server capability.
-.\"*********************************************************
-.TP
-.B \-\-local host
-Local host name or IP address for bind.
-If specified, OpenVPN will bind to this address only.
-If unspecified, OpenVPN will bind to all interfaces.
-.\"*********************************************************
-.TP
-.B \-\-remote host [port] [proto]
-Remote host name or IP address. On the client, multiple
-.B \-\-remote
-options may be specified for redundancy, each referring
-to a different OpenVPN server. Specifying multiple
-.B \-\-remote
-options for this purpose is a special case of the more
-general connection\-profile feature. See the
-.B <connection>
-documentation below.
-
-The OpenVPN client will try to connect to a server at
-.B host:port
-in the order specified by the list of
-.B \-\-remote
-options.
-
-.B proto
-indicates the protocol to use when connecting with the
-remote, and may be "tcp" or "udp".
-
-For forcing IPv4 or IPv6 connection suffix tcp or udp
-with 4/6 like udp4/udp6/tcp4/tcp6.
-
-The client will move on to the next host in the list,
-in the event of connection failure.
-Note that at any given time, the OpenVPN client
-will at most be connected to
-one server.
-
-Note that since UDP is connectionless, connection failure
-is defined by the
-.B \-\-ping
-and
-.B \-\-ping\-restart
-options.
-
-Note the following corner case: If you use multiple
-.B \-\-remote
-options, AND you are dropping root privileges on
-the client with
-.B \-\-user
-and/or
-.B \-\-group,
-AND the client is running a non\-Windows OS, if the client needs
-to switch to a different server, and that server pushes
-back different TUN/TAP or route settings, the client may lack
-the necessary privileges to close and reopen the TUN/TAP interface.
-This could cause the client to exit with a fatal error.
-
-If
-.B \-\-remote
-is unspecified, OpenVPN will listen
-for packets from any IP address, but will not act on those packets unless
-they pass all authentication tests. This requirement for authentication
-is binding on all potential peers, even those from known and supposedly
-trusted IP addresses (it is very easy to forge a source IP address on
-a UDP packet).
-
-When used in TCP mode,
-.B \-\-remote
-will act as a filter, rejecting connections from any host which does
-not match
-.B host.
-
-If
-.B host
-is a DNS name which resolves to multiple IP addresses,
-OpenVPN will try them in the order that the system getaddrinfo()
-presents them, so priorization and DNS randomization is done
-by the system library. Unless an IP version is forced by the
-protocol specification (4/6 suffix), OpenVPN will try both IPv4
-and IPv6 addresses, in the order getaddrinfo() returns them.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-daemon \ progname
+Become a daemon after all initialization functions are completed. This
+option will cause all message and error output to be sent to the syslog
+file (such as \fB/var/log/messages\fP), except for the output of
+scripts and ifconfig commands, which will go to \fB/dev/null\fP unless
+otherwise redirected. The syslog redirection occurs immediately at the
+point that \fB\-\-daemon\fP is parsed on the command line even though the
+daemonization point occurs later. If one of the \fB\-\-log\fP options is
+present, it will supersede syslog redirection.
+.sp
+The optional \fBprogname\fP parameter will cause OpenVPN to report its
+program name to the system logger as \fBprogname\fP\&. This can be useful in
+linking OpenVPN messages in the syslog file with specific tunnels. When
+unspecified, \fBprogname\fP defaults to "openvpn".
+.sp
+When OpenVPN is run with the \fB\-\-daemon\fP option, it will try to delay
+daemonization until the majority of initialization functions which are
+capable of generating fatal errors are complete. This means that
+initialization scripts can test the return status of the openvpn command
+for a fairly reliable indication of whether the command has correctly
+initialized and entered the packet forwarding event loop.
+.sp
+In OpenVPN, the vast majority of errors which occur after initialization
+are non\-fatal.
+.sp
+Note: as soon as OpenVPN has daemonized, it can not ask for usernames,
+passwords, or key pass phrases anymore. This has certain consequences,
+namely that using a password\-protected private key will fail unless the
+\fB\-\-askpass\fP option is used to tell OpenVPN to ask for the pass phrase
+(this requirement is new in v2.3.7, and is a consequence of calling
+daemon() before initializing the crypto layer).
+.sp
+Further, using \fB\-\-daemon\fP together with \fB\-\-auth\-user\-pass\fP (entered
+on console) and \fB\-\-auth\-nocache\fP will fail as soon as key
+renegotiation (and reauthentication) occurs.
.TP
-.B \-\-remote\-random\-hostname
-Prepend a random string (6 bytes, 12 hex characters) to hostname to prevent
-DNS caching. For example, "foo.bar.gov" would be modified to
-"<random\-chars>.foo.bar.gov".
-.\"*********************************************************
-.TP
-.B <connection>
-Define a client connection
-profile. Client connection profiles are groups of OpenVPN options that
-describe how to connect to a given OpenVPN server. Client connection
-profiles are specified within an OpenVPN configuration file, and
-each profile is bracketed by
-.B <connection>
-and
-.B </connection>.
-
-An OpenVPN client will try each connection profile sequentially
-until it achieves a successful connection.
-
-.B \-\-remote\-random
-can be used to initially "scramble" the connection
-list.
-
-Here is an example of connection profile usage:
-
+.B \-\-disable\-occ
+Don\(aqt output a warning message if option inconsistencies are detected
+between peers. An example of an option inconsistency would be where one
+peer uses \fB\-\-dev tun\fP while the other peer uses \fB\-\-dev tap\fP\&.
+.sp
+Use of this option is discouraged, but is provided as a temporary fix in
+situations where a recent version of OpenVPN must connect to an old
+version.
+.TP
+.BI \-\-engine \ engine\-name
+Enable OpenSSL hardware\-based crypto engine functionality.
+.sp
+If \fBengine\-name\fP is specified, use a specific crypto engine. Use the
+\fB\-\-show\-engines\fP standalone option to list the crypto engines which
+are supported by OpenSSL.
+.TP
+.B \-\-fast\-io
+(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
+poll/epoll/select prior to the write operation. The purpose of such a
+call would normally be to block until the device or socket is ready to
+accept the write. Such blocking is unnecessary on some platforms which
+don\(aqt support write blocking on UDP sockets or TUN/TAP devices. In such
+cases, one can optimize the event loop by avoiding the poll/epoll/select
+call, improving CPU efficiency by 5% to 10%.
+.sp
+This option can only be used on non\-Windows systems, when \fB\-\-proto
+udp\fP is specified, and when \fB\-\-shaper\fP is NOT specified.
+.TP
+.BI \-\-group \ group
+Similar to the \fB\-\-user\fP option, this option changes the group ID of
+the OpenVPN process to \fBgroup\fP after initialization.
+.TP
+.BI \-\-ignore\-unknown\-option \ args
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-client
-dev tun
-
-<connection>
-remote 198.19.34.56 1194 udp
-</connection>
-
-<connection>
-remote 198.19.34.56 443 tcp
-</connection>
-
-<connection>
-remote 198.19.34.56 443 tcp
-http\-proxy 192.168.0.8 8080
-</connection>
-
-<connection>
-remote 198.19.36.99 443 tcp
-http\-proxy 192.168.0.8 8080
-</connection>
-
-persist\-key
-persist\-tun
-pkcs12 client.p12
-remote\-cert\-tls server
-verb 3
-.in -4
-.ft
+.ft C
+ignore\-unknown\-options opt1 opt2 opt3 ... optN
+.ft P
.fi
-
-First we try to connect to a server at 198.19.34.56:1194 using UDP.
-If that fails, we then try to connect to 198.19.34.56:443 using TCP.
-If that also fails, then try connecting through an HTTP proxy at
-192.168.0.8:8080 to 198.19.34.56:443 using TCP. Finally, try to
-connect through the same proxy to a server at 198.19.36.99:443
-using TCP.
-
-The following OpenVPN options may be used inside of
-a
-.B <connection>
-block:
-
-.B bind,
-.B connect\-retry,
-.B connect\-retry\-max,
-.B connect\-timeout,
-.B explicit\-exit\-notify,
-.B float,
-.B fragment,
-.B http\-proxy,
-.B http\-proxy\-option,
-.B link\-mtu,
-.B local,
-.B lport,
-.B mssfix,
-.B mtu\-disc,
-.B nobind,
-.B port,
-.B proto,
-.B remote,
-.B rport,
-.B socks\-proxy,
-.B tun\-mtu and
-.B tun\-mtu\-extra.
-
-A defaulting mechanism exists for specifying options to apply to
-all
-.B <connection>
-profiles. If any of the above options (with the exception of
-.B remote
-) appear outside of a
-.B <connection>
-block, but in a configuration file which has one or more
-.B <connection>
-blocks, the option setting will be used as a default for
-.B <connection>
-blocks which follow it in the configuration file.
-
-For example, suppose the
-.B nobind
-option were placed in the sample configuration file above, near
-the top of the file, before the first
-.B <connection>
-block. The effect would be as if
-.B nobind
-were declared in all
-.B <connection>
-blocks below it.
-.\"*********************************************************
-.TP
-.B \-\-proto\-force p
-When iterating through connection profiles,
-only consider profiles using protocol
-.B p
-('tcp'|'udp').
-.\"*********************************************************
-.TP
-.B \-\-remote\-random
-When multiple
-.B \-\-remote
-address/ports are specified, or if connection profiles are being
-used, initially randomize the order of the list
-as a kind of basic load\-balancing measure.
-.\"*********************************************************
-.TP
-.B \-\-proto p
-Use protocol
-.B p
-for communicating with remote host.
-.B p
-can be
-.B udp,
-.B tcp\-client,
-or
-.B tcp\-server.
-
-The default protocol is
-.B udp
-when
-.B \-\-proto
-is not specified.
-
-For UDP operation,
-.B \-\-proto udp
-should be specified on both peers.
-
-For TCP operation, one peer must use
-.B \-\-proto tcp\-server
-and the other must use
-.B \-\-proto tcp\-client.
-A peer started with
-.B tcp\-server
-will wait indefinitely for an incoming connection. A peer
-started with
-.B tcp\-client
-will attempt to connect, and if that fails, will sleep for 5
-seconds (adjustable via the
-.B \-\-connect\-retry
-option) and try again infinite or up to N retries (adjustable via the
-.B \-\-connect\-retry\-max
-option). Both TCP client and server will simulate
-a SIGUSR1 restart signal if either side resets the connection.
-
-OpenVPN is designed to operate optimally over UDP, but TCP capability is provided
-for situations where UDP cannot be used.
-In comparison with UDP, TCP will usually be
-somewhat less efficient and less robust when used over unreliable or congested
-networks.
-
-This article outlines some of problems with tunneling IP over TCP:
-
-.I http://sites.inka.de/sites/bigred/devel/tcp\-tcp.html
-
-There are certain cases, however, where using TCP may be advantageous from
-a security and robustness perspective, such as tunneling non\-IP or
-application\-level UDP protocols, or tunneling protocols which don't
-possess a built\-in reliability layer.
-.\"*********************************************************
-.TP
-.B \-\-connect\-retry n [max]
-Wait
-.B n
-seconds between connection attempts (default=5). Repeated reconnection
-attempts are slowed down after 5 retries per remote by doubling the wait
-time after each unsuccessful attempt. The optional argument
-.B max
-specifies the maximum value of wait time in seconds at which it gets
-capped (default=300).
-.\"*********************************************************
-.TP
-.B \-\-connect\-retry\-max n
-.B n
-specifies the number of times each
-.B \-\-remote
-or
-.B <connection>
-entry is tried. Specifying
-.B n
-as one would try each entry exactly once. A successful connection
-resets the counter. (default=unlimited).
-.\"*********************************************************
-.TP
-.B \-\-show\-proxy\-settings
-Show sensed HTTP or SOCKS proxy settings. Currently, only Windows clients
-support this option.
-.\"*********************************************************
-.TP
-.B \-\-http\-proxy server port [authfile|'auto'|'auto\-nct'] [auth\-method]
-Connect to remote host through an HTTP proxy at address
-.B server
-and port
-.B port.
-If HTTP Proxy\-Authenticate is required,
-.B authfile
-is a file containing a username and password on 2 lines, or
-"stdin" to prompt from console. Its content can also be specified
-in the config file with the
-.B \-\-http\-proxy\-user\-pass
-option. (See section on inline files)
-
-.B auth\-method
-should be one of "none", "basic", or "ntlm".
-
-HTTP Digest authentication is supported as well, but only via
-the
-.B auto
-or
-.B auto\-nct
-flags (below).
-
-The
-.B auto
-flag causes OpenVPN to automatically determine the
-.B auth\-method
-and query stdin or the management interface for
-username/password credentials, if required. This flag
-exists on OpenVPN 2.1 or higher.
-
-The
-.B auto\-nct
-flag (no clear\-text auth) instructs OpenVPN to automatically
-determine the authentication method, but to reject weak
-authentication protocols such as HTTP Basic Authentication.
-.\"*********************************************************
-.TP
-.B \-\-http\-proxy\-option type [parm]
-Set extended HTTP proxy options.
-Repeat to set multiple options.
-
-.B VERSION version \-\-
-Set HTTP version number to
-.B version
-(default=1.0).
-
-.B AGENT user\-agent \-\-
-Set HTTP "User\-Agent" string to
-.B user\-agent.
-
-.B CUSTOM\-HEADER name content \-\-
-Adds the custom Header with
-.B name
-as name and
-.B content
-as the content of the custom HTTP header.
-.\"*********************************************************
-.TP
-.B \-\-socks\-proxy server [port] [authfile]
-Connect to remote host through a Socks5 proxy at address
-.B server
-and port
-.B port
-(default=1080).
-.B authfile
-(optional) is a file containing a username and password on 2 lines, or
-"stdin" to prompt from console.
-.\"*********************************************************
-.TP
-.B \-\-resolv\-retry n
-If hostname resolve fails for
-.B \-\-remote,
-retry resolve for
-.B n
-seconds before failing.
-
-Set
-.B n
-to "infinite" to retry indefinitely.
-
-By default,
-.B \-\-resolv\-retry infinite
-is enabled. You can disable by setting n=0.
-.\"*********************************************************
-.TP
-.B \-\-float
-Allow remote peer to change its IP address and/or port number, such as due to
-DHCP (this is the default if
-.B \-\-remote
-is not used).
-.B \-\-float
-when specified with
-.B \-\-remote
-allows an OpenVPN session to initially connect to a peer
-at a known address, however if packets arrive from a new
-address and pass all authentication tests, the new address
-will take control of the session. This is useful when
-you are connecting to a peer which holds a dynamic address
-such as a dial\-in user or DHCP client.
-
-Essentially,
-.B \-\-float
-tells OpenVPN to accept authenticated packets
-from any address, not only the address which was specified in the
-.B \-\-remote
-option.
-.\"*********************************************************
-.TP
-.B \-\-ipchange cmd
-Run command
-.B cmd
-when our remote ip\-address is initially authenticated or
-changes.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-When
-.B cmd
-is executed two arguments are appended after any arguments specified in
-.B cmd
-, as follows:
-
-.B cmd ip_address port_number
-
-Don't use
-.B \-\-ipchange
-in
-.B \-\-mode server
-mode. Use a
-.B \-\-client\-connect
-script instead.
-
-See the "Environmental Variables" section below for
-additional parameters passed as environmental variables.
-
-If you are running in a dynamic IP address environment where
-the IP addresses of either peer could change without notice,
-you can use this script, for example, to edit the
-.I /etc/hosts
-file with the current address of the peer. The script will
-be run every time the remote peer changes its IP address.
-
-Similarly if
-.I our
-IP address changes due to DHCP, we should configure
-our IP address change script (see man page for
-.BR dhcpcd (8)
-) to deliver a
-.B SIGHUP
-or
-.B SIGUSR1
-signal to OpenVPN. OpenVPN will then
-reestablish a connection with its most recently authenticated
-peer on its new IP address.
-.\"*********************************************************
-.TP
-.B \-\-port port
-TCP/UDP port number or port name for both local and remote (sets both
-.B \-\-lport
-and
-.B \-\-rport
-options to given port). The current
-default of 1194 represents the official IANA port number
-assignment for OpenVPN and has been used since version 2.0\-beta17.
-Previous versions used port 5000 as the default.
-.\"*********************************************************
-.TP
-.B \-\-lport port
-Set local TCP/UDP port number or name. Cannot be used together with
-.B \-\-nobind
-option.
-.\"*********************************************************
-.TP
-.B \-\-rport port
-Set TCP/UDP port number or name used by the
-.B \-\-remote
-option. The port can also be set directly using the
-.B \-\-remote
-option.
-.\"*********************************************************
-.TP
-.B \-\-bind [ipv6only]
-Bind to local address and port. This is the default unless any of
-.B \-\-proto tcp\-client
-,
-.B \-\-http\-proxy
-or
-.B \-\-socks\-proxy
-are used.
-
-If the
-.B ipv6only
-keyword is present OpenVPN will bind only to IPv6 (as oposed
-to IPv6 and IPv4) when a IPv6 socket is opened.
-
-.\"*********************************************************
-.TP
-.B \-\-nobind
-Do not bind to local address and port. The IP stack will allocate
-a dynamic port for returning packets. Since the value of the dynamic port
-could not be known in advance by a peer, this option is only suitable for
-peers which will be initiating connections by using the
-.B \-\-remote
-option.
-.\"*********************************************************
-.TP
-.B \-\-dev tunX | tapX | null
-TUN/TAP virtual network device (
-.B X
-can be omitted for a dynamic device.)
-
-See examples section below
-for an example on setting up a TUN device.
-
-You must use either tun devices on both ends of the connection
-or tap devices on both ends. You cannot mix them, as they
-represent different underlying network layers.
-
-.B tun
-devices encapsulate IPv4 or IPv6 (OSI Layer 3) while
-.B tap
-devices encapsulate Ethernet 802.3 (OSI Layer 2).
-.\"*********************************************************
-.TP
-.B \-\-dev\-type device\-type
-Which device type are we using?
-.B device\-type
-should be
-.B tun
-(OSI Layer 3)
-or
-.B tap
-(OSI Layer 2).
-Use this option only if the TUN/TAP device used with
-.B \-\-dev
-does not begin with
-.B tun
-or
-.B tap.
-.\"*********************************************************
-.TP
-.B \-\-topology mode
-Configure virtual addressing topology when running in
-.B \-\-dev tun
-mode. This directive has no meaning in
-.B \-\-dev tap
-mode, which always uses a
-.B subnet
-topology.
-
-If you set this directive on the server, the
-.B \-\-server
-and
-.B \-\-server\-bridge
-directives will automatically push your chosen topology setting to clients
-as well. This directive can also be manually pushed to clients. Like the
-.B \-\-dev
-directive, this directive must always be compatible between client and server.
-
-.B mode
-can be one of:
-
-.B net30 \-\-
-Use a point\-to\-point topology, by allocating one /30 subnet per client.
-This is designed to allow point\-to\-point semantics when some
-or all of the connecting clients might be Windows systems. This is the
-default on OpenVPN 2.0.
-
-.B p2p \-\-
-Use a point\-to\-point topology where the remote endpoint of the client's
-tun interface always points to the local endpoint of the server's tun interface.
-This mode allocates a single IP address per connecting client.
-Only use
-when none of the connecting clients are Windows systems. This mode
-is functionally equivalent to the
-.B \-\-ifconfig\-pool\-linear
-directive which is available in OpenVPN 2.0, is deprecated and will be
-removed in OpenVPN 2.5
-
-.B subnet \-\-
-Use a subnet rather than a point\-to\-point topology by
-configuring the tun interface with a local IP address and subnet mask,
-similar to the topology used in
-.B \-\-dev tap
-and ethernet bridging mode.
-This mode allocates a single IP address per connecting client and works on
-Windows as well. Only available when server and clients are OpenVPN 2.1 or
-higher, or OpenVPN 2.0.x which has been manually patched with the
-.B \-\-topology
-directive code. When used on Windows, requires version 8.2 or higher
-of the TAP\-Win32 driver. When used on *nix, requires that the tun
-driver supports an
-.BR ifconfig (8)
-command which sets a subnet instead of a remote endpoint IP address.
-
-This option exists in OpenVPN 2.1 or higher.
-
-Note: Using
-.B \-\-topology subnet
-changes the interpretation of the arguments of
-.B \-\-ifconfig
-to mean "address netmask", no longer "local remote".
-.\"*********************************************************
-.TP
-.B \-\-dev\-node node
-Explicitly set the device node rather than using
-/dev/net/tun, /dev/tun, /dev/tap, etc. If OpenVPN
-cannot figure out whether
-.B node
-is a TUN or TAP device based on the name, you should
-also specify
-.B \-\-dev\-type tun
-or
-.B \-\-dev\-type tap.
-
-Under Mac OS X this option can be used to specify the default tun
-implementation. Using
-.B \-\-dev\-node utun
-forces usage of the native Darwin tun kernel support. Use
-.B \-\-dev\-node utunN
-to select a specific utun instance. To force using the tun.kext (/dev/tunX) use
-.B \-\-dev\-node tun\fR.
-When not specifying a
-.B \-\-dev\-node
-option openvpn will first try to open utun, and fall back to tun.kext.
-
-On Windows systems, select the TAP\-Win32 adapter which
-is named
-.B node
-in the Network Connections Control Panel or the
-raw GUID of the adapter enclosed by braces.
-The
-.B \-\-show\-adapters
-option under Windows can also be used
-to enumerate all available TAP\-Win32
-adapters and will show both the network
-connections control panel name and the GUID for
-each TAP\-Win32 adapter.
-.TP
-.B \-\-lladdr address
-Specify the link layer address, more commonly known as the MAC address.
-Only applied to TAP devices.
-.\"*********************************************************
-.TP
-.B \-\-iproute cmd
-Set alternate command to execute instead of default iproute2 command.
+.UNINDENT
+.UNINDENT
+.sp
+When one of options \fBopt1 ... optN\fP is encountered in the configuration
+file the configuration file parsing does not fail if this OpenVPN version
+does not support the option. Multiple \fB\-\-ignore\-unknown\-option\fP options
+can be given to support a larger number of options to ignore.
+.sp
+This option should be used with caution, as there are good security
+reasons for having OpenVPN fail if it detects problems in a config file.
+Having said that, there are valid reasons for wanting new software
+features to gracefully degrade when encountered by older software
+versions.
+.sp
+\fB\-\-ignore\-unknown\-option\fP is available since OpenVPN 2.3.3.
+.TP
+.BI \-\-iproute \ cmd
+Set alternate command to execute instead of default \fBiproute2\fP command.
May be used in order to execute OpenVPN in unprivileged environment.
-.\"*********************************************************
-.TP
-.B \-\-ifconfig l rn
-Set TUN/TAP adapter parameters.
-.B l
-is the IP address of the local VPN endpoint.
-For TUN devices in point\-to\-point mode,
-.B rn
-is the IP address of the remote VPN endpoint.
-For TAP devices, or TUN devices used with
-.B \-\-topology subnet,
-.B rn
-is the subnet mask of the virtual network segment
-which is being created or connected to.
-
-For TUN devices, which facilitate virtual
-point\-to\-point IP connections (when used in
-.B \-\-topology net30
-or
-.B p2p
-mode),
-the proper usage of
-.B \-\-ifconfig
-is to use two private IP addresses
-which are not a member of any
-existing subnet which is in use.
-The IP addresses may be consecutive
-and should have their order reversed
-on the remote peer. After the VPN
-is established, by pinging
-.B rn,
-you will be pinging across the VPN.
-
-For TAP devices, which provide
-the ability to create virtual
-ethernet segments, or TUN devices in
-.B \-\-topology subnet
-mode (which create virtual "multipoint networks"),
-.B \-\-ifconfig
-is used to set an IP address and
-subnet mask just as a physical
-ethernet adapter would be
-similarly configured. If you are
-attempting to connect to a remote
-ethernet bridge, the IP address
-and subnet should be set to values
-which would be valid on the
-the bridged ethernet segment (note
-also that DHCP can be used for the
-same purpose).
-
-This option, while primarily a proxy for the
-.BR ifconfig (8)
-command, is designed to simplify TUN/TAP
-tunnel configuration by providing a
-standard interface to the different
-ifconfig implementations on different
-platforms.
-
-.B \-\-ifconfig
-parameters which are IP addresses can
-also be specified as a DNS or /etc/hosts
-file resolvable name.
-
-For TAP devices,
-.B \-\-ifconfig
-should not be used if the TAP interface will be
-getting an IP address lease from a DHCP
-server.
-.\"*********************************************************
.TP
-.B \-\-ifconfig\-noexec
-Don't actually execute ifconfig/netsh commands, instead
-pass
-.B \-\-ifconfig
-parameters to scripts using environmental variables.
-.\"*********************************************************
-.TP
-.B \-\-ifconfig\-nowarn
-Don't output an options consistency check warning
-if the
-.B \-\-ifconfig
-option on this side of the
-connection doesn't match the remote side. This is useful
-when you want to retain the overall benefits of the
-options consistency check (also see
-.B \-\-disable\-occ
-option) while only disabling the ifconfig component of
-the check.
-
-For example,
-if you have a configuration where the local host uses
-.B \-\-ifconfig
-but the remote host does not, use
-.B \-\-ifconfig\-nowarn
-on the local host.
-
-This option will also silence warnings about potential
-address conflicts which occasionally annoy more experienced
-users by triggering "false positive" warnings.
-.\"*********************************************************
-.TP
-.B \-\-route network/IP [netmask] [gateway] [metric]
-Add route to routing table after connection is established.
-Multiple routes can be specified. Routes will be
-automatically torn down in reverse order prior to
-TUN/TAP device close.
-
-This option is intended as
-a convenience proxy for the
-.BR route (8)
-shell command,
-while at the same time providing portable semantics
-across OpenVPN's platform space.
-
-.B netmask
-default \-\- 255.255.255.255
-
-.B gateway
-default \-\- taken from
-.B \-\-route\-gateway
-or the second parameter to
-.B \-\-ifconfig
-when
-.B \-\-dev tun
-is specified.
-
-.B metric
-default \-\- taken from
-.B \-\-route\-metric
-otherwise 0.
-
-The default can be specified by leaving an option blank or setting
-it to "default".
-
-The
-.B network
-and
-.B gateway
-parameters can
-also be specified as a DNS or /etc/hosts
-file resolvable name, or as one of three special keywords:
-
-.B vpn_gateway
-\-\- The remote VPN endpoint address
-(derived either from
-.B \-\-route\-gateway
-or the second parameter to
-.B \-\-ifconfig
-when
-.B \-\-dev tun
-is specified).
-
-.B net_gateway
-\-\- The pre\-existing IP default gateway, read from the routing
-table (not supported on all OSes).
-
-.B remote_host
-\-\- The
-.B \-\-remote
-address if OpenVPN is being run in client mode, and is undefined in server mode.
-.\"*********************************************************
-.TP
-.B \-\-route\-gateway gw|'dhcp'
-Specify a default gateway
-.B gw
-for use with
-.B \-\-route.
-
-If
-.B dhcp
-is specified as the parameter,
-the gateway address will be extracted from a DHCP
-negotiation with the OpenVPN server\-side LAN.
-.\"*********************************************************
-.TP
-.B \-\-route\-metric m
-Specify a default metric
-.B m
-for use with
-.B \-\-route.
-.\"*********************************************************
-.TP
-.B \-\-route\-delay [n] [w]
-Delay
-.B n
-seconds (default=0) after connection
-establishment, before adding routes. If
-.B n
-is 0, routes will be added immediately upon connection
-establishment. If
-.B \-\-route\-delay
-is omitted, routes will be added immediately after TUN/TAP device
-open and
-.B \-\-up
-script execution, before any
-.B \-\-user
-or
-.B \-\-group
-privilege downgrade (or
-.B \-\-chroot
-execution.)
-
-This option is designed to be useful in scenarios where DHCP is
-used to set
-tap adapter addresses. The delay will give the DHCP handshake
-time to complete before routes are added.
-
-On Windows,
-.B \-\-route\-delay
-tries to be more intelligent by waiting
-.B w
-seconds (w=30 by default)
-for the TAP\-Win32 adapter to come up before adding routes.
-.\"*********************************************************
-.TP
-.B \-\-route\-up cmd
-Run command
-.B cmd
-after routes are added, subject to
-.B \-\-route\-delay.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-See the "Environmental Variables" section below for
-additional parameters passed as environmental variables.
-.\"*********************************************************
-.TP
-.B \-\-route\-pre\-down cmd
-Run command
-.B cmd
-before routes are removed upon disconnection.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-See the "Environmental Variables" section below for
-additional parameters passed as environmental variables.
-.\"*********************************************************
-.TP
-.B \-\-route\-noexec
-Don't add or remove routes automatically. Instead pass routes to
-.B \-\-route\-up
-script using environmental variables.
-.\"*********************************************************
-.TP
-.B \-\-route\-nopull
-When used with
-.B \-\-client
-or
-.B \-\-pull,
-accept options pushed by server EXCEPT for routes, block\-outside\-dns and dhcp
-options like DNS servers.
-
-When used on the client, this option effectively bars the
-server from adding routes to the client's routing table,
-however note that this option still allows the server
-to set the TCP/IP properties of the client's TUN/TAP interface.
-.\"*********************************************************
+.BI \-\-keying\-material\-exporter \ args
+Save Exported Keying Material [RFC5705] of len bytes (must be between 16
+and 4095 bytes) using \fBlabel\fP in environment
+(\fBexported_keying_material\fP) for use by plugins in
+\fBOPENVPN_PLUGIN_TLS_FINAL\fP callback.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+keying\-material\-exporter label len
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Note that exporter \fBlabels\fP have the potential to collide with existing
+PRF labels. In order to prevent this, labels \fIMUST\fP begin with
+\fBEXPORTER\fP\&.
.TP
-.B \-\-allow\-pull\-fqdn
-Allow client to pull DNS names from server (rather than being limited
-to IP address) for
-.B \-\-ifconfig,
-.B \-\-route,
-and
-.B \-\-route\-gateway.
-.\"*********************************************************
-.TP
-.B \-\-client\-nat snat|dnat network netmask alias
-This pushable client option sets up a stateless one\-to\-one NAT
-rule on packet addresses (not ports), and is useful in cases
-where routes or ifconfig settings pushed to the client would
-create an IP numbering conflict.
-
-.B network/netmask
-(for example 192.168.0.0/255.255.0.0)
-defines the local view of a resource from the client perspective, while
-.B alias/netmask
-(for example 10.64.0.0/255.255.0.0)
-defines the remote view from the server perspective.
-
-Use
-.B snat
-(source NAT) for resources owned by the client and
-.B dnat
-(destination NAT) for remote resources.
-
-Set
-.B \-\-verb 6
-for debugging info showing the transformation of src/dest
-addresses in packets.
-.\"*********************************************************
+.B \-\-mlock
+Disable paging by calling the POSIX mlockall function. Requires that
+OpenVPN be initially run as root (though OpenVPN can subsequently
+downgrade its UID using the \fB\-\-user\fP option).
+.sp
+Using this option ensures that key material and tunnel data are never
+written to disk due to virtual memory paging operations which occur
+under most modern operating systems. It ensures that even if an attacker
+was able to crack the box running OpenVPN, he would not be able to scan
+the system swap file to recover previously used ephemeral keys, which
+are used for a period of time governed by the \fB\-\-reneg\fP options (see
+below), then are discarded.
+.sp
+The downside of using \fB\-\-mlock\fP is that it will reduce the amount of
+physical memory available to other applications.
+.sp
+The limit on how much memory can be locked and how that limit
+is enforced are OS\-dependent. On Linux the default limit that an
+unprivileged process may lock (RLIMIT_MEMLOCK) is low, and if
+privileges are dropped later, future memory allocations will very
+likely fail. The limit can be increased using ulimit or systemd
+directives depending on how OpenVPN is started.
+.TP
+.BI \-\-nice \ n
+Change process priority after initialization (\fBn\fP greater than 0 is
+lower priority, \fBn\fP less than zero is higher priority).
.TP
-.B \-\-redirect\-gateway flags...
-Automatically execute routing commands to cause all outgoing IP traffic
-to be redirected over the VPN. This is a client\-side option.
-
-This option performs three steps:
-
-.B (1)
-Create a static route for the
-.B \-\-remote
-address which forwards to the pre\-existing default gateway.
-This is done so that
-.B (3)
-will not create a routing loop.
-
-.B (2)
-Delete the default gateway route.
-
-.B (3)
-Set the new default gateway to be the VPN endpoint address (derived either from
-.B \-\-route\-gateway
-or the second parameter to
-.B \-\-ifconfig
-when
-.B \-\-dev tun
-is specified).
-
-When the tunnel is torn down, all of the above steps are reversed so
-that the original default route is restored.
-
-Option flags:
-
-.B local \-\-
-Add the
-.B local
-flag if both OpenVPN peers are directly connected via a common subnet,
-such as with wireless. The
-.B local
-flag will cause step
-.B 1
-above to be omitted.
-
-.B autolocal \-\-
-Try to automatically determine whether to enable
-.B local
-flag above.
-
-.B def1 \-\-
-Use this flag to override
-the default gateway by using 0.0.0.0/1 and 128.0.0.0/1
-rather than 0.0.0.0/0. This has the benefit of overriding
-but not wiping out the original default gateway.
-
-.B bypass\-dhcp \-\-
-Add a direct route to the DHCP server (if it is non\-local) which
-bypasses the tunnel
-(Available on Windows clients, may not be available
-on non\-Windows clients).
-
-.B bypass\-dns \-\-
-Add a direct route to the DNS server(s) (if they are non\-local) which
-bypasses the tunnel
-(Available on Windows clients, may not be available
-on non\-Windows clients).
-
-.B block\-local \-\-
-Block access to local LAN when the tunnel is active, except for
-the LAN gateway itself. This is accomplished by routing the local
-LAN (except for the LAN gateway address) into the tunnel.
-
-.B ipv6 \-\-
-Redirect IPv6 routing into the tunnel. This works similar to the
-.B def1
-flag, that is, more specific IPv6 routes are added (2000::/4, 3000::/4),
-covering the whole IPv6 unicast space.
-
-.B !ipv4 \-\-
-Do not redirect IPv4 traffic \- typically used in the flag pair
-.B "ipv6 !ipv4"
-to redirect IPv6\-only.
-.\"*********************************************************
-.TP
-.B \-\-link\-mtu n
-Sets an upper bound on the size of UDP packets which are sent
-between OpenVPN peers. It's best not to set this parameter unless
-you know what you're doing.
-.\"*********************************************************
-.\"*********************************************************
-.TP
-.B \-\-redirect\-private [flags]
-Like \-\-redirect\-gateway, but omit actually changing the default
-gateway. Useful when pushing private subnets.
-.\"*********************************************************
-.TP
-.B \-\-tun\-mtu n
-Take the TUN device MTU to be
-.B n
-and derive the link MTU
-from it (default=1500). In most cases, you will probably want to
-leave this parameter set to its default value.
-
-The MTU (Maximum Transmission Units) is
-the maximum datagram size in bytes that can be sent unfragmented
-over a particular network path. OpenVPN requires that packets
-on the control or data channels be sent unfragmented.
-
-MTU problems often manifest themselves as connections which
-hang during periods of active usage.
-
-It's best to use the
-.B \-\-fragment
-and/or
-.B \-\-mssfix
-options to deal with MTU sizing issues.
-.\"*********************************************************
-.TP
-.B \-\-tun\-mtu\-extra n
-Assume that the TUN/TAP device might return as many as
-.B n
-bytes more than the
-.B \-\-tun\-mtu
-size on read. This parameter defaults to 0, which is sufficient for
-most TUN devices. TAP devices may introduce additional overhead in excess
-of the MTU size, and a setting of 32 is the default when TAP devices are used.
-This parameter only controls internal OpenVPN buffer sizing,
-so there is no transmission overhead associated with using a larger value.
-.\"*********************************************************
-.TP
-.B \-\-mtu\-disc type
-Should we do Path MTU discovery on TCP/UDP channel? Only supported on OSes such
-as Linux that supports the necessary system call to set.
-
-.B 'no'
-\-\- Never send DF (Don't Fragment) frames
-.br
-.B 'maybe'
-\-\- Use per\-route hints
-.br
-.B 'yes'
-\-\- Always DF (Don't Fragment)
-.br
-.\"*********************************************************
+.B \-\-persist\-key
+Don\(aqt re\-read key files across \fBSIGUSR1\fP or \fB\-\-ping\-restart\fP\&.
+.sp
+This option can be combined with \fB\-\-user nobody\fP to allow restarts
+triggered by the \fBSIGUSR1\fP signal. Normally if you drop root
+privileges in OpenVPN, the daemon cannot be restarted since it will now
+be unable to re\-read protected key files.
+.sp
+This option solves the problem by persisting keys across \fBSIGUSR1\fP
+resets, so they don\(aqt need to be re\-read.
+.TP
+.BI \-\-remap\-usr1 \ signal
+Control whether internally or externally generated \fBSIGUSR1\fP signals
+are remapped to \fBSIGHUP\fP (restart without persisting state) or
+SIGTERM (exit).
+.sp
+\fBsignal\fP can be set to \fBSIGHUP\fP or \fBSIGTERM\fP\&. By default,
+no remapping occurs.
.TP
-.B \-\-mtu\-test
-To empirically measure MTU on connection startup,
-add the
-.B \-\-mtu\-test
-option to your configuration.
-OpenVPN will send ping packets of various sizes
-to the remote peer and measure the largest packets
-which were successfully received. The
-.B \-\-mtu\-test
-process normally takes about 3 minutes to complete.
-.\"*********************************************************
-.TP
-.B \-\-fragment max
-Enable internal datagram fragmentation so
-that no UDP datagrams are sent which
-are larger than
-.B max
-bytes.
-
-The
-.B max
-parameter is interpreted in the same way as the
-.B \-\-link\-mtu
-parameter, i.e. the UDP packet size after encapsulation
-overhead has been added in, but not including
-the UDP header itself.
-
-The
-.B \-\-fragment
-option only makes sense when you are using the UDP protocol (
-.B \-\-proto udp
-).
-
-.B \-\-fragment
-adds 4 bytes of overhead per datagram.
-
-See the
-.B \-\-mssfix
-option below for an important related option to
-.B \-\-fragment.
-
-It should also be noted that this option is not meant to replace
-UDP fragmentation at the IP stack level. It is only meant as a
-last resort when path MTU discovery is broken. Using this option
-is less efficient than fixing path MTU discovery for your IP link and
-using native IP fragmentation instead.
-
-Having said that, there are circumstances where using OpenVPN's
-internal fragmentation capability may be your only option, such
-as tunneling a UDP multicast stream which requires fragmentation.
-.\"*********************************************************
+.BI \-\-script\-security \ level
+This directive offers policy\-level control over OpenVPN\(aqs usage of
+external programs and scripts. Lower \fBlevel\fP values are more
+restrictive, higher values are more permissive. Settings for \fBlevel\fP:
+.INDENT 7.0
.TP
-.B \-\-mssfix max
-Announce to TCP sessions running over the tunnel that they should limit
-their send packet sizes such that after OpenVPN has encapsulated them,
-the resulting UDP packet size that OpenVPN sends to its peer will not
-exceed
-.B max
-bytes. The default value is
-.B 1450.
-
-The
-.B max
-parameter is interpreted in the same way as the
-.B \-\-link\-mtu
-parameter, i.e. the UDP packet size after encapsulation
-overhead has been added in, but not including
-the UDP header itself. Resulting packet would be at most 28
-bytes larger for IPv4 and 48 bytes for IPv6 (20/40 bytes for IP
-header and 8 bytes for UDP header). Default value of 1450 allows
-IPv4 packets to be transmitted over a link with MTU 1473 or higher
-without IP level fragmentation.
-
-The
-.B \-\-mssfix
-option only makes sense when you are using the UDP protocol
-for OpenVPN peer\-to\-peer communication, i.e.
-.B \-\-proto udp.
-
-.B \-\-mssfix
-and
-.B \-\-fragment
-can be ideally used together, where
-.B \-\-mssfix
-will try to keep TCP from needing
-packet fragmentation in the first place,
-and if big packets come through anyhow
-(from protocols other than TCP),
-.B \-\-fragment
-will internally fragment them.
-
-Both
-.B \-\-fragment
-and
-.B \-\-mssfix
-are designed to work around cases where Path MTU discovery
-is broken on the network path between OpenVPN peers.
-
-The usual symptom of such a breakdown is an OpenVPN
-connection which successfully starts, but then stalls
-during active usage.
-
-If
-.B \-\-fragment
-and
-.B \-\-mssfix
-are used together,
-.B \-\-mssfix
-will take its default
-.B max
-parameter from the
-.B \-\-fragment max
-option.
-
-Therefore, one could lower the maximum UDP packet size
-to 1300 (a good first try for solving MTU\-related
-connection problems) with the following options:
-
-.B \-\-tun\-mtu 1500 \-\-fragment 1300 \-\-mssfix
-.\"*********************************************************
+.B \fB0\fP
+Strictly no calling of external programs.
.TP
-.B \-\-sndbuf size
-Set the TCP/UDP socket send buffer size.
-Defaults to operation system default.
-.\"*********************************************************
+.B \fB1\fP
+(Default) Only call built\-in executables such as ifconfig,
+ip, route, or netsh.
.TP
-.B \-\-rcvbuf size
-Set the TCP/UDP socket receive buffer size.
-Defaults to operation system default.
-.\"*********************************************************
+.B \fB2\fP
+Allow calling of built\-in executables and user\-defined
+scripts.
.TP
-.B \-\-mark value
-Mark encrypted packets being sent with value. The mark value can be
-matched in policy routing and packetfilter rules. This option is
-only supported in Linux and does nothing on other operating systems.
-.\"*********************************************************
-.TP
-.B \-\-socket\-flags flags...
-Apply the given flags to the OpenVPN transport socket.
-Currently, only
-.B TCP_NODELAY
-is supported.
-
-The
-.B TCP_NODELAY
-socket flag is useful in TCP mode, and causes the kernel
-to send tunnel packets immediately over the TCP connection without
-trying to group several smaller packets into a larger packet.
-This can result in a considerably improvement in latency.
-
-This option is pushable from server to client, and should be used
-on both client and server for maximum effect.
-.\"*********************************************************
-.TP
-.B \-\-txqueuelen n
-(Linux only) Set the TX queue length on the TUN/TAP interface.
-Currently defaults to 100.
-.\"*********************************************************
-.TP
-.B \-\-shaper n
-Limit bandwidth of outgoing tunnel data to
-.B n
-bytes per second on the TCP/UDP port.
-Note that this will only work if mode is set to p2p.
-If you want to limit the bandwidth
-in both directions, use this option on both peers.
-
-OpenVPN uses the following algorithm to implement
-traffic shaping: Given a shaper rate of
-.I n
-bytes per second, after a datagram write of
-.I b
-bytes is queued on the TCP/UDP port, wait a minimum of
-.I (b / n)
-seconds before queuing the next write.
-
-It should be noted that OpenVPN supports multiple
-tunnels between the same two peers, allowing you
-to construct full\-speed and reduced bandwidth tunnels
-at the same time,
-routing low\-priority data such as off\-site backups
-over the reduced bandwidth tunnel, and other data
-over the full\-speed tunnel.
-
-Also note that for low bandwidth tunnels
-(under 1000 bytes per second), you should probably
-use lower MTU values as well (see above), otherwise
-the packet latency will grow so large as to trigger
-timeouts in the TLS layer and TCP connections running
-over the tunnel.
-
-OpenVPN allows
-.B n
-to be between 100 bytes/sec and 100 Mbytes/sec.
-.\"*********************************************************
-.TP
-.B \-\-inactive n [bytes]
-Causes OpenVPN to exit after
-.B n
-seconds of inactivity on the TUN/TAP device. The time length of
-inactivity is measured since the last incoming or outgoing tunnel
-packet. The default value is 0 seconds, which disables this feature.
-
-If the optional
-.B bytes
-parameter is included,
-exit if less than
-.B bytes
-of combined in/out traffic are produced on the tun/tap device
-in
-.B n
-seconds.
-
-In any case, OpenVPN's internal ping packets (which are just
-keepalives) and TLS control packets are not considered
-"activity", nor are they counted as traffic, as they are used
-internally by OpenVPN and are not an indication of actual user
-activity.
-.\"*********************************************************
-.TP
-.B \-\-ping n
-Ping remote over the TCP/UDP control channel
-if no packets have been sent for at least
-.B n
-seconds (specify
-.B \-\-ping
-on both peers to cause ping packets to be sent in both directions since
-OpenVPN ping packets are not echoed like IP ping packets).
-When used in one of OpenVPN's secure modes (where
-.B \-\-secret, \-\-tls\-server,
-or
-.B \-\-tls\-client
-is specified), the ping packet
-will be cryptographically secure.
-
-This option has two intended uses:
-
-(1) Compatibility
-with stateful firewalls. The periodic ping will ensure that
-a stateful firewall rule which allows OpenVPN UDP packets to
-pass will not time out.
-
-(2) To provide a basis for the remote to test the existence
-of its peer using the
-.B \-\-ping\-exit
-option.
-.\"*********************************************************
-.TP
-.B \-\-ping\-exit n
-Causes OpenVPN to exit after
-.B n
-seconds pass without reception of a ping
-or other packet from remote.
-This option can be combined with
-.B \-\-inactive, \-\-ping,
-and
-.B \-\-ping\-exit
-to create a two\-tiered inactivity disconnect.
-
-For example,
-
-.B openvpn [options...] \-\-inactive 3600 \-\-ping 10 \-\-ping\-exit 60
-
-when used on both peers will cause OpenVPN to exit within 60
-seconds if its peer disconnects, but will exit after one
-hour if no actual tunnel data is exchanged.
-.\"*********************************************************
-.TP
-.B \-\-ping\-restart n
-Similar to
-.B \-\-ping\-exit,
-but trigger a
-.B SIGUSR1
-restart after
-.B n
-seconds pass without reception of a ping
-or other packet from remote.
-
-This option is useful in cases
-where the remote peer has a dynamic IP address and
-a low\-TTL DNS name is used to track the IP address using
-a service such as
-.I http://dyndns.org/
-+ a dynamic DNS client such
-as
-.B ddclient.
-
-If the peer cannot be reached, a restart will be triggered, causing
-the hostname used with
-.B \-\-remote
-to be re\-resolved (if
-.B \-\-resolv\-retry
-is also specified).
-
-In server mode,
-.B \-\-ping\-restart, \-\-inactive,
-or any other type of internally generated signal will always be
-applied to
-individual client instance objects, never to whole server itself.
-Note also in server mode that any internally generated signal
-which would normally cause a restart, will cause the deletion
-of the client instance object instead.
-
-In client mode, the
-.B \-\-ping\-restart
-parameter is set to 120 seconds by default. This default will
-hold until the client pulls a replacement value from the server, based on
-the
-.B \-\-keepalive
-setting in the server configuration.
-To disable the 120 second default, set
-.B \-\-ping\-restart 0
-on the client.
-
-See the signals section below for more information
-on
-.B SIGUSR1.
-
-Note that the behavior of
-.B SIGUSR1
-can be modified by the
-.B \-\-persist\-tun, \-\-persist\-key, \-\-persist\-local\-ip,
-and
-.B \-\-persist\-remote\-ip
-options.
-
-Also note that
-.B \-\-ping\-exit
-and
-.B \-\-ping\-restart
-are mutually exclusive and cannot be used together.
-.\"*********************************************************
-.TP
-.B \-\-keepalive interval timeout
-A helper directive designed to simplify the expression of
-.B \-\-ping
-and
-.B \-\-ping\-restart.
-
-This option can be used on both client and server side, but it is
-enough to add this on the server side as it will push appropriate
-.B \-\-ping
-and
-.B \-\-ping\-restart
-options to the client. If used on both server and client,
-the values pushed from server will override the client local values.
-
-The
-.B timeout
-argument will be twice as long on the server side. This ensures that
-a timeout is detected on client side before the server side drops
-the connection.
-
-For example,
-.B \-\-keepalive 10 60
-expands as follows:
-
+.B \fB3\fP
+Allow passwords to be passed to scripts via environmental
+variables (potentially unsafe).
+.UNINDENT
+.sp
+OpenVPN releases before v2.3 also supported a \fBmethod\fP flag which
+indicated how OpenVPN should call external commands and scripts. This
+could be either \fBexecve\fP or \fBsystem\fP\&. As of OpenVPN 2.3, this
+flag is no longer accepted. In most *nix environments the execve()
+approach has been used without any issues.
+.sp
+Some directives such as \fB\-\-up\fP allow options to be passed to the
+external script. In these cases make sure the script name does not
+contain any spaces or the configuration parser will choke because it
+can\(aqt determine where the script name ends and script options start.
+.sp
+To run scripts in Windows in earlier OpenVPN versions you needed to
+either add a full path to the script interpreter which can parse the
+script or use the \fBsystem\fP flag to run these scripts. As of OpenVPN
+2.3 it is now a strict requirement to have full path to the script
+interpreter when running non\-executables files. This is not needed for
+executable files, such as .exe, .com, .bat or .cmd files. For example,
+if you have a Visual Basic script, you must use this syntax now:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
- if mode server:
- ping 10 # Argument: interval
- ping\-restart 120 # Argument: timeout*2
- push "ping 10" # Argument: interval
- push "ping\-restart 60" # Argument: timeout
- else
- ping 10 # Argument: interval
- ping\-restart 60 # Argument: timeout
-.in -4
-.ft
+.ft C
+\-\-up \(aqC:\e\eWindows\e\eSystem32\e\ewscript.exe C:\e\eProgram\e Files\e\eOpenVPN\e\econfig\e\emy\-up\-script.vbs\(aq
+.ft P
.fi
-.\"*********************************************************
-.TP
-.B \-\-ping\-timer\-rem
-Run the
-.B \-\-ping\-exit
-/
-.B \-\-ping\-restart
-timer only if we have a remote address. Use this option if you are
-starting the daemon in listen mode (i.e. without an explicit
-.B \-\-remote
-peer), and you don't want to start clocking timeouts until a remote
-peer connects.
-.\"*********************************************************
-.TP
-.B \-\-persist\-tun
-Don't close and reopen TUN/TAP device or run up/down scripts
-across
-.B SIGUSR1
-or
-.B \-\-ping\-restart
-restarts.
-
-.B SIGUSR1
-is a restart signal similar to
-.B SIGHUP,
-but which offers finer\-grained control over
-reset options.
-.\"*********************************************************
-.TP
-.B \-\-persist\-key
-Don't re\-read key files across
-.B SIGUSR1
-or
-.B \-\-ping\-restart.
-
-This option can be combined with
-.B \-\-user nobody
-to allow restarts triggered by the
-.B SIGUSR1
+.UNINDENT
+.UNINDENT
+.sp
+Please note the single quote marks and the escaping of the backslashes
+(\e) and the space character.
+.sp
+The reason the support for the \fBsystem\fP flag was removed is due to
+the security implications with shell expansions when executing scripts
+via the \fBsystem()\fP call.
+.TP
+.BI \-\-setcon \ context
+Apply SELinux \fBcontext\fP after initialization. This essentially
+provides the ability to restrict OpenVPN\(aqs rights to only network I/O
+operations, thanks to SELinux. This goes further than \fB\-\-user\fP and
+\fB\-\-chroot\fP in that those two, while being great security features,
+unfortunately do not protect against privilege escalation by
+exploitation of a vulnerable system call. You can of course combine all
+three, but please note that since setcon requires access to /proc you
+will have to provide it inside the chroot directory (e.g. with mount
+\-\-bind).
+.sp
+Since the setcon operation is delayed until after initialization,
+OpenVPN can be restricted to just network\-related system calls, whereas
+by applying the context before startup (such as the OpenVPN one provided
+in the SELinux Reference Policies) you will have to allow many things
+required only during initialization.
+.sp
+Like with chroot, complications can result when scripts or restarts are
+executed after the setcon operation, which is why you should really
+consider using the \fB\-\-persist\-key\fP and \fB\-\-persist\-tun\fP options.
+.TP
+.BI \-\-status \ args
+Write operational status to \fBfile\fP every \fBn\fP seconds.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+status file
+status file n
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Status can also be written to the syslog by sending a \fBSIGUSR2\fP
signal.
-Normally if you drop root privileges in OpenVPN,
-the daemon cannot be restarted since it will now be unable to re\-read protected
-key files.
-
-This option solves the problem by persisting keys across
-.B SIGUSR1
-resets, so they don't need to be re\-read.
-.\"*********************************************************
+.sp
+With multi\-client capability enabled on a server, the status file
+includes a list of clients and a routing table. The output format can be
+controlled by the \fB\-\-status\-version\fP option in that case.
+.sp
+For clients or instances running in point\-to\-point mode, it will contain
+the traffic statistics.
+.TP
+.BI \-\-status\-version \ n
+Set the status file format version number to \fBn\fP\&.
+.sp
+This only affects the status file on servers with multi\-client
+capability enabled. Valid status version values:
+.INDENT 7.0
+.TP
+.B \fB1\fP
+Traditional format (default). The client list contains the
+following fields comma\-separated: Common Name, Real Address, Bytes
+Received, Bytes Sent, Connected Since.
+.TP
+.B \fB2\fP
+A more reliable format for external processing. Compared to
+version \fB1\fP, the client list contains some additional fields:
+Virtual Address, Virtual IPv6 Address, Username, Client ID, Peer ID,
+Data Channel Cipher. Future versions may extend the number of fields.
+.TP
+.B \fB3\fP
+Identical to \fB2\fP, but fields are tab\-separated.
+.UNINDENT
.TP
-.B \-\-persist\-local\-ip
-Preserve initially resolved local IP address and port number
-across
-.B SIGUSR1
-or
-.B \-\-ping\-restart
-restarts.
-.\"*********************************************************
-.TP
-.B \-\-persist\-remote\-ip
-Preserve most recently authenticated remote IP address and port number
-across
-.B SIGUSR1
+.B \-\-test\-crypto
+Do a self\-test of OpenVPN\(aqs crypto options by encrypting and decrypting
+test packets using the data channel encryption options specified above.
+This option does not require a peer to function, and therefore can be
+specified without \fB\-\-dev\fP or \fB\-\-remote\fP\&.
+.sp
+The typical usage of \fB\-\-test\-crypto\fP would be something like this:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-test\-crypto \-\-secret key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
or
-.B \-\-ping\-restart
-restarts.
-.\"*********************************************************
-.TP
-.B \-\-mlock
-Disable paging by calling the POSIX mlockall function.
-Requires that OpenVPN be initially run as root (though
-OpenVPN can subsequently downgrade its UID using the
-.B \-\-user
-option).
-
-Using this option ensures that key material and tunnel
-data are never written to disk due to virtual
-memory paging operations which occur under most
-modern operating systems. It ensures that even if an
-attacker was able to crack the box running OpenVPN, he
-would not be able to scan the system swap file to
-recover previously used
-ephemeral keys, which are used for a period of time
-governed by the
-.B \-\-reneg
-options (see below), then are discarded.
-
-The downside
-of using
-.B \-\-mlock
-is that it will reduce the amount of physical
-memory available to other applications.
-.\"*********************************************************
-.TP
-.B \-\-up cmd
-Run command
-.B cmd
-after successful TUN/TAP device open
-(pre
-.B \-\-user
-UID change).
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-The up command is useful for specifying route
-commands which route IP traffic destined for
-private subnets which exist at the other
-end of the VPN connection into the tunnel.
-
-For
-.B \-\-dev tun
-execute as:
-
-.B cmd tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_remote_ip [ init | restart ]
-
-For
-.B \-\-dev tap
-execute as:
-
-.B cmd tap_dev tap_mtu link_mtu ifconfig_local_ip ifconfig_netmask [ init | restart ]
-
-See the "Environmental Variables" section below for
-additional parameters passed as environmental variables.
-
-Note that if
-.B cmd
-includes arguments, all OpenVPN\-generated arguments will be appended
-to them to build an argument list with which the executable will be
-called.
-
-Typically,
-.B cmd
-will run a script to add routes to the tunnel.
-
-Normally the up script is called after the TUN/TAP device is opened.
-In this context, the last command line parameter passed to the script
-will be
-.I init.
-If the
-.B \-\-up\-restart
-option is also used, the up script will be called for restarts as
-well. A restart is considered to be a partial reinitialization
-of OpenVPN where the TUN/TAP instance is preserved (the
-.B \-\-persist\-tun
-option will enable such preservation). A restart
-can be generated by a SIGUSR1 signal, a
-.B \-\-ping\-restart
-timeout, or a connection reset when the TCP protocol is enabled
-with the
-.B \-\-proto
-option. If a restart occurs, and
-.B \-\-up\-restart
-has been specified, the up script will be called with
-.I restart
-as the last parameter.
-
-NOTE: on restart, OpenVPN will not pass the full set of environment
-variables to the script. Namely, everything related to routing and
-gateways will not be passed, as nothing needs to be done anyway \- all
-the routing setup is already in place. Additionally, the up\-restart
-script will run with the downgraded UID/GID settings (if configured).
-
-The following standalone example shows how the
-.B \-\-up
-script can be called in both an initialization and restart context.
-(NOTE: for security reasons, don't run the following example unless UDP port
-9999 is blocked by your firewall. Also, the example will run indefinitely,
-so you should abort with control\-c).
-
-.B openvpn \-\-dev tun \-\-port 9999 \-\-verb 4 \-\-ping\-restart 10 \-\-up 'echo up' \-\-down 'echo down' \-\-persist\-tun \-\-up\-restart
-
-Note that OpenVPN also provides the
-.B \-\-ifconfig
-option to automatically ifconfig the TUN device,
-eliminating the need to define an
-.B \-\-up
-script, unless you also want to configure routes
-in the
-.B \-\-up
-script.
-
-If
-.B \-\-ifconfig
-is also specified, OpenVPN will pass the ifconfig local
-and remote endpoints on the command line to the
-.B \-\-up
-script so that they can be used to configure routes such as:
-
-.B route add \-net 10.0.0.0 netmask 255.255.255.0 gw $5
-.\"*********************************************************
-.TP
-.B \-\-up\-delay
-Delay TUN/TAP open and possible
-.B \-\-up
-script execution
-until after TCP/UDP connection establishment with peer.
-
-In
-.B \-\-proto udp
-mode, this option normally requires the use of
-.B \-\-ping
-to allow connection initiation to be sensed in the absence
-of tunnel data, since UDP is a "connectionless" protocol.
-
-On Windows, this option will delay the TAP\-Win32 media state
-transitioning to "connected" until connection establishment,
-i.e. the receipt of the first authenticated packet from the peer.
-.\"*********************************************************
-.TP
-.B \-\-down cmd
-Run command
-.B cmd
-after TUN/TAP device close
-(post
-.B \-\-user
-UID change and/or
-.B \-\-chroot
-).
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-Called with the same parameters and environmental
-variables as the
-.B \-\-up
-option above.
-
-Note that if you reduce privileges by using
-.B \-\-user
-and/or
-.B \-\-group,
-your
-.B \-\-down
-script will also run at reduced privilege.
-.\"*********************************************************
-.TP
-.B \-\-down\-pre
-Call
-.B \-\-down
-cmd/script before, rather than after, TUN/TAP close.
-.\"*********************************************************
-.TP
-.B \-\-up\-restart
-Enable the
-.B \-\-up
-and
-.B \-\-down
-scripts to be called for restarts as well as initial program start.
-This option is described more fully above in the
-.B \-\-up
-option documentation.
-.\"*********************************************************
-.TP
-.B \-\-setenv name value
-Set a custom environmental variable
-.B name=value
-to pass to script.
-.\"*********************************************************
-.TP
-.B \-\-setenv FORWARD_COMPATIBLE 1
-Relax config file syntax checking so that unknown directives
-will trigger a warning but not a fatal error,
-on the assumption that a given unknown directive might be valid
-in future OpenVPN versions.
-
-This option should be used with caution, as there are good security
-reasons for having OpenVPN fail if it detects problems in a
-config file. Having said that, there are valid reasons for wanting
-new software features to gracefully degrade when encountered by
-older software versions.
-
-It is also possible to tag a single directive so as not to trigger
-a fatal error if the directive isn't recognized. To do this,
-prepend the following before the directive:
-.B setenv opt
-
-Versions prior to OpenVPN 2.3.3 will always ignore options set with the
-.B setenv opt
-directive.
-
-See also
-.B \-\-ignore\-unknown\-option
-.\"*********************************************************
-.TP
-.B \-\-setenv\-safe name value
-Set a custom environmental variable
-.B OPENVPN_name=value
-to pass to script.
-
-This directive is designed to be pushed by the server to clients,
-and the prepending of "OPENVPN_" to the environmental variable
-is a safety precaution to prevent a LD_PRELOAD style attack
-from a malicious or compromised server.
-.\"*********************************************************
-.TP
-.B \-\-ignore\-unknown\-option opt1 opt2 opt3 ... optN
-When one of options
-.B opt1 ... optN
-is encountered in the configuration file the configuration
-file parsing does not fail if this OpenVPN version does not
-support the option. Multiple
-.B \-\-ignore\-unknown\-option
-options can be given to support a larger number of options to ignore.
-
-This option should be used with caution, as there are good security
-reasons for having OpenVPN fail if it detects problems in a
-config file. Having said that, there are valid reasons for wanting
-new software features to gracefully degrade when encountered by
-older software versions.
-
-.B \-\-ignore\-unknown\-option
-is available since OpenVPN 2.3.3.
-.\"*********************************************************
-.TP
-.B \-\-script\-security level
-This directive offers policy\-level control over OpenVPN's usage of external programs
-and scripts. Lower
-.B level
-values are more restrictive, higher values are more permissive. Settings for
-.B level:
-
-.B 0 \-\-
-Strictly no calling of external programs.
-.br
-.B 1 \-\-
-(Default) Only call built\-in executables such as ifconfig, ip, route, or netsh.
-.br
-.B 2 \-\-
-Allow calling of built\-in executables and user\-defined scripts.
-.br
-.B 3 \-\-
-Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
-
-OpenVPN releases before v2.3 also supported a
-.B method
-flag which indicated how OpenVPN should call external commands and scripts. This
-could be either
-.B execve
-or
-.B system.
-As of OpenVPN 2.3, this flag is no longer accepted. In most *nix environments the execve()
-approach has been used without any issues.
-
-Some directives such as \-\-up allow options to be passed to the external
-script. In these cases make sure the script name does not contain any spaces or
-the configuration parser will choke because it can't determine where the script
-name ends and script options start.
-
-To run scripts in Windows in earlier OpenVPN
-versions you needed to either add a full path to the script interpreter which can parse the
-script or use the
-.B system
-flag to run these scripts. As of OpenVPN 2.3 it is now a strict requirement to have
-full path to the script interpreter when running non\-executables files.
-This is not needed for executable files, such as .exe, .com, .bat or .cmd files. For
-example, if you have a Visual Basic script, you must use this syntax now:
-
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-\-\-up 'C:\\\\Windows\\\\System32\\\\wscript.exe C:\\\\Program\\ Files\\\\OpenVPN\\\\config\\\\my\-up\-script.vbs'
-.in -4
-.ft
+.ft C
+openvpn \-\-test\-crypto \-\-secret key \-\-verb 9
+.ft P
.fi
-
-Please note the single quote marks and the escaping of the backslashes (\\) and
-the space character.
-
-The reason the support for the
-.B system
-flag was removed is due to the security implications with shell expansions
-when executing scripts via the system() call.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+This option is very useful to test OpenVPN after it has been ported to a
+new platform, or to isolate problems in the compiler, OpenSSL crypto
+library, or OpenVPN\(aqs crypto code. Since it is a self\-test mode,
+problems with encryption and authentication can be debugged
+independently of network and tunnel issues.
+.TP
+.BI \-\-tmp\-dir \ dir
+Specify a directory \fBdir\fP for temporary files. This directory will be
+used by openvpn processes and script to communicate temporary data with
+openvpn main process. Note that the directory must be writable by the
+OpenVPN process after it has dropped it\(aqs root privileges.
+.sp
+This directory will be used by in the following cases:
+.INDENT 7.0
+.IP \(bu 2
+\fB\-\-client\-connect\fP scripts and \fBOPENVPN_PLUGIN_CLIENT_CONNECT\fP
+plug\-in hook to dynamically generate client\-specific configuration
+\fBclient_connect_config_file\fP and return success/failure via
+\fBclient_connect_deferred_file\fP when using deferred client connect
+method
+.IP \(bu 2
+\fBOPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\fP plug\-in hooks returns
+success/failure via \fBauth_control_file\fP when using deferred auth
+method
+.IP \(bu 2
+\fBOPENVPN_PLUGIN_ENABLE_PF\fP plugin hook to pass filtering rules
+via \fBpf_file\fP
+.UNINDENT
.TP
-.B \-\-disable\-occ
-Don't output a warning message if option inconsistencies are detected between
-peers. An example of an option inconsistency would be where one peer uses
-.B \-\-dev tun
-while the other peer uses
-.B \-\-dev tap.
-
-Use of this option is discouraged, but is provided as
-a temporary fix in situations where a recent version of OpenVPN must
-connect to an old version.
-.\"*********************************************************
-.TP
-.B \-\-user user
-Change the user ID of the OpenVPN process to
-.B user
-after initialization, dropping privileges in the process.
-This option is useful to protect the system
-in the event that some hostile party was able to gain control of
-an OpenVPN session. Though OpenVPN's security features make
-this unlikely, it is provided as a second line of defense.
-
-By setting
-.B user
-to
-.I nobody
-or somebody similarly unprivileged, the hostile party would be
-limited in what damage they could cause. Of course once
-you take away privileges, you cannot return them
-to an OpenVPN session. This means, for example, that if
-you want to reset an OpenVPN daemon with a
-.B SIGUSR1
-signal
-(for example in response
-to a DHCP reset), you should make use of one or more of the
-.B \-\-persist
-options to ensure that OpenVPN doesn't need to execute any privileged
-operations in order to restart (such as re\-reading key files
-or running
-.BR ifconfig
-on the TUN device).
-.\"*********************************************************
-.TP
-.B \-\-group group
-Similar to the
-.B \-\-user
-option,
-this option changes the group ID of the OpenVPN process to
-.B group
-after initialization.
-.\"*********************************************************
-.TP
-.B \-\-cd dir
-Change directory to
-.B dir
-prior to reading any files such as
-configuration files, key files, scripts, etc.
-.B dir
-should be an absolute path, with a leading "/",
-and without any references
-to the current directory such as "." or "..".
-
-This option is useful when you are running
-OpenVPN in
-.B \-\-daemon
-mode, and you want to consolidate all of
-your OpenVPN control files in one location.
-.\"*********************************************************
-.TP
-.B \-\-chroot dir
-Chroot to
-.B dir
-after initialization.
-.B \-\-chroot
-essentially redefines
-.B dir
-as being the top
-level directory tree (/). OpenVPN will therefore
-be unable to access any files outside this tree.
-This can be desirable from a security standpoint.
-
-Since the chroot operation is delayed until after
-initialization, most OpenVPN options that reference
-files will operate in a pre\-chroot context.
-
-In many cases, the
-.B dir
-parameter can point to an empty directory, however
-complications can result when scripts or restarts
-are executed after the chroot operation.
-
-Note: The SSL library will probably need /dev/urandom to be available inside
-the chroot directory
-.B dir.
-This is because SSL libraries occasionally need to collect fresh random. Newer
-linux kernels and some BSDs implement a getrandom() or getentropy() syscall
-that removes the need for /dev/urandom to be available.
-.\"*********************************************************
-.TP
-.B \-\-setcon context
-Apply SELinux
-.B context
-after initialization. This
-essentially provides the ability to restrict OpenVPN's
-rights to only network I/O operations, thanks to
-SELinux. This goes further than
-.B \-\-user
-and
-.B \-\-chroot
-in that those two, while being great security features,
-unfortunately do not protect against privilege escalation
-by exploitation of a vulnerable system call. You can of
-course combine all three, but please note that since
-setcon requires access to /proc you will have to provide
-it inside the chroot directory (e.g. with mount \-\-bind).
-
-Since the setcon operation is delayed until after
-initialization, OpenVPN can be restricted to just
-network\-related system calls, whereas by applying the
-context before startup (such as the OpenVPN one provided
-in the SELinux Reference Policies) you will have to
-allow many things required only during initialization.
-
-Like with chroot, complications can result when scripts
-or restarts are executed after the setcon operation,
-which is why you should really consider using the
-.B \-\-persist\-key
-and
-.B \-\-persist\-tun
-options.
-.\"*********************************************************
-.TP
-.B \-\-daemon [progname]
-Become a daemon after all initialization functions are completed.
-This option will cause all message and error output to
-be sent to the syslog file (such as /var/log/messages),
-except for the output of scripts and
-ifconfig commands,
-which will go to /dev/null unless otherwise redirected.
-The syslog redirection occurs immediately at the point
-that
-.B \-\-daemon
-is parsed on the command line even though
-the daemonization point occurs later. If one of the
-.B \-\-log
-options is present, it will supercede syslog
-redirection.
-
-The optional
-.B progname
-parameter will cause OpenVPN to report its program name
-to the system logger as
-.B progname.
-This can be useful in linking OpenVPN messages
-in the syslog file with specific tunnels.
-When unspecified,
-.B progname
-defaults to "openvpn".
-
-When OpenVPN is run with the
-.B \-\-daemon
-option, it will try to delay daemonization until the majority of initialization
-functions which are capable of generating fatal errors are complete. This means
-that initialization scripts can test the return status of the
-openvpn command for a fairly reliable indication of whether the command
-has correctly initialized and entered the packet forwarding event loop.
-
-In OpenVPN, the vast majority of errors which occur after initialization are non\-fatal.
-
-Note: as soon as OpenVPN has daemonized, it can not ask for usernames,
-passwords, or key pass phrases anymore. This has certain consequences,
-namely that using a password\-protected private key will fail unless the
-.B \-\-askpass
-option is used to tell OpenVPN to ask for the pass phrase (this
-requirement is new in v2.3.7, and is a consequence of calling daemon()
-before initializing the crypto layer).
-
-Further, using
-.B \-\-daemon
-together with
-.B \-\-auth\-user\-pass
-(entered on console) and
-.B \-\-auth\-nocache
-will fail as soon as key renegotiation (and reauthentication) occurs.
-.\"*********************************************************
-.TP
-.B \-\-syslog [progname]
-Direct log output to system logger, but do not become a daemon.
-See
-.B \-\-daemon
-directive above for description of
-.B progname
-parameter.
+.B \-\-use\-prediction\-resistance
+Enable prediction resistance on mbed TLS\(aqs RNG.
+.sp
+Enabling prediction resistance causes the RNG to reseed in each call for
+random. Reseeding this often can quickly deplete the kernel entropy
+pool.
+.sp
+If you need this option, please consider running a daemon that adds
+entropy to the kernel pool.
+.TP
+.BI \-\-user \ user
+Change the user ID of the OpenVPN process to \fBuser\fP after
+initialization, dropping privileges in the process. This option is
+useful to protect the system in the event that some hostile party was
+able to gain control of an OpenVPN session. Though OpenVPN\(aqs security
+features make this unlikely, it is provided as a second line of defense.
+.sp
+By setting \fBuser\fP to \fBnobody\fP or somebody similarly unprivileged,
+the hostile party would be limited in what damage they could cause. Of
+course once you take away privileges, you cannot return them to an
+OpenVPN session. This means, for example, that if you want to reset an
+OpenVPN daemon with a \fBSIGUSR1\fP signal (for example in response to
+a DHCP reset), you should make use of one or more of the \fB\-\-persist\fP
+options to ensure that OpenVPN doesn\(aqt need to execute any privileged
+operations in order to restart (such as re\-reading key files or running
+\fBifconfig\fP on the TUN device).
+.TP
+.BI \-\-writepid \ file
+Write OpenVPN\(aqs main process ID to \fBfile\fP\&.
+.UNINDENT
+.SS Log options
+.INDENT 0.0
+.TP
+.BI \-\-echo \ parms
+Echo \fBparms\fP to log output.
+.sp
+Designed to be used to send messages to a controlling application which
+is receiving the OpenVPN log output.
.TP
.B \-\-errors\-to\-stderr
-Output errors to stderr instead of stdout unless log output is redirected by one of the
-.B \-\-log
-options.
-.\"*********************************************************
+Output errors to stderr instead of stdout unless log output is
+redirected by one of the \fB\-\-log\fP options.
+.TP
+.BI \-\-log \ file
+Output logging messages to \fBfile\fP, including output to stdout/stderr
+which is generated by called scripts. If \fBfile\fP already exists it will
+be truncated. This option takes effect immediately when it is parsed in
+the command line and will supersede syslog output if \fB\-\-daemon\fP or
+\fB\-\-inetd\fP is also specified. This option is persistent over the entire
+course of an OpenVPN instantiation and will not be reset by
+\fBSIGHUP\fP, \fBSIGUSR1\fP, or \fB\-\-ping\-restart\fP\&.
+.sp
+Note that on Windows, when OpenVPN is started as a service, logging
+occurs by default without the need to specify this option.
+.TP
+.BI \-\-log\-append \ file
+Append logging messages to \fBfile\fP\&. If \fBfile\fP does not exist, it will
+be created. This option behaves exactly like \fB\-\-log\fP except that it
+appends to rather than truncating the log file.
.TP
-.B \-\-passtos
-Set the TOS field of the tunnel packet to what the payload's TOS is.
-.\"*********************************************************
+.B \-\-machine\-readable\-output
+Always write timestamps and message flags to log messages, even when
+they otherwise would not be prefixed. In particular, this applies to log
+messages sent to stdout.
.TP
-.B \-\-inetd [wait|nowait] [progname]
-Use this option when OpenVPN is being run from the inetd or
-.BR xinetd(8)
-server.
-
-The
-.B wait/nowait
-option must match what is specified in the inetd/xinetd
-config file. The
-.B nowait
-mode can only be used with
-.B \-\-proto tcp\-server.
-The default is
-.B wait.
-The
-.B nowait
-mode can be used to instantiate the OpenVPN daemon as a classic TCP server,
-where client connection requests are serviced on a single
-port number. For additional information on this kind of configuration,
-see the OpenVPN FAQ:
-.I http://openvpn.net/faq.html#oneport
-
-This option precludes the use of
-.B \-\-daemon, \-\-local,
-or
-.B \-\-remote.
-Note that this option causes message and error output to be handled in the same
-way as the
-.B \-\-daemon
-option. The optional
-.B progname
-parameter is also handled exactly as in
-.B \-\-daemon.
-
-Also note that in
-.B wait
-mode, each OpenVPN tunnel requires a separate TCP/UDP port and
-a separate inetd or xinetd entry. See the OpenVPN 1.x HOWTO for an example
-on using OpenVPN with xinetd:
-.I http://openvpn.net/1xhowto.html
-.\"*********************************************************
-.TP
-.B \-\-log file
-Output logging messages to
-.B file,
-including output to stdout/stderr which
-is generated by called scripts.
-If
-.B file
-already exists it will be truncated.
-This option takes effect
-immediately when it is parsed in the command line
-and will supercede syslog output if
-.B \-\-daemon
-or
-.B \-\-inetd
-is also specified.
-This option is persistent over the entire course of
-an OpenVPN instantiation and will not be reset by SIGHUP,
-SIGUSR1, or
-.B \-\-ping\-restart.
-
-Note that on Windows, when OpenVPN is started as a service,
-logging occurs by default without the need to specify
-this option.
-.\"*********************************************************
-.TP
-.B \-\-log\-append file
-Append logging messages to
-.B file.
-If
-.B file
-does not exist, it will be created.
-This option behaves exactly like
-.B \-\-log
-except that it appends to rather
-than truncating the log file.
-.\"*********************************************************
+.BI \-\-mute \ n
+Log at most \fBn\fP consecutive messages in the same category. This is
+useful to limit repetitive logging of similar message types.
+.TP
+.B \-\-mute\-replay\-warnings
+Silence the output of replay warnings, which are a common false alarm on
+WiFi networks. This option preserves the security of the replay
+protection code without the verbosity associated with warnings about
+duplicate packets.
.TP
.B \-\-suppress\-timestamps
-Avoid writing timestamps to log messages, even when they
-otherwise would be prepended. In particular, this applies to
-log messages sent to stdout.
-.\"*********************************************************
+Avoid writing timestamps to log messages, even when they otherwise would
+be prepended. In particular, this applies to log messages sent to
+stdout.
.TP
-.B \-\-machine\-readable\-output
-Always write timestamps and message flags to log messages, even when they
-otherwise would not be prefixed. In particular, this applies to
-log messages sent to stdout.
-.\"*********************************************************
-.TP
-.B \-\-writepid file
-Write OpenVPN's main process ID to
-.B file.
-.\"*********************************************************
-.TP
-.B \-\-nice n
-Change process priority after initialization
-(
-.B n
-greater than 0 is lower priority,
-.B n
-less than zero is higher priority).
-.\"*********************************************************
-.\".TP
-.\".B \-\-nice\-work n
-.\"Change priority of background TLS work thread. The TLS thread
-.\"feature is enabled when OpenVPN is built
-.\"with pthread support, and you are running OpenVPN
-.\"in TLS mode (i.e. with
-.\".B \-\-tls\-client
-.\"or
-.\".B \-\-tls\-server
-.\"specified).
-.\"
-.\"Using a TLS thread offloads the CPU\-intensive process of SSL/TLS\-based
-.\"key exchange to a background thread so that it does not become
-.\"a latency bottleneck in the tunnel packet forwarding process.
-.\"
-.\"The parameter
-.\".B n
-.\"is interpreted exactly as with the
-.\".B \-\-nice
-.\"option above, but in relation to the work thread rather
-.\"than the main thread.
-.\"*********************************************************
+.BI \-\-syslog \ progname
+Direct log output to system logger, but do not become a daemon. See
+\fB\-\-daemon\fP directive above for description of \fBprogname\fP parameter.
.TP
-.B \-\-fast\-io
-(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding
-a call to poll/epoll/select prior to the write operation. The purpose
-of such a call would normally be to block until the device
-or socket is ready to accept the write. Such blocking is unnecessary
-on some platforms which don't support write blocking on UDP sockets
-or TUN/TAP devices. In such cases, one can optimize the event loop
-by avoiding the poll/epoll/select call, improving CPU efficiency
-by 5% to 10%.
-
-This option can only be used on non\-Windows systems, when
-.B \-\-proto udp
-is specified, and when
-.B \-\-shaper
-is NOT specified.
-.\"*********************************************************
+.BI \-\-verb \ n
+Set output verbosity to \fBn\fP (default \fB1\fP). Each level shows all
+info from the previous levels. Level \fB3\fP is recommended if you want
+a good summary of what\(aqs happening without being swamped by output.
+.INDENT 7.0
.TP
-.B \-\-multihome
-Configure a multi\-homed UDP server. This option needs to be used when
-a server has more than one IP address (e.g. multiple interfaces, or
-secondary IP addresses), and is not using
-.B \-\-local
-to force binding to one specific address only. This option will
-add some extra lookups to the packet path to ensure that the UDP reply
-packets are always sent from the address that the client is
-talking to. This is not supported on all platforms, and it adds more
-processing, so it's not enabled by default.
-
-Note: this option is only relevant for UDP servers.
-
-Note 2: if you do an IPv6+IPv4 dual\-stack bind on a Linux machine with
-multiple IPv4 address, connections to IPv4 addresses will not work
-right on kernels before 3.15, due to missing kernel support for the
-IPv4\-mapped case (some distributions have ported this to earlier kernel
-versions, though).
-.\"*********************************************************
-.TP
-.B \-\-echo [parms...]
-Echo
-.B parms
-to log output.
-
-Designed to be used to send messages to a controlling application
-which is receiving the OpenVPN log output.
-.\"*********************************************************
-.TP
-.B \-\-remap\-usr1 signal
-Control whether internally or externally
-generated SIGUSR1 signals are remapped to
-SIGHUP (restart without persisting state) or
-SIGTERM (exit).
-
-.B signal
-can be set to "SIGHUP" or "SIGTERM". By default, no remapping
-occurs.
-.\"*********************************************************
-.TP
-.B \-\-verb n
-Set output verbosity to
-.B n
-(default=1). Each level shows all info from the previous levels.
-Level 3 is recommended if you want a good summary
-of what's happening without being swamped by output.
-
-.B 0 \-\-
+.B \fB0\fP
No output except fatal errors.
-.br
-.B 1 to 4 \-\-
-Normal usage range.
-.br
-.B 5 \-\-
-Output
-.B R
-and
-.B W
-characters to the console for each packet read and write, uppercase is
-used for TCP/UDP packets and lowercase is used for TUN/TAP packets.
-.br
-.B 6 to 11 \-\-
-Debug info range (see errlevel.h for additional
-information on debug levels).
-.\"*********************************************************
-.TP
-.B \-\-status file [n]
-Write operational status to
-.B file
-every
-.B n
-seconds.
-
-Status can also be written to the syslog by sending a
-.B SIGUSR2
-signal.
-
-With multi\-client capability enabled on a server, the status file includes a
-list of clients and a routing table. The output format can be controlled by the
-.B \-\-status\-version
-option in that case.
-
-For clients or instances running in point\-to\-point mode, it will contain the
-traffic statistics.
-.\"*********************************************************
.TP
-.B \-\-status\-version [n]
-Set the status file format version number to
-.B n\fR.
-
-This only affects the status file on servers with multi\-client capability
-enabled.
-
-.B 1
-\-\- traditional format (default). The client list contains the following
-fields comma\-separated: Common Name, Real Address, Bytes Received, Bytes Sent,
-Connected Since.
-.br
-.B 2
-\-\- a more reliable format for external processing. Compared to version 1, the
-client list contains some additional fields: Virtual Address, Virtual IPv6
-Address, Username, Client ID, Peer ID.
-Future versions may extend the number of fields.
-.br
-.B 3
-\-\- identical to 2, but fields are tab\-separated.
-
-.\"*********************************************************
-.TP
-.B \-\-mute n
-Log at most
-.B n
-consecutive messages in the same category. This is useful to
-limit repetitive logging of similar message types.
-.\"*********************************************************
+.B \fB1\fP to \fB4\fP
+Normal usage range.
.TP
-.B \-\-compress [algorithm]
-Enable a compression algorithm.
-
-The
-.B algorithm
-parameter may be "lzo", "lz4", or empty. LZO and LZ4
-are different compression algorithms, with LZ4 generally
+.B \fB5\fP
+Outputs \fBR\fP and \fBW\fP characters to the console for
+each packet read and write, uppercase is used for TCP/UDP
+packets and lowercase is used for TUN/TAP packets.
+.TP
+.B \fB6\fP to \fB11\fP
+Debug info range (see \fBerrlevel.h\fP in the source code for
+additional information on debug levels).
+.UNINDENT
+.UNINDENT
+.SS Protocol options
+.sp
+Options in this section affect features available in the OpenVPN wire
+protocol. Many of these options also define the encryption options
+of the data channel in the OpenVPN wire protocol. These options must be
+configured in a compatible way between both the local and remote side.
+.INDENT 0.0
+.TP
+.BI \-\-allow\-compression \ mode
+As described in the \fB\-\-compress\fP option, compression is a potentially
+dangerous option. This option allows controlling the behaviour of
+OpenVPN when compression is used and allowed.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+allow\-compression
+allow\-compression mode
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fBmode\fP argument can be one of the following values:
+.INDENT 7.0
+.TP
+.B \fBasym\fP (default)
+OpenVPN will only \fIdecompress downlink packets\fP but \fInot compress
+uplink packets\fP\&. This also allows migrating to disable compression
+when changing both server and client configurations to remove
+compression at the same time is not a feasible option.
+.TP
+.B \fBno\fP
+OpenVPN will refuse any non\-stub compression.
+.TP
+.B \fByes\fP
+OpenVPN will send and receive compressed packets.
+.UNINDENT
+.TP
+.BI \-\-auth \ alg
+Authenticate data channel packets and (if enabled) \fBtls\-auth\fP control
+channel packets with HMAC using message digest algorithm \fBalg\fP\&. (The
+default is \fBSHA1\fP ). HMAC is a commonly used message authentication
+algorithm (MAC) that uses a data string, a secure hash algorithm and a
+key to produce a digital signature.
+.sp
+The OpenVPN data channel protocol uses encrypt\-then\-mac (i.e. first
+encrypt a packet then HMAC the resulting ciphertext), which prevents
+padding oracle attacks.
+.sp
+If an AEAD cipher mode (e.g. GCM) is chosen then the specified \fB\-\-auth\fP
+algorithm is ignored for the data channel and the authentication method
+of the AEAD cipher is used instead. Note that \fBalg\fP still specifies
+the digest used for \fBtls\-auth\fP\&.
+.sp
+In static\-key encryption mode, the HMAC key is included in the key file
+generated by \fB\-\-genkey\fP\&. In TLS mode, the HMAC key is dynamically
+generated and shared between peers via the TLS control channel. If
+OpenVPN receives a packet with a bad HMAC it will drop the packet. HMAC
+usually adds 16 or 20 bytes per packet. Set \fBalg=none\fP to disable
+authentication.
+.sp
+For more information on HMAC see
+\fI\%http://www.cs.ucsd.edu/users/mihir/papers/hmac.html\fP
+.TP
+.BI \-\-cipher \ alg
+This option is deprecated for server\-client mode. \fB\-\-data\-ciphers\fP
+or possibly \fI\-\-data\-ciphers\-fallback\(ga\fP should be used instead.
+.sp
+Encrypt data channel packets with cipher algorithm \fBalg\fP\&.
+.sp
+The default is \fBBF\-CBC\fP, an abbreviation for Blowfish in Cipher
+Block Chaining mode. When cipher negotiation (NCP) is allowed,
+OpenVPN 2.4 and newer on both client and server side will automatically
+upgrade to \fBAES\-256\-GCM\fP\&. See \fB\-\-data\-ciphers\fP and
+\fB\-\-ncp\-disable\fP for more details on NCP.
+.sp
+Using \fBBF\-CBC\fP is no longer recommended, because of its 64\-bit
+block size. This small block size allows attacks based on collisions, as
+demonstrated by SWEET32. See
+\fI\%https://community.openvpn.net/openvpn/wiki/SWEET32\fP
+for details. Due to this, support for \fBBF\-CBC\fP, \fBDES\fP,
+\fBCAST5\fP, \fBIDEA\fP and \fBRC2\fP ciphers will be removed in
+OpenVPN 2.6.
+.sp
+To see other ciphers that are available with OpenVPN, use the
+\fB\-\-show\-ciphers\fP option.
+.sp
+Set \fBalg\fP to \fBnone\fP to disable encryption.
+.TP
+.BI \-\-compress \ algorithm
+\fBDEPRECATED\fP Enable a compression algorithm. Compression is generally
+not recommended. VPN tunnels which use compression are susceptible to
+the VORALCE attack vector.
+.sp
+The \fBalgorithm\fP parameter may be \fBlzo\fP, \fBlz4\fP,
+\fBlz4\-v2\fP, \fBstub\fP, \fBstub\-v2\fP or empty.
+LZO and LZ4 are different compression algorithms, with LZ4 generally
offering the best performance with least CPU usage.
-For backwards compatibility with OpenVPN versions before v2.4, use "lzo"
-(which is identical to the older option "\-\-comp\-lzo yes").
-
-If the
-.B algorithm
-parameter is empty, compression will be turned off, but the packet
-framing for compression will still be enabled, allowing a different
-setting to be pushed later.
-
-.B Security Considerations
-
-Compression and encryption is a tricky combination. If an attacker knows or is
-able to control (parts of) the plaintext of packets that contain secrets, the
-attacker might be able to extract the secret if compression is enabled. See
-e.g. the CRIME and BREACH attacks on TLS which also leverage compression to
-break encryption. If you are not entirely sure that the above does not apply
-to your traffic, you are advised to *not* enable compression.
-
-.\"*********************************************************
-.TP
-.B \-\-comp\-lzo [mode]
-.B DEPRECATED
-This option will be removed in a future OpenVPN release. Use the
-newer
-.B \-\-compress
-instead.
-
-Use LZO compression \-\- may add up to 1 byte per
-packet for incompressible data.
-.B mode
-may be "yes", "no", or "adaptive" (default).
-
-In a server mode setup, it is possible to selectively turn
-compression on or off for individual clients.
-
+.sp
+The \fBlz4\-v2\fP and \fBstub\-v2\fP variants implement a better
+framing that does not add overhead when packets cannot be compressed. All
+other variants always add one extra framing byte compared to no
+compression framing.
+.sp
+If the \fBalgorithm\fP parameter is \fBstub\fP, \fBstub\-v2\fP or empty,
+compression will be turned off, but the packet framing for compression
+will still be enabled, allowing a different setting to be pushed later.
+Additionally, \fBstub\fP and \fBstub\-v2\fP wil disable announcing
+\fBlzo\fP and \fBlz4\fP compression support via \fIIV_\fP variables to the
+server.
+.sp
+Note: the \fBstub\fP (or empty) option is NOT compatible with the older
+option \fB\-\-comp\-lzo no\fP\&.
+.sp
+\fB*Security Considerations*\fP
+.sp
+Compression and encryption is a tricky combination. If an attacker knows
+or is able to control (parts of) the plain\-text of packets that contain
+secrets, the attacker might be able to extract the secret if compression
+is enabled. See e.g. the \fICRIME\fP and \fIBREACH\fP attacks on TLS and
+\fIVORACLE\fP on VPNs which also leverage to break encryption. If you are not
+entirely sure that the above does not apply to your traffic, you are
+advised to \fInot\fP enable compression.
+.TP
+.BI \-\-comp\-lzo \ mode
+\fBDEPRECATED\fP Enable LZO compression algorithm. Compression is
+generally not recommended. VPN tunnels which uses compression are
+suspectible to the VORALCE attack vector.
+.sp
+Use LZO compression \-\- may add up to 1 byte per packet for incompressible
+data. \fBmode\fP may be \fByes\fP, \fBno\fP, or \fBadaptive\fP
+(default).
+.sp
+In a server mode setup, it is possible to selectively turn compression
+on or off for individual clients.
+.sp
First, make sure the client\-side config file enables selective
-compression by having at least one
-.B \-\-comp\-lzo
-directive, such as
-.B \-\-comp\-lzo no.
-This will turn off compression by default,
-but allow a future directive push from the server to
-dynamically change the
-on/off/adaptive setting.
-
-Next in a
-.B \-\-client\-config\-dir
-file, specify the compression setting for the client,
-for example:
-
+compression by having at least one \fB\-\-comp\-lzo\fP directive, such as
+\fB\-\-comp\-lzo no\fP\&. This will turn off compression by default, but allow
+a future directive push from the server to dynamically change the
+\fBon\fP/\fBoff\fP/\fBadaptive\fP setting.
+.sp
+Next in a \fB\-\-client\-config\-dir\fP file, specify the compression setting
+for the client, for example:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
+.ft C
comp\-lzo yes
push "comp\-lzo yes"
-.in -4
-.ft
+.ft P
.fi
-
-The first line sets the
-.B comp\-lzo
-setting for the server
-side of the link, the second sets the client side.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+The first line sets the \fBcomp\-lzo\fP setting for the server side of the
+link, the second sets the client side.
.TP
.B \-\-comp\-noadapt
-When used in conjunction with
-.B \-\-comp\-lzo,
-this option will disable OpenVPN's adaptive compression algorithm.
-Normally, adaptive compression is enabled with
-.B \-\-comp\-lzo.
-
+\fBDEPRECATED\fP When used in conjunction with \fB\-\-comp\-lzo\fP, this option
+will disable OpenVPN\(aqs adaptive compression algorithm. Normally, adaptive
+compression is enabled with \fB\-\-comp\-lzo\fP\&.
+.sp
Adaptive compression tries to optimize the case where you have
compression enabled, but you are sending predominantly incompressible
-(or pre\-compressed) packets over the tunnel, such as an FTP or rsync transfer
-of a large, compressed file. With adaptive compression,
-OpenVPN will periodically sample the compression process to measure its
-efficiency. If the data being sent over the tunnel is already compressed,
-the compression efficiency will be very low, triggering openvpn to disable
-compression for a period of time until the next re\-sample test.
-.\"*********************************************************
-.TP
-.B \-\-management socket\-name unix [pw\-file] \ \ \ \ \ (recommended)
-.TQ
-.B \-\-management IP port [pw\-file]
-Enable a management server on a
-.B socket\-name
-Unix socket on those platforms supporting it, or on
-a designated TCP port.
-
-.B pw\-file
-, if specified, is a password file where the password must be on first line.
-Instead of a filename it can use the keyword stdin which will prompt the user
-for a password to use when OpenVPN is starting.
-
-For unix sockets, the default behaviour is to create a unix domain socket
-that may be connected to by any process. Use the
-.B \-\-management\-client\-user
-and
-.B \-\-management\-client\-group
-directives to restrict access.
-
-The management interface provides a special mode where the TCP management link
-can operate over the tunnel itself. To enable this mode, set IP to
-.B tunnel.
-Tunnel mode will cause the management interface to listen for a
-TCP connection on the local VPN address of the TUN/TAP interface.
-
-.B BEWARE
-of enabling the management interface over TCP. In these cases you should
-.I ALWAYS
-make use of
-.B pw\-file
-to password protect the management interface. Any user who can connect to this
-TCP
-.B IP:port
-will be able to manage and control (and interfere with) the OpenVPN process.
-It is also strongly recommended to set IP to 127.0.0.1 (localhost) to restrict
-accessibility of the management server to local clients.
-
-While the management port is designed for programmatic control of OpenVPN by
-other applications, it is possible to telnet to the port, using a telnet client
-in "raw" mode. Once connected, type "help" for a list of commands.
-
-For detailed documentation on the management interface, see the
-.I management\-notes.txt
-file in the management folder of the OpenVPN source distribution.
-
+(or pre\-compressed) packets over the tunnel, such as an FTP or rsync
+transfer of a large, compressed file. With adaptive compression, OpenVPN
+will periodically sample the compression process to measure its
+efficiency. If the data being sent over the tunnel is already
+compressed, the compression efficiency will be very low, triggering
+openvpn to disable compression for a period of time until the next
+re\-sample test.
.TP
-.B \-\-management\-client
-Management interface will connect as a TCP/unix domain client to
-.B IP:port
-specified by
-.B \-\-management
-rather than listen as a TCP server or on a unix domain socket.
-
-If the client connection fails to connect or is disconnected,
-a SIGTERM signal will be generated causing OpenVPN to quit.
-.\"*********************************************************
+.B \-\-key\-direction
+Alternative way of specifying the optional direction parameter for the
+\fB\-\-tls\-auth\fP and \fB\-\-secret\fP options. Useful when using inline files
+(See section on inline files).
+.TP
+.BI \-\-keysize \ n
+\fBDEPRECATED\fP This option will be removed in OpenVPN 2.6.
+.sp
+Size of cipher key in bits (optional). If unspecified, defaults to
+cipher\-specific default. The \fB\-\-show\-ciphers\fP option (see below) shows
+all available OpenSSL ciphers, their default key sizes, and whether the
+key size can be changed. Use care in changing a cipher\(aqs default key
+size. Many ciphers have not been extensively cryptanalyzed with
+non\-standard key lengths, and a larger key may offer no real guarantee
+of greater security, or may even reduce security.
+.TP
+.BI \-\-data\-ciphers \ cipher\-list
+Restrict the allowed ciphers to be negotiated to the ciphers in
+\fBcipher\-list\fP\&. \fBcipher\-list\fP is a colon\-separated list of ciphers,
+and defaults to \fBAES\-256\-GCM:AES\-128\-GCM\fP\&.
+.sp
+For servers, the first cipher from \fBcipher\-list\fP that is also
+supported by the client will be pushed to clients that support cipher
+negotiation.
+.sp
+Cipher negotiation is enabled in client\-server mode only. I.e. if
+\fB\-\-mode\fP is set to \(aqserver\(aq (server\-side, implied by setting
+\fB\-\-server\fP ), or if \fB\-\-pull\fP is specified (client\-side, implied by
+setting \-\-client).
+.sp
+If no common cipher is found during cipher negotiation, the connection
+is terminated. To support old clients/old servers that do not provide any
+cipher negotiation support see \fB\-\-data\-ciphers\-fallback\fP\&.
+.sp
+Additionally, to allow for more smooth transition, if NCP is enabled,
+OpenVPN will inherit the cipher of the peer if that cipher is different
+from the local \fB\-\-cipher\fP setting, but the peer cipher is one of the
+ciphers specified in \fB\-\-data\-ciphers\fP\&. E.g. a non\-NCP client (<=v2.3,
+or with \-\-ncp\-disabled set) connecting to a NCP server (v2.4+) with
+\fB\-\-cipher BF\-CBC\fP and \fB\-\-data\-ciphers AES\-256\-GCM:AES\-256\-CBC\fP set can
+either specify \fB\-\-cipher BF\-CBC\fP or \fB\-\-cipher AES\-256\-CBC\fP and both
+will work.
+.sp
+Note for using NCP with an OpenVPN 2.4 peer: This list must include the
+\fBAES\-256\-GCM\fP and \fBAES\-128\-GCM\fP ciphers.
+.sp
+This list is restricted to be 127 chars long after conversion to OpenVPN
+ciphers.
+.sp
+This option was called \fB\-\-ncp\-ciphers\fP in OpenVPN 2.4 but has been renamed
+to \fB\-\-data\-ciphers\fP in OpenVPN 2.5 to more accurately reflect its meaning.
+.TP
+.BI \-\-data\-ciphers\-fallback \ alg
+Configure a cipher that is used to fall back to if we could not determine
+which cipher the peer is willing to use.
+.sp
+This option should only be needed to
+connect to peers that are running OpenVPN 2.3 and older version, and
+have been configured with \fI\-\-enable\-small\fP
+(typically used on routers or other embedded devices).
.TP
-.B \-\-management\-query\-passwords
-Query management channel for private key password and
-.B \-\-auth\-user\-pass
-username/password. Only query the management channel
-for inputs which ordinarily would have been queried from the
-console.
-.\"*********************************************************
+.B \-\-ncp\-disable
+\fBDEPRECATED\fP Disable "Negotiable Crypto Parameters". This completely
+disables cipher negotiation.
+.TP
+.BI \-\-secret \ args
+Enable Static Key encryption mode (non\-TLS). Use pre\-shared secret
+\fBfile\fP which was generated with \fB\-\-genkey\fP\&.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+secret file
+secret file direction
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The optional \fBdirection\fP parameter enables the use of 4 distinct keys
+(HMAC\-send, cipher\-encrypt, HMAC\-receive, cipher\-decrypt), so that each
+data flow direction has a different set of HMAC and cipher keys. This
+has a number of desirable security properties including eliminating
+certain kinds of DoS and message replay attacks.
+.sp
+When the \fBdirection\fP parameter is omitted, 2 keys are used
+bidirectionally, one for HMAC and the other for encryption/decryption.
+.sp
+The \fBdirection\fP parameter should always be complementary on either
+side of the connection, i.e. one side should use \fB0\fP and the other
+should use \fB1\fP, or both sides should omit it altogether.
+.sp
+The \fBdirection\fP parameter requires that \fBfile\fP contains a 2048 bit
+key. While pre\-1.5 versions of OpenVPN generate 1024 bit key files, any
+version of OpenVPN which supports the \fBdirection\fP parameter, will also
+support 2048 bit key file generation using the \fB\-\-genkey\fP option.
+.sp
+Static key encryption mode has certain advantages, the primary being
+ease of configuration.
+.sp
+There are no certificates or certificate authorities or complicated
+negotiation handshakes and protocols. The only requirement is that you
+have a pre\-existing secure channel with your peer (such as \fBssh\fP) to
+initially copy the key. This requirement, along with the fact that your
+key never changes unless you manually generate a new one, makes it
+somewhat less secure than TLS mode (see below). If an attacker manages
+to steal your key, everything that was ever encrypted with it is
+compromised. Contrast that to the perfect forward secrecy features of
+TLS mode (using Diffie Hellman key exchange), where even if an attacker
+was able to steal your private key, he would gain no information to help
+him decrypt past sessions.
+.sp
+Another advantageous aspect of Static Key encryption mode is that it is
+a handshake\-free protocol without any distinguishing signature or
+feature (such as a header or protocol handshake sequence) that would
+mark the ciphertext packets as being generated by OpenVPN. Anyone
+eavesdropping on the wire would see nothing but random\-looking data.
+.TP
+.BI \-\-tran\-window \ n
+Transition window \-\- our old key can live this many seconds after a new
+a key renegotiation begins (default \fB3600\fP seconds). This feature
+allows for a graceful transition from old to new key, and removes the key
+renegotiation sequence from the critical path of tunnel data forwarding.
+.UNINDENT
+.SS Client Options
+.sp
+The client options are used when connecting to an OpenVPN server configured
+to use \fB\-\-server\fP, \fB\-\-server\-bridge\fP, or \fB\-\-mode server\fP in its
+configuration.
+.INDENT 0.0
.TP
-.B \-\-management\-query\-proxy
-Query management channel for proxy server information for a specific
-.B \-\-remote
-(client\-only).
-.\"*********************************************************
+.B \-\-allow\-pull\-fqdn
+Allow client to pull DNS names from server (rather than being limited to
+IP address) for \fB\-\-ifconfig\fP, \fB\-\-route\fP, and \fB\-\-route\-gateway\fP\&.
.TP
-.B \-\-management\-query\-remote
-Allow management interface to override
-.B \-\-remote
-directives (client\-only).
-.\"*********************************************************
-.TP
-.B \-\-management\-external\-key
-Allows usage for external private key file instead of
-.B \-\-key
-option (client\-only).
-.\"*********************************************************
-.TP
-.B \-\-management\-external\-cert certificate\-hint
-Allows usage for external certificate instead of
-.B \-\-cert
-option (client\-only).
-.B certificate\-hint
-is an arbitrary string which is passed to a management
-interface client as an argument of NEED\-CERTIFICATE notification.
-Requires \-\-management\-external\-key.
-.\"*********************************************************
+.B \-\-allow\-recursive\-routing
+When this option is set, OpenVPN will not drop incoming tun packets with
+same destination as host.
.TP
-.B \-\-management\-forget\-disconnect
-Make OpenVPN forget passwords when management session
-disconnects.
-
-This directive does not affect the
-.B \-\-http\-proxy
-username/password. It is always cached.
-.\"*********************************************************
+.BI \-\-auth\-token \ token
+This is not an option to be used directly in any configuration files,
+but rather push this option from a \fB\-\-client\-connect\fP script or a
+\fB\-\-plugin\fP which hooks into the \fBOPENVPN_PLUGIN_CLIENT_CONNECT\fP
+or \fBOPENVPN_PLUGIN_CLIENT_CONNECT_V2\fP calls. This option provides a
+possibility to replace the clients password with an authentication token
+during the lifetime of the OpenVPN client.
+.sp
+Whenever the connection is renegotiated and the
+\fB\-\-auth\-user\-pass\-verify\fP script or \fB\-\-plugin\fP making use of the
+\fBOPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\fP hook is triggered, it will
+pass over this token as the password instead of the password the user
+provided. The authentication token can only be reset by a full reconnect
+where the server can push new options to the client. The password the
+user entered is never preserved once an authentication token has been
+set. If the OpenVPN server side rejects the authentication token then
+the client will receive an \fBAUTH_FAILED\fP and disconnect.
+.sp
+The purpose of this is to enable two factor authentication methods, such
+as HOTP or TOTP, to be used without needing to retrieve a new OTP code
+each time the connection is renegotiated. Another use case is to cache
+authentication data on the client without needing to have the users
+password cached in memory during the life time of the session.
+.sp
+To make use of this feature, the \fB\-\-client\-connect\fP script or
+\fB\-\-plugin\fP needs to put
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+push "auth\-token UNIQUE_TOKEN_VALUE"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+into the file/buffer for dynamic configuration data. This will then make
+the OpenVPN server to push this value to the client, which replaces the
+local password with the \fBUNIQUE_TOKEN_VALUE\fP\&.
+.sp
+Newer clients (2.4.7+) will fall back to the original password method
+after a failed auth. Older clients will keep using the token value and
+react according to \fB\-\-auth\-retry\fP
.TP
-.B \-\-management\-hold
-Start OpenVPN in a hibernating state, until a client
-of the management interface explicitly starts it
-with the
-.B hold release
-command.
-.\"*********************************************************
+.BI \-\-auth\-token\-user \ base64username
+Companion option to \fB\-\-auth\-token\fP\&. This options allows to override
+the username used by the client when reauthenticating with the \fBauth\-token\fP\&.
+It also allows to use \fB\-\-auth\-token\fP in setups that normally do not use
+username and password.
+.sp
+The username has to be base64 encoded.
.TP
-.B \-\-management\-signal
-Send SIGUSR1 signal to OpenVPN if management session disconnects.
-This is useful when you wish to disconnect an OpenVPN session on
-user logoff. For \-\-management\-client this option is not needed since
-a disconnect will always generate a SIGTERM.
-.\"*********************************************************
-.TP
-.B \-\-management\-log\-cache n
-Cache the most recent
-.B n
-lines of log file history for usage
-by the management channel.
-.\"*********************************************************
+.B \-\-auth\-user\-pass
+Authenticate with server using username/password.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+auth\-user\-pass
+auth\-user\-pass up
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If \fBup\fP is present, it must be a file containing username/password on 2
+lines. If the password line is missing, OpenVPN will prompt for one.
+.sp
+If \fBup\fP is omitted, username/password will be prompted from the
+console.
+.sp
+The server configuration must specify an \fB\-\-auth\-user\-pass\-verify\fP
+script to verify the username/password provided by the client.
+.TP
+.BI \-\-auth\-retry \ type
+Controls how OpenVPN responds to username/password verification errors
+such as the client\-side response to an \fBAUTH_FAILED\fP message from
+the server or verification failure of the private key password.
+.sp
+Normally used to prevent auth errors from being fatal on the client
+side, and to permit username/password requeries in case of error.
+.sp
+An \fBAUTH_FAILED\fP message is generated by the server if the client
+fails \fB\-\-auth\-user\-pass\fP authentication, or if the server\-side
+\fB\-\-client\-connect\fP script returns an error status when the client
+tries to connect.
+.sp
+\fBtype\fP can be one of:
+.INDENT 7.0
.TP
-.B \-\-management\-up\-down
-Report tunnel up/down events to management interface.
-.B
-.\"*********************************************************
+.B \fBnone\fP
+Client will exit with a fatal error (this is the default).
.TP
-.B \-\-management\-client\-auth
-Gives management interface client the responsibility
-to authenticate clients after their client certificate
-has been verified. See management\-notes.txt in OpenVPN
-distribution for detailed notes.
-.\"*********************************************************
+.B \fBnointeract\fP
+Client will retry the connection without requerying
+for an \fB\-\-auth\-user\-pass\fP username/password. Use this option for
+unattended clients.
.TP
-.B \-\-management\-client\-pf
-Management interface clients must specify a packet
-filter file for each connecting client. See management\-notes.txt
-in OpenVPN distribution for detailed notes.
-.\"*********************************************************
-.TP
-.B \-\-management\-client\-user u
-When the management interface is listening on a unix domain socket,
-only allow connections from user
-.B u.
-.\"*********************************************************
-.TP
-.B \-\-management\-client\-group g
-When the management interface is listening on a unix domain socket,
-only allow connections from group
-.B g.
-.\"*********************************************************
-.TP
-.B \-\-plugin module\-pathname [init\-string]
-Load plug\-in module from the file
-.B module\-pathname,
-passing
-.B init\-string
-as an argument
-to the module initialization function. Multiple
-plugin modules may be loaded into one OpenVPN
-process.
-
-The
-.B module\-pathname
-argument can be just a filename or a filename with a relative
-or absolute path. The format of the filename and path defines
-if the plug\-in will be loaded from a default plug\-in directory
-or outside this directory.
-
+.B \fBinteract\fP
+Client will requery for an \fB\-\-auth\-user\-pass\fP
+username/password and/or private key password before attempting a
+reconnection.
+.UNINDENT
+.sp
+Note that while this option cannot be pushed, it can be controlled from
+the management interface.
+.TP
+.B \-\-client
+A helper directive designed to simplify the configuration of OpenVPN\(aqs
+client mode. This directive is equivalent to:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-.B \-\-plugin path\ \ \ \ \ \ \ \ Effective directory used
-====================================================
- myplug.so DEFAULT_DIR/myplug.so
- subdir/myplug.so DEFAULT_DIR/subdir/myplug.so
- ./subdir/myplug.so CWD/subdir/myplug.so
- /usr/lib/my/plug.so /usr/lib/my/plug.so
-.in -4
+.ft C
+pull
+tls\-client
+.ft P
.fi
-
-DEFAULT_DIR is replaced by the default plug\-in directory,
-which is configured at the build time of OpenVPN. CWD is the
-current directory where OpenVPN was started or the directory
-OpenVPN have swithed into via the
-.B \-\-cd
-option before the
-.B \-\-plugin
-option.
-
-For more information and examples on how to build OpenVPN
-plug\-in modules, see the README file in the
-.B plugin
-folder of the OpenVPN source distribution.
-
-If you are using an RPM install of OpenVPN, see
-/usr/share/openvpn/plugin. The documentation is
-in
-.B doc
-and the actual plugin modules are in
-.B lib.
-
-Multiple plugin modules can be cascaded, and modules can be
-used in tandem with scripts. The modules will be called by
-OpenVPN in the order that they are declared in the config
-file. If both a plugin and script are configured for the same
-callback, the script will be called last. If the
-return code of the module/script controls an authentication
-function (such as tls\-verify, auth\-user\-pass\-verify, or
-client\-connect), then
-every module and script must return success (0) in order for
-the connection to be authenticated.
-.\"*********************************************************
-.TP
-.B \-\-keying\-material\-exporter label len
-Save Exported Keying Material [RFC5705] of len bytes (must be
-between 16 and 4095 bytes) using label in environment
-(exported_keying_material) for use by plugins in
-OPENVPN_PLUGIN_TLS_FINAL callback.
-
-Note that exporter labels have the potential to collide with existing PRF
-labels. In order to prevent this, labels MUST begin with "EXPORTER".
-
-This option requires OpenSSL 1.0.1 or newer.
-.\"*********************************************************
-.SS Server Mode
-Starting with OpenVPN 2.0, a multi\-client TCP/UDP server mode
-is supported, and can be enabled with the
-.B \-\-mode server
-option. In server mode, OpenVPN will listen on a single
-port for incoming client connections. All client
-connections will be routed through a single tun or tap
-interface. This mode is designed for scalability and should
-be able to support hundreds or even thousands of clients
-on sufficiently fast hardware. SSL/TLS authentication must
-be used in this mode.
-.\"*********************************************************
-.TP
-.B \-\-server network netmask ['nopool']
-A helper directive designed to simplify the configuration
-of OpenVPN's server mode. This directive will set up an
-OpenVPN server which will allocate addresses to clients
-out of the given network/netmask. The server itself
-will take the ".1" address of the given network
-for use as the server\-side endpoint of the local
-TUN/TAP interface.
-
-For example,
-.B \-\-server 10.8.0.0 255.255.255.0
-expands as follows:
-
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-client\-nat \ args
+This pushable client option sets up a stateless one\-to\-one NAT rule on
+packet addresses (not ports), and is useful in cases where routes or
+ifconfig settings pushed to the client would create an IP numbering
+conflict.
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
- mode server
- tls\-server
- push "topology [topology]"
-
- if dev tun AND (topology == net30 OR topology == p2p):
- ifconfig 10.8.0.1 10.8.0.2
- if !nopool:
- ifconfig\-pool 10.8.0.4 10.8.0.251
- route 10.8.0.0 255.255.255.0
- if client\-to\-client:
- push "route 10.8.0.0 255.255.255.0"
- else if topology == net30:
- push "route 10.8.0.1"
-
- if dev tap OR (dev tun AND topology == subnet):
- ifconfig 10.8.0.1 255.255.255.0
- if !nopool:
- ifconfig\-pool 10.8.0.2 10.8.0.253 255.255.255.0
- push "route\-gateway 10.8.0.1"
- if route\-gateway unset:
- route\-gateway 10.8.0.2
-
-.in -4
-.ft
+.ft C
+client\-nat snat 192.168.0.0/255.255.0.0
+client\-nat dnat 10.64.0.0/255.255.0.0
+.ft P
.fi
-
-Don't use
-.B \-\-server
-if you are ethernet bridging. Use
-.B \-\-server\-bridge
-instead.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+\fBnetwork/netmask\fP (for example \fB192.168.0.0/255.255.0.0\fP) defines
+the local view of a resource from the client perspective, while
+\fBalias/netmask\fP (for example \fB10.64.0.0/255.255.0.0\fP) defines the
+remote view from the server perspective.
+.sp
+Use \fBsnat\fP (source NAT) for resources owned by the client and
+\fBdnat\fP (destination NAT) for remote resources.
+.sp
+Set \fB\-\-verb 6\fP for debugging info showing the transformation of
+src/dest addresses in packets.
+.TP
+.BI \-\-connect\-retry \ n
+Wait \fBn\fP seconds between connection attempts (default \fB5\fP).
+Repeated reconnection attempts are slowed down after 5 retries per
+remote by doubling the wait time after each unsuccessful attempt. An
+optional argument \fBmax\fP specifies the maximum value of wait time in
+seconds at which it gets capped (default \fB300\fP).
+.TP
+.BI \-\-connect\-retry\-max \ n
+\fBn\fP specifies the number of times each \fB\-\-remote\fP or
+\fB<connection>\fP entry is tried. Specifying \fBn\fP as \fB1\fP would try
+each entry exactly once. A successful connection resets the counter.
+(default \fIunlimited\fP).
+.TP
+.BI \-\-connect\-timeout \ n
+See \fB\-\-server\-poll\-timeout\fP\&.
+.TP
+.BI \-\-explicit\-exit\-notify \ n
+In UDP client mode or point\-to\-point mode, send server/peer an exit
+notification if tunnel is restarted or OpenVPN process is exited. In
+client mode, on exit/restart, this option will tell the server to
+immediately close its client instance object rather than waiting for a
+timeout.
+.sp
+The \fBn\fP parameter (default \fB1\fP if not present) controls the
+maximum number of attempts that the client will try to resend the exit
+notification message.
+.sp
+In UDP server mode, send \fBRESTART\fP control channel command to
+connected clients. The \fBn\fP parameter (default \fB1\fP if not present)
+controls client behavior. With \fBn\fP = \fB1\fP client will attempt to
+reconnect to the same server, with \fBn\fP = \fB2\fP client will advance
+to the next server.
+.sp
+OpenVPN will not send any exit notifications unless this option is
+enabled.
.TP
-.B \-\-server\-bridge gateway netmask pool\-start\-IP pool\-end\-IP
+.BI \-\-inactive \ args
+Causes OpenVPN to exit after \fBn\fP seconds of inactivity on the TUN/TAP
+device. The time length of inactivity is measured since the last
+incoming or outgoing tunnel packet. The default value is 0 seconds,
+which disables this feature.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+inactive n
+inactive n bytes
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If the optional \fBbytes\fP parameter is included, exit if less than
+\fBbytes\fP of combined in/out traffic are produced on the tun/tap device
+in \fBn\fP seconds.
+.sp
+In any case, OpenVPN\(aqs internal ping packets (which are just keepalives)
+and TLS control packets are not considered "activity", nor are they
+counted as traffic, as they are used internally by OpenVPN and are not
+an indication of actual user activity.
+.TP
+.BI \-\-proto\-force \ p
+When iterating through connection profiles, only consider profiles using
+protocol \fBp\fP (\fBtcp\fP | \fBudp\fP).
.TP
-.B \-\-server\-bridge ['nogw']
-
-A helper directive similar to
-.B \-\-server
-which is designed to simplify the configuration
-of OpenVPN's server mode in ethernet bridging configurations.
-
-If
-.B \-\-server\-bridge
-is used without any parameters, it will enable a DHCP\-proxy
-mode, where connecting OpenVPN clients will receive an IP
-address for their TAP adapter from the DHCP server running
-on the OpenVPN server\-side LAN.
-Note that only clients that support
-the binding of a DHCP client with the TAP adapter (such as
-Windows) can support this mode. The optional
-.B nogw
-flag (advanced) indicates that gateway information should not be
-pushed to the client.
-
-To configure ethernet bridging, you
-must first use your OS's bridging capability
-to bridge the TAP interface with the ethernet
-NIC interface. For example, on Linux this is done
-with the
-.B brctl
-tool, and with Windows XP it is done in the Network
-Connections Panel by selecting the ethernet and
-TAP adapters and right\-clicking on "Bridge Connections".
-
-Next you you must manually set the
-IP/netmask on the bridge interface. The
-.B gateway
-and
-.B netmask
-parameters to
-.B \-\-server\-bridge
-can be set to either the IP/netmask of the
-bridge interface, or the IP/netmask of the
-default gateway/router on the bridged
-subnet.
-
-Finally, set aside a IP range in the bridged
-subnet,
-denoted by
-.B pool\-start\-IP
-and
-.B pool\-end\-IP,
-for OpenVPN to allocate to connecting
-clients.
-
-For example,
-.B server\-bridge 10.8.0.4 255.255.255.0 10.8.0.128 10.8.0.254
-expands as follows:
-
+.B \-\-pull
+This option must be used on a client which is connecting to a
+multi\-client server. It indicates to OpenVPN that it should accept
+options pushed by the server, provided they are part of the legal set of
+pushable options (note that the \fB\-\-pull\fP option is implied by
+\fB\-\-client\fP ).
+.sp
+In particular, \fB\-\-pull\fP allows the server to push routes to the
+client, so you should not use \fB\-\-pull\fP or \fB\-\-client\fP in situations
+where you don\(aqt trust the server to have control over the client\(aqs
+routing table.
+.TP
+.BI \-\-pull\-filter \ args
+Filter options on the client pushed by the server to the client.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-mode server
-tls\-server
-
-ifconfig\-pool 10.8.0.128 10.8.0.254 255.255.255.0
-push "route\-gateway 10.8.0.4"
-.in -4
-.ft
+.ft C
+pull\-filter accept text
+pull\-filter ignore text
+pull\-filter reject text
+.ft P
.fi
-
-In another example,
-.B \-\-server\-bridge
-(without parameters) expands as follows:
-
+.UNINDENT
+.UNINDENT
+.sp
+Filter options received from the server if the option starts with
+\fBtext\fP\&. The action flag \fBaccept\fP allows the option,
+\fBignore\fP removes it and \fBreject\fP flags an error and triggers
+a \fBSIGUSR1\fP restart. The filters may be specified multiple times,
+and each filter is applied in the order it is specified. The filtering of
+each option stops as soon as a match is found. Unmatched options are accepted
+by default.
+.sp
+Prefix comparison is used to match \fBtext\fP against the received option so
+that
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-mode server
-tls\-server
-
-push "route\-gateway dhcp"
-.in -4
-.ft
+.ft C
+pull\-filter ignore "route"
+.ft P
.fi
-
-Or
-.B \-\-server\-bridge nogw
-expands as follows:
-
+.UNINDENT
+.UNINDENT
+.sp
+would remove all pushed options starting with \fBroute\fP which would
+include, for example, \fBroute\-gateway\fP\&. Enclose \fItext\fP in quotes to
+embed spaces.
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-mode server
-tls\-server
-.in -4
-.ft
+.ft C
+pull\-filter accept "route 192.168.1."
+pull\-filter ignore "route "
+.ft P
.fi
-.\"*********************************************************
-.TP
-.B \-\-push "option"
-Push a config file option back to the client for remote
-execution. Note that
-.B
-option
-must be enclosed in double quotes (""). The client must specify
-.B \-\-pull
-in its config file. The set of options which can be
-pushed is limited by both feasibility and security.
-Some options such as those which would execute scripts
-are banned, since they would effectively allow a compromised
-server to execute arbitrary code on the client.
-Other options such as TLS or MTU parameters
-cannot be pushed because the client needs to know
-them before the connection to the server can be initiated.
-
-This is a partial list of options which can currently be pushed:
-.B \-\-route, \-\-route\-gateway, \-\-route\-delay, \-\-redirect\-gateway,
-.B \-\-ip\-win32, \-\-dhcp\-option,
-.B \-\-inactive, \-\-ping, \-\-ping\-exit, \-\-ping\-restart,
-.B \-\-setenv,
-.B \-\-auth\-token,
-.B \-\-persist\-key, \-\-persist\-tun, \-\-echo,
-.B \-\-comp\-lzo,
-.B \-\-socket\-flags,
-.B \-\-sndbuf, \-\-rcvbuf
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+would remove all routes that do not start with \fB192.168.1\fP\&.
+.sp
+\fINote\fP that \fBreject\fP may result in a repeated cycle of failure and
+reconnect, unless multiple remotes are specified and connection to the
+next remote succeeds. To silently ignore an option pushed by the server,
+use \fBignore\fP\&.
+.TP
+.BI \-\-remote \ args
+Remote host name or IP address, port and protocol.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote host
+remote host port
+remote host port proto
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fBport\fP and \fBproto\fP arguments are optional. The OpenVPN client
+will try to connect to a server at \fBhost:port\fP\&. The \fBproto\fP argument
+indicates the protocol to use when connecting with the remote, and may be
+\fBtcp\fP or \fBudp\fP\&. To enforce IPv4 or IPv6 connections add a
+\fB4\fP or \fB6\fP suffix; like \fBudp4\fP / \fBudp6\fP
+/ \fBtcp4\fP / \fBtcp6\fP\&.
+.sp
+On the client, multiple \fB\-\-remote\fP options may be specified for
+redundancy, each referring to a different OpenVPN server, in the order
+specified by the list of \fB\-\-remote\fP options. Specifying multiple
+\fB\-\-remote\fP options for this purpose is a special case of the more
+general connection\-profile feature. See the \fB<connection>\fP
+documentation below.
+.sp
+The client will move on to the next host in the list, in the event of
+connection failure. Note that at any given time, the OpenVPN client will
+at most be connected to one server.
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote server1.example.net
+remote server1.example.net 1194
+remote server2.example.net 1194 tcp
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.INDENT 7.0
+.TP
+.B \fINote:\fP
+Since UDP is connectionless, connection failure is defined by
+the \fB\-\-ping\fP and \fB\-\-ping\-restart\fP options.
+.sp
+Also, if you use multiple \fB\-\-remote\fP options, AND you are dropping
+root privileges on the client with \fB\-\-user\fP and/or \fB\-\-group\fP AND
+the client is running a non\-Windows OS, if the client needs to switch
+to a different server, and that server pushes back different TUN/TAP
+or route settings, the client may lack the necessary privileges to
+close and reopen the TUN/TAP interface. This could cause the client
+to exit with a fatal error.
+.UNINDENT
+.sp
+If \fB\-\-remote\fP is unspecified, OpenVPN will listen for packets from any
+IP address, but will not act on those packets unless they pass all
+authentication tests. This requirement for authentication is binding on
+all potential peers, even those from known and supposedly trusted IP
+addresses (it is very easy to forge a source IP address on a UDP
+packet).
+.sp
+When used in TCP mode, \fB\-\-remote\fP will act as a filter, rejecting
+connections from any host which does not match \fBhost\fP\&.
+.sp
+If \fBhost\fP is a DNS name which resolves to multiple IP addresses,
+OpenVPN will try them in the order that the system getaddrinfo()
+presents them, so priorization and DNS randomization is done by the
+system library. Unless an IP version is forced by the protocol
+specification (4/6 suffix), OpenVPN will try both IPv4 and IPv6
+addresses, in the order getaddrinfo() returns them.
.TP
-.B \-\-push\-reset
-Don't inherit the global push list for a specific client instance.
-Specify this option in a client\-specific context such
-as with a
-.B \-\-client\-config\-dir
-configuration file. This option will ignore
-.B \-\-push
-options at the global config file level.
-.\"*********************************************************
-.TP
-.B \-\-push\-remove opt
-selectively remove all
-.B \-\-push
-options matching "opt" from the option list for a client. "opt" is matched
-as a substring against the whole option string to\-be\-pushed to the client, so
-.B \-\-push\-remove route
-would remove all
-.B \-\-push route ...
-and
-.B \-\-push route\-ipv6 ...
-statements, while
-.B \-\-push\-remove 'route\-ipv6 2001:'
-would only remove IPv6 routes for 2001:... networks.
-
-.B \-\-push\-remove
-can only be used in a client\-specific context, like in a
-.B \-\-client\-config\-dir
-file, or
-.B \-\-client\-connect
-script or plugin \-\- similar to
-.B \-\-push\-reset,
-just more selective.
-
-NOTE: to
-.I change
-an option,
-.B \-\-push\-remove
-can be used to first remove the old value, and then add a new
-.B \-\-push
-option with the new value.
-.\"*********************************************************
+.B \-\-remote\-random
+When multiple \fB\-\-remote\fP address/ports are specified, or if connection
+profiles are being used, initially randomize the order of the list as a
+kind of basic load\-balancing measure.
.TP
-.B \-\-push\-peer\-info
-Push additional information about the client to server.
-The following data is always pushed to the server:
-
-IV_VER=<version> \-\- the client OpenVPN version
-
-IV_PLAT=[linux|solaris|openbsd|mac|netbsd|freebsd|win] \-\- the client OS platform
-
-IV_LZO_STUB=1 \-\- if client was built with LZO stub capability
-
-IV_LZ4=1 \-\- if the client supports LZ4 compressions.
-
-IV_PROTO=2 \-\- if the client supports peer\-id floating mechansim
-
-IV_NCP=2 \-\- negotiable ciphers, client supports
-.B \-\-cipher
-pushed by the server, a value of 2 or greater indicates client
-supports AES\-GCM\-128 and AES\-GCM\-256.
-
-IV_GUI_VER=<gui_id> <version> \-\- the UI version of a UI if one is
-running, for example "de.blinkt.openvpn 0.5.47" for the
-Android app.
-
-When
-.B \-\-push\-peer\-info
-is enabled the additional information consists of the following data:
-
-IV_HWADDR=<mac address> \-\- the MAC address of clients default gateway
-
-IV_SSL=<version string> \-\- the ssl version used by the client, e.g. "OpenSSL 1.0.2f 28 Jan 2016".
-
-IV_PLAT_VER=x.y \- the version of the operating system, e.g. 6.1 for Windows 7.
-
-UV_<name>=<value> \-\- client environment variables whose names start with "UV_"
-.\"*********************************************************
+.B \-\-remote\-random\-hostname
+Prepend a random string (6 bytes, 12 hex characters) to hostname to
+prevent DNS caching. For example, "foo.bar.gov" would be modified to
+"<random\-chars>.foo.bar.gov".
.TP
-.B \-\-disable
-Disable a particular client (based on the common name)
-from connecting. Don't use this option to disable a client
-due to key or password compromise. Use a CRL (certificate
-revocation list) instead (see the
-.B \-\-crl\-verify
-option).
-
-This option must be associated with a specific client instance,
-which means that it must be specified either in a client
-instance config file using
-.B \-\-client\-config\-dir
-or dynamically generated using a
-.B \-\-client\-connect
-script.
-.\"*********************************************************
-.TP
-.B \-\-ifconfig\-pool start\-IP end\-IP [netmask]
-Set aside a pool of subnets to be
-dynamically allocated to connecting clients, similar
-to a DHCP server. For tun\-style
-tunnels, each client will be given a /30 subnet (for
-interoperability with Windows clients). For tap\-style
-tunnels, individual addresses will be allocated, and the
-optional
-.B netmask
-parameter will also be pushed to clients.
-
-.\"*********************************************************
-.TP
-.B \-\-ifconfig\-pool\-persist file [seconds]
-Persist/unpersist ifconfig\-pool
-data to
-.B file,
-at
-.B seconds
-intervals (default=600), as well as on program startup and
-shutdown.
-
-The goal of this option is to provide a long\-term association
-between clients (denoted by their common name) and the virtual
-IP address assigned to them from the ifconfig\-pool.
-Maintaining a long\-term
-association is good for clients because it allows them
-to effectively use the
-.B \-\-persist\-tun
-option.
-
-.B file
-is a comma\-delimited ASCII file, formatted as
-<Common\-Name>,<IP\-address>.
-
-If
-.B seconds
-= 0,
-.B file
-will be treated as read\-only. This is useful if
-you would like to treat
-.B file
-as a configuration file.
-
-Note that the entries in this file are treated by OpenVPN as
-suggestions only, based on past associations between
-a common name and IP address. They do not guarantee that the given common
-name will always receive the given IP address. If you want guaranteed
-assignment, use
-.B \-\-ifconfig\-push
-
-.\"*********************************************************
+.BI \-\-resolv\-retry \ n
+If hostname resolve fails for \fB\-\-remote\fP, retry resolve for \fBn\fP
+seconds before failing.
+.sp
+Set \fBn\fP to "infinite" to retry indefinitely.
+.sp
+By default, \fB\-\-resolv\-retry infinite\fP is enabled. You can disable by
+setting n=0.
.TP
-.B \-\-ifconfig\-pool\-linear
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5
-
-Modifies the
-.B \-\-ifconfig\-pool
-directive to
-allocate individual TUN interface addresses for
-clients rather than /30 subnets. NOTE: This option
-is incompatible with Windows clients.
-
-This option is deprecated, and should be replaced with
-.B \-\-topology p2p
-which is functionally equivalent.
-.\"*********************************************************
-.TP
-.B \-\-ifconfig\-push local remote\-netmask [alias]
-Push virtual IP endpoints for client tunnel,
-overriding the \-\-ifconfig\-pool dynamic allocation.
-
-The parameters
-.B local
-and
-.B remote\-netmask
-are set according to the
-.B \-\-ifconfig
-directive which you want to execute on the client machine to
-configure the remote end of the tunnel. Note that the parameters
-.B local
-and
-.B remote\-netmask
-are from the perspective of the client, not the server. They may be
-DNS names rather than IP addresses, in which case they will be resolved
-on the server at the time of client connection.
-
-The optional
-.B alias
-parameter may be used in cases where NAT causes the client view
-of its local endpoint to differ from the server view. In this case
-.B local/remote\-netmask
-will refer to the server view while
-.B alias/remote\-netmask
-will refer to the client view.
-
-This option must be associated with a specific client instance,
-which means that it must be specified either in a client
-instance config file using
-.B \-\-client\-config\-dir
-or dynamically generated using a
-.B \-\-client\-connect
-script.
-
-Remember also to include a
-.B \-\-route
-directive in the main OpenVPN config file which encloses
-.B local,
-so that the kernel will know to route it
-to the server's TUN/TAP interface.
-
-OpenVPN's internal client IP address selection algorithm works as
-follows:
-
-.B 1
-\-\- Use
-.B \-\-client\-connect script
-generated file for static IP (first choice).
-.br
-.B 2
-\-\- Use
-.B \-\-client\-config\-dir
-file for static IP (next choice).
-.br
-.B 3
-\-\- Use
-.B \-\-ifconfig\-pool
-allocation for dynamic IP (last choice).
-.br
-.\"*********************************************************
-.TP
-.B \-\-iroute network [netmask]
-Generate an internal route to a specific
-client. The
-.B netmask
-parameter, if omitted, defaults to 255.255.255.255.
-
-This directive can be used to route a fixed subnet from
-the server to a particular client, regardless
-of where the client is connecting from. Remember
-that you must also add the route to the system
-routing table as well (such as by using the
-.B \-\-route
-directive). The reason why two routes are needed
-is that the
-.B \-\-route
-directive routes the packet from the kernel
-to OpenVPN. Once in OpenVPN, the
-.B \-\-iroute
-directive routes to the specific client.
-
-This option must be specified either in a client
-instance config file using
-.B \-\-client\-config\-dir
-or dynamically generated using a
-.B \-\-client\-connect
-script.
-
-The
-.B \-\-iroute
-directive also has an important interaction with
-.B \-\-push
-"route ...".
-.B \-\-iroute
-essentially defines a subnet which is owned by a
-particular client (we will call this client A).
-If you would like other clients to be able to reach A's
-subnet, you can use
-.B \-\-push
-"route ..."
-together with
-.B \-\-client\-to\-client
-to effect this. In order for all clients to see
-A's subnet, OpenVPN must push this route to all clients
-EXCEPT for A, since the subnet is already owned by A.
-OpenVPN accomplishes this by not
-not pushing a route to a client
-if it matches one of the client's iroutes.
-.\"*********************************************************
+.B \-\-single\-session
+After initially connecting to a remote peer, disallow any new
+connections. Using this option means that a remote peer cannot connect,
+disconnect, and then reconnect.
+.sp
+If the daemon is reset by a signal or \fB\-\-ping\-restart\fP, it will allow
+one new connection.
+.sp
+\fB\-\-single\-session\fP can be used with \fB\-\-ping\-exit\fP or \fB\-\-inactive\fP
+to create a single dynamic session that will exit when finished.
.TP
-.B \-\-client\-to\-client
-Because the OpenVPN server mode handles multiple clients
-through a single tun or tap interface, it is effectively
-a router. The
-.B \-\-client\-to\-client
-flag tells OpenVPN to internally route client\-to\-client
-traffic rather than pushing all client\-originating traffic
-to the TUN/TAP interface.
-
-When this option is used, each client will "see" the other
-clients which are currently connected. Otherwise, each
-client will only see the server. Don't use this option
-if you want to firewall tunnel traffic using
-custom, per\-client rules.
-.\"*********************************************************
+.BI \-\-server\-poll\-timeout \ n
+When connecting to a remote server do not wait for more than \fBn\fP
+seconds for a response before trying the next server. The default value
+is 120s. This timeout includes proxy and TCP connect timeouts.
+.TP
+.BI \-\-static\-challenge \ args
+Enable static challenge/response protocol
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+static\-challenge text echo
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fBtext\fP challenge text is presented to the user which describes what
+information is requested. The \fBecho\fP flag indicates if the user\(aqs
+input should be echoed on the screen. Valid \fBecho\fP values are
+\fB0\fP or \fB1\fP\&.
+.sp
+See management\-notes.txt in the OpenVPN distribution for a description of
+the OpenVPN challenge/response protocol.
+.UNINDENT
+.INDENT 0.0
.TP
-.B \-\-duplicate\-cn
-Allow multiple clients with the same common name to concurrently connect.
-In the absence of this option, OpenVPN will disconnect a client instance
-upon connection of a new client having the same common name.
-.\"*********************************************************
-.TP
-.B \-\-client\-connect cmd
-Run
-.B command cmd
-on client connection.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-The command is passed the common name
-and IP address of the just\-authenticated client
-as environmental variables (see environmental variable section
-below). The command is also passed
-the pathname of a freshly created temporary file as the last argument
-(after any arguments specified in
-.B cmd
-), to be used by the command
-to pass dynamically generated config file directives back to OpenVPN.
-
-If the script wants to generate a dynamic config file
-to be applied on the server when the client connects,
-it should write it to the file named by the last argument.
-
-See the
-.B \-\-client\-config\-dir
-option below for options which
-can be legally used in a dynamically generated config file.
-
-Note that the return value of
-.B script
-is significant. If
-.B script
-returns a non\-zero error status, it will cause the client
-to be disconnected.
-.\"*********************************************************
-.TP
-.B \-\-client\-disconnect cmd
-Like
-.B \-\-client\-connect
-but called on client instance shutdown. Will not be called
-unless the
-.B \-\-client\-connect
-script and plugins (if defined)
-were previously called on this instance with
-successful (0) status returns.
-
-The exception to this rule is if the
-.B \-\-client\-disconnect
-command or plugins are cascaded, and at least one client\-connect
-function succeeded, then ALL of the client\-disconnect functions for
-scripts and plugins will be called on client instance object deletion,
-even in cases where some of the related client\-connect functions returned
-an error status.
-
-The
-.B \-\-client\-disconnect
-command is passed the same pathname as the corresponding
-.B \-\-client\-connect
-command as its last argument. (after any arguments specified in
-.B cmd
-).
-.B
-.\"*********************************************************
-.TP
-.B \-\-client\-config\-dir dir
-Specify a directory
-.B dir
-for custom client config files. After
-a connecting client has been authenticated, OpenVPN will
-look in this directory for a file having the same name
-as the client's X509 common name. If a matching file
-exists, it will be opened and parsed for client\-specific
-configuration options. If no matching file is found, OpenVPN
-will instead try to open and parse a default file called
-"DEFAULT", which may be provided but is not required. Note that
-the configuration files must be readable by the OpenVPN process
-after it has dropped it's root privileges.
-
-This file can specify a fixed IP address for a given
-client using
-.B \-\-ifconfig\-push,
-as well as fixed subnets owned by the client using
-.B \-\-iroute.
-
-One of the useful properties of this option is that it
-allows client configuration files to be conveniently
-created, edited, or removed while the server is live,
-without needing to restart the server.
-
-The following
-options are legal in a client\-specific context:
-.B \-\-push, \-\-push\-reset, \-\-push\-remove, \-\-iroute, \-\-ifconfig\-push,
-and
-.B \-\-config.
-.\"*********************************************************
+.B \-\-show\-proxy\-settings
+Show sensed HTTP or SOCKS proxy settings. Currently, only Windows
+clients support this option.
+.TP
+.BI \-\-http\-proxy \ args
+Connect to remote host through an HTTP proxy. This requires at least an
+address \fBserver\fP and \fBport\fP argument. If HTTP Proxy\-Authenticate
+is required, a file name to an \fBauthfile\fP file containing a username
+and password on 2 lines can be given, or \fBstdin\fP to prompt from
+console. Its content can also be specified in the config file with the
+\fB\-\-http\-proxy\-user\-pass\fP option. (See section on inline files)
+.sp
+The last optional argument is an \fBauth\-method\fP which should be one
+of \fBnone\fP, \fBbasic\fP, or \fBntlm\fP\&.
+.sp
+HTTP Digest authentication is supported as well, but only via the
+\fBauto\fP or \fBauto\-nct\fP flags (below). This must replace
+the \fBauthfile\fP argument.
+.sp
+The \fBauto\fP flag causes OpenVPN to automatically determine the
+\fBauth\-method\fP and query stdin or the management interface for
+username/password credentials, if required. This flag exists on OpenVPN
+2.1 or higher.
+.sp
+The \fBauto\-nct\fP flag (no clear\-text auth) instructs OpenVPN to
+automatically determine the authentication method, but to reject weak
+authentication protocols such as HTTP Basic Authentication.
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+http\-proxy proxy.example.net 3128
+http\-proxy proxy.example.net 3128 authfile.txt
+http\-proxy proxy.example.net 3128 stdin
+http\-proxy proxy.example.net 3128 auto basic
+http\-proxy proxy.example.net 3128 auto\-nct ntlm
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-http\-proxy\-option \ args
+Set extended HTTP proxy options. Requires an option \fBtype\fP as argument
+and an optional \fBparameter\fP to the type. Repeat to set multiple
+options.
+.INDENT 7.0
+.TP
+.B \fBVERSION\fP \fBversion\fP
+Set HTTP version number to \fBversion\fP (default \fB1.0\fP).
+.TP
+.B \fBAGENT\fP \fBuser\-agent\fP
+Set HTTP "User\-Agent" string to \fBuser\-agent\fP\&.
+.TP
+.B \fBCUSTOM\-HEADER\fP \fBname\fP \fBcontent\fP
+Adds the custom Header with \fBname\fP as name and \fBcontent\fP as
+the content of the custom HTTP header.
+.UNINDENT
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+http\-proxy\-option VERSION 1.1
+http\-proxy\-option AGENT OpenVPN/2.4
+http\-proxy\-option X\-Proxy\-Flag some\-flags
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-socks\-proxy \ args
+Connect to remote host through a Socks5 proxy. A required \fBserver\fP
+argument is needed. Optionally a \fBport\fP (default \fB1080\fP) and
+\fBauthfile\fP can be given. The \fBauthfile\fP is a file containing a
+username and password on 2 lines, or \fBstdin\fP can be used to
+prompt from console.
+.UNINDENT
+.SS Server Options
+.sp
+Starting with OpenVPN 2.0, a multi\-client TCP/UDP server mode is
+supported, and can be enabled with the \fB\-\-mode server\fP option. In
+server mode, OpenVPN will listen on a single port for incoming client
+connections. All client connections will be routed through a single tun
+or tap interface. This mode is designed for scalability and should be
+able to support hundreds or even thousands of clients on sufficiently
+fast hardware. SSL/TLS authentication must be used in this mode.
+.INDENT 0.0
+.TP
+.BI \-\-auth\-gen\-token \ args
+Returns an authentication token to successfully authenticated clients.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+auth\-gen\-token [lifetime] [external\-auth]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+After successful user/password authentication, the OpenVPN server will
+with this option generate a temporary authentication token and push that
+to the client. On the following renegotiations, the OpenVPN client will pass
+this token instead of the users password. On the server side the server
+will do the token authentication internally and it will NOT do any
+additional authentications against configured external user/password
+authentication mechanisms.
+.sp
+The tokens implemented by this mechanism include an initial timestamp and
+a renew timestamp and are secured by HMAC.
+.sp
+The \fBlifetime\fP argument defines how long the generated token is valid.
+The lifetime is defined in seconds. If lifetime is not set or it is set
+to \fB0\fP, the token will never expire.
+.sp
+The token will expire either after the configured \fBlifetime\fP of the
+token is reached or after not being renewed for more than 2 *
+\fBreneg\-sec\fP seconds. Clients will be sent renewed tokens on every TLS
+renogiation to keep the client\(aqs token updated. This is done to
+invalidate a token if a client is disconnected for a sufficently long
+time, while at the same time permitting much longer token lifetimes for
+active clients.
+.sp
+This feature is useful for environments which are configured to use One
+Time Passwords (OTP) as part of the user/password authentications and
+that authentication mechanism does not implement any auth\-token support.
+.sp
+When the \fBexternal\-auth\fP keyword is present the normal
+authentication method will always be called even if auth\-token succeeds.
+Normally other authentications method are skipped if auth\-token
+verification suceeds or fails.
+.sp
+This option postpones this decision to the external authentication
+methods and checks the validity of the account and do other checks.
+.sp
+In this mode the environment will have a \fBsession_id\fP variable that
+holds the session id from auth\-gen\-token. Also an environment variable
+\fBsession_state\fP is present. This variable indicates whether the
+auth\-token has succeeded or not. It can have the following values:
+.INDENT 7.0
+.TP
+.B \fBInitial\fP
+No token from client.
+.TP
+.B \fBAuthenticated\fP
+Token is valid and not expired.
+.TP
+.B \fBExpired\fP
+Token is valid but has expired.
+.TP
+.B \fBInvalid\fP
+Token is invalid (failed HMAC or wrong length)
+.TP
+.B \fBAuthenticatedEmptyUser\fP / \fBExpiredEmptyUser\fP
+The token is not valid with the username sent from the client but
+would be valid (or expired) if we assume an empty username was
+used instead. These two cases are a workaround for behaviour in
+OpenVPN 3. If this workaround is not needed these two cases should
+be handled in the same way as \fBInvalid\fP\&.
+.UNINDENT
+.sp
+\fBWarning:\fP Use this feature only if you want your authentication
+method called on every verification. Since the external authentication
+is called it needs to also indicate a success or failure of the
+authentication. It is strongly recommended to return an authentication
+failure in the case of the Invalid/Expired auth\-token with the
+external\-auth option unless the client could authenticate in another
+acceptable way (e.g. client certificate), otherwise returning success
+will lead to authentication bypass (as does returning success on a wrong
+password from a script).
+.TP
+.BI \-\-auth\-gen\-token\-secret \ file
+Specifies a file that holds a secret for the HMAC used in
+\fB\-\-auth\-gen\-token\fP If \fBfile\fP is not present OpenVPN will generate a
+random secret on startup. This file should be used if auth\-token should
+validate after restarting a server or if client should be able to roam
+between multiple OpenVPN servers with their auth\-token.
+.TP
+.B \-\-auth\-user\-pass\-optional
+Allow connections by clients that do not specify a username/password.
+Normally, when \fB\-\-auth\-user\-pass\-verify\fP or
+\fB\-\-management\-client\-auth\fP are specified (or an authentication plugin
+module), the OpenVPN server daemon will require connecting clients to
+specify a username and password. This option makes the submission of a
+username/password by clients optional, passing the responsibility to the
+user\-defined authentication module/script to accept or deny the client
+based on other factors (such as the setting of X509 certificate fields).
+When this option is used, and a connecting client does not submit a
+username/password, the user\-defined authentication module/script will
+see the username and password as being set to empty strings (""). The
+authentication module/script MUST have logic to detect this condition
+and respond accordingly.
.TP
.B \-\-ccd\-exclusive
-Require, as a
-condition of authentication, that a connecting client has a
-.B \-\-client\-config\-dir
-file.
-.\"*********************************************************
-.TP
-.B \-\-tmp\-dir dir
-Specify a directory
-.B dir
-for temporary files. This directory will be used by
-openvpn processes and script to communicate temporary
-data with openvpn main process. Note that
-the directory must be writable by the OpenVPN process
-after it has dropped it's root privileges.
-
-This directory will be used by in the following cases:
-
-*
-.B \-\-client\-connect
-scripts to dynamically generate client\-specific
-configuration files.
-
-*
-.B OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
-plugin hook to return success/failure via auth_control_file
-when using deferred auth method
-
-*
-.B OPENVPN_PLUGIN_ENABLE_PF
-plugin hook to pass filtering rules via pf_file
-.\"*********************************************************
-.TP
-.B \-\-hash\-size r v
-Set the size of the real address hash table to
-.B r
-and the virtual address table to
-.B v.
-By default, both tables are sized at 256 buckets.
-.\"*********************************************************
+Require, as a condition of authentication, that a connecting client has
+a \fB\-\-client\-config\-dir\fP file.
+.TP
+.BI \-\-client\-config\-dir \ dir
+Specify a directory \fBdir\fP for custom client config files. After a
+connecting client has been authenticated, OpenVPN will look in this
+directory for a file having the same name as the client\(aqs X509 common
+name. If a matching file exists, it will be opened and parsed for
+client\-specific configuration options. If no matching file is found,
+OpenVPN will instead try to open and parse a default file called
+"DEFAULT", which may be provided but is not required. Note that the
+configuration files must be readable by the OpenVPN process after it has
+dropped it\(aqs root privileges.
+.sp
+This file can specify a fixed IP address for a given client using
+\fB\-\-ifconfig\-push\fP, as well as fixed subnets owned by the client using
+\fB\-\-iroute\fP\&.
+.sp
+One of the useful properties of this option is that it allows client
+configuration files to be conveniently created, edited, or removed while
+the server is live, without needing to restart the server.
+.sp
+The following options are legal in a client\-specific context: \fB\-\-push\fP,
+\fB\-\-push\-reset\fP, \fB\-\-push\-remove\fP, \fB\-\-iroute\fP, \fB\-\-ifconfig\-push\fP,
+\fB\-\-vlan\-pvid\fP and \fB\-\-config\fP\&.
.TP
-.B \-\-bcast\-buffers n
-Allocate
-.B n
-buffers for broadcast datagrams (default=256).
-.\"*********************************************************
+.B \-\-client\-to\-client
+Because the OpenVPN server mode handles multiple clients through a
+single tun or tap interface, it is effectively a router. The
+\fB\-\-client\-to\-client\fP flag tells OpenVPN to internally route
+client\-to\-client traffic rather than pushing all client\-originating
+traffic to the TUN/TAP interface.
+.sp
+When this option is used, each client will "see" the other clients which
+are currently connected. Otherwise, each client will only see the
+server. Don\(aqt use this option if you want to firewall tunnel traffic
+using custom, per\-client rules.
.TP
-.B \-\-tcp\-queue\-limit n
-Maximum number of output packets queued before TCP (default=64).
-
-When OpenVPN is tunneling data from a TUN/TAP device to a
-remote client over a TCP connection, it is possible that the TUN/TAP device
-might produce data at a faster rate than the TCP connection
-can support. When the number of output packets queued before sending to
-the TCP socket reaches this limit for a given client connection,
-OpenVPN will start to drop outgoing packets directed
-at this client.
-.\"*********************************************************
+.B \-\-disable
+Disable a particular client (based on the common name) from connecting.
+Don\(aqt use this option to disable a client due to key or password
+compromise. Use a CRL (certificate revocation list) instead (see the
+\fB\-\-crl\-verify\fP option).
+.sp
+This option must be associated with a specific client instance, which
+means that it must be specified either in a client instance config file
+using \fB\-\-client\-config\-dir\fP or dynamically generated using a
+\fB\-\-client\-connect\fP script.
+.TP
+.BI \-\-connect\-freq \ args
+Allow a maximum of \fBn\fP new connections per \fBsec\fP seconds from
+clients.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+connect\-freq n sec
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This is designed to contain DoS attacks which flood the server
+with connection requests using certificates which will ultimately fail
+to authenticate.
+.sp
+This is an imperfect solution however, because in a real DoS scenario,
+legitimate connections might also be refused.
+.sp
+For the best protection against DoS attacks in server mode, use
+\fB\-\-proto udp\fP and either \fB\-\-tls\-auth\fP or \fB\-\-tls\-crypt\fP\&.
.TP
-.B \-\-tcp\-nodelay
-This macro sets the TCP_NODELAY socket flag on the server
-as well as pushes it to connecting clients. The TCP_NODELAY
-flag disables the Nagle algorithm on TCP sockets causing
-packets to be transmitted immediately with low latency,
-rather than waiting a short period of time in order
-to aggregate several packets into a larger containing
-packet. In VPN applications over TCP, TCP_NODELAY
-is generally a good latency optimization.
-
-The macro expands as follows:
-
+.B \-\-duplicate\-cn
+Allow multiple clients with the same common name to concurrently
+connect. In the absence of this option, OpenVPN will disconnect a client
+instance upon connection of a new client having the same common name.
+.TP
+.BI \-\-ifconfig\-pool \ args
+Set aside a pool of subnets to be dynamically allocated to connecting
+clients, similar to a DHCP server.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
- if mode server:
- socket\-flags TCP_NODELAY
- push "socket\-flags TCP_NODELAY"
-.in -4
-.ft
+.ft C
+ifconfig\-pool start\-IP end\-IP [netmask]
+.ft P
.fi
-.\"*********************************************************
-.TP
-.B \-\-max\-clients n
-Limit server to a maximum of
-.B n
-concurrent clients.
-.\"*********************************************************
-.TP
-.B \-\-max\-routes\-per\-client n
-Allow a maximum of
-.B n
-internal routes per client (default=256).
-This is designed to
-help contain DoS attacks where an authenticated client floods the
-server with packets appearing to come from many unique MAC addresses,
-forcing the server to deplete
-virtual memory as its internal routing table expands.
-This directive can be used in a
-.B \-\-client\-config\-dir
-file or auto\-generated by a
-.B \-\-client\-connect
-script to override the global value for a particular client.
-
-Note that this
-directive affects OpenVPN's internal routing table, not the
-kernel routing table.
-.\"*********************************************************
-.TP
-.B \-\-stale\-routes\-check n [t]
-Remove routes haven't had activity for
-.B n
-seconds (i.e. the ageing time).
-
-This check is ran every
-.B t
-seconds (i.e. check interval).
-
-If
-.B t
-is not present it defaults to
-.B n
-
-This option helps to keep the dynamic routing table small.
-See also
-.B \-\-max\-routes\-per\-client
-.\"*********************************************************
-.TP
-.B \-\-connect\-freq n sec
-Allow a maximum of
-.B n
-new connections per
-.B sec
-seconds from clients. This is designed to contain DoS attacks which flood
-the server with connection requests using certificates which
-will ultimately fail to authenticate.
-
-This is an imperfect solution however, because in a real
-DoS scenario, legitimate connections might also be refused.
-
-For the best protection against DoS attacks in server mode,
-use
-.B \-\-proto udp
-and either
-.B \-\-tls\-auth
-or
-.B \-\-tls\-crypt\fR.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+For tun\-style tunnels, each client
+will be given a /30 subnet (for interoperability with Windows clients).
+For tap\-style tunnels, individual addresses will be allocated, and the
+optional \fBnetmask\fP parameter will also be pushed to clients.
+.TP
+.BI \-\-ifconfig\-ipv6\-pool \ args
+Specify an IPv6 address pool for dynamic assignment to clients.
+.sp
+Valid args:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ifconfig\-ipv6\-pool ipv6addr/bits
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The pool starts at \fBipv6addr\fP and matches the offset determined from
+the start of the IPv4 pool. If the host part of the given IPv6
+address is \fB0\fP, the pool starts at \fBipv6addr\fP +1.
+.TP
+.BI \-\-ifconfig\-pool\-persist \ args
+Persist/unpersist ifconfig\-pool data to \fBfile\fP, at \fBseconds\fP
+intervals (default \fB600\fP), as well as on program startup and shutdown.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ifconfig\-pool\-persist file [seconds]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The goal of this option is to provide a long\-term association between
+clients (denoted by their common name) and the virtual IP address
+assigned to them from the ifconfig\-pool. Maintaining a long\-term
+association is good for clients because it allows them to effectively
+use the \fB\-\-persist\-tun\fP option.
+.sp
+\fBfile\fP is a comma\-delimited ASCII file, formatted as
+\fB<Common\-Name>,<IP\-address>\fP\&.
+.sp
+If \fBseconds\fP = \fB0\fP, \fBfile\fP will be treated as read\-only. This
+is useful if you would like to treat \fBfile\fP as a configuration file.
+.sp
+Note that the entries in this file are treated by OpenVPN as
+\fIsuggestions\fP only, based on past associations between a common name and
+IP address. They do not guarantee that the given common name will always
+receive the given IP address. If you want guaranteed assignment, use
+\fB\-\-ifconfig\-push\fP
+.TP
+.BI \-\-ifconfig\-push \ args
+Push virtual IP endpoints for client tunnel, overriding the
+\fB\-\-ifconfig\-pool\fP dynamic allocation.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ifconfig\-push local remote\-netmask [alias]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The parameters \fBlocal\fP and \fBremote\-netmask\fP are set according to the
+\fB\-\-ifconfig\fP directive which you want to execute on the client machine
+to configure the remote end of the tunnel. Note that the parameters
+\fBlocal\fP and \fBremote\-netmask\fP are from the perspective of the client,
+not the server. They may be DNS names rather than IP addresses, in which
+case they will be resolved on the server at the time of client
+connection.
+.sp
+The optional \fBalias\fP parameter may be used in cases where NAT causes
+the client view of its local endpoint to differ from the server view. In
+this case \fBlocal/remote\-netmask\fP will refer to the server view while
+\fBalias/remote\-netmask\fP will refer to the client view.
+.sp
+This option must be associated with a specific client instance, which
+means that it must be specified either in a client instance config file
+using \fB\-\-client\-config\-dir\fP or dynamically generated using a
+\fB\-\-client\-connect\fP script.
+.sp
+Remember also to include a \fB\-\-route\fP directive in the main OpenVPN
+config file which encloses \fBlocal\fP, so that the kernel will know to
+route it to the server\(aqs TUN/TAP interface.
+.sp
+OpenVPN\(aqs internal client IP address selection algorithm works as
+follows:
+.INDENT 7.0
+.IP 1. 3
+Use \fB\-\-client\-connect script\fP generated file for static IP
+(first choice).
+.IP 2. 3
+Use \fB\-\-client\-config\-dir\fP file for static IP (next choice).
+.IP 3. 3
+Use \fB\-\-ifconfig\-pool\fP allocation for dynamic IP (last
+choice).
+.UNINDENT
+.TP
+.BI \-\-ifconfig\-ipv6\-push \ args
+for \fB\-\-client\-config\-dir\fP per\-client static IPv6 interface
+configuration, see \fB\-\-client\-config\-dir\fP and \fB\-\-ifconfig\-push\fP for
+more details.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ifconfig\-ipv6\-push ipv6addr/bits ipv6remote
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-inetd \ args
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+inetd
+inetd wait
+inetd nowait
+inetd wait progname
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Use this option when OpenVPN is being run from the inetd or \fBxinetd\fP(8)
+server.
+.sp
+The \fBwait\fP and \fBnowait\fP option must match what is specified
+in the inetd/xinetd config file. The \fBnowait\fP mode can only be used
+with \fB\-\-proto tcp\-server\fP The default is \fBwait\fP\&. The
+\fBnowait\fP mode can be used to instantiate the OpenVPN daemon as a
+classic TCP server, where client connection requests are serviced on a
+single port number. For additional information on this kind of
+configuration, see the OpenVPN FAQ:
+\fI\%https://community.openvpn.net/openvpn/wiki/325\-openvpn\-as\-a\-\-forking\-tcp\-server\-which\-can\-service\-multiple\-clients\-over\-a\-single\-tcp\-port\fP
+.sp
+This option precludes the use of \fB\-\-daemon\fP, \fB\-\-local\fP or
+\fB\-\-remote\fP\&. Note that this option causes message and error output to
+be handled in the same way as the \fB\-\-daemon\fP option. The optional
+\fBprogname\fP parameter is also handled exactly as in \fB\-\-daemon\fP\&.
+.sp
+Also note that in \fBwait\fP mode, each OpenVPN tunnel requires a separate
+TCP/UDP port and a separate inetd or xinetd entry. See the OpenVPN 1.x
+HOWTO for an example on using OpenVPN with xinetd:
+\fI\%https://openvpn.net/community\-resources/1xhowto/\fP
.TP
-.B \-\-learn\-address cmd
-Run command
-.B cmd
-to validate client virtual addresses or routes.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-Three arguments will be appended to any arguments in
-.B cmd
-as follows:
-
-.B [1] operation \-\-
-"add", "update", or "delete" based on whether or not
-the address is being added to, modified, or deleted from
-OpenVPN's internal routing table.
-.br
-.B [2] address \-\-
-The address being learned or unlearned. This can be
-an IPv4 address such as "198.162.10.14", an IPv4 subnet
-such as "198.162.10.0/24", or an ethernet MAC address (when
-.B \-\-dev tap
-is being used) such as "00:FF:01:02:03:04".
-.br
-.B [3] common name \-\-
-The common name on the certificate associated with the
-client linked to this address. Only present for "add"
-or "update" operations, not "delete".
-
-On "add" or "update" methods, if the script returns
-a failure code (non\-zero), OpenVPN will reject the address
-and will not modify its internal routing table.
-
-Normally, the
-.B cmd
-script will use the information provided above to set
-appropriate firewall entries on the VPN TUN/TAP interface.
-Since OpenVPN provides the association between virtual IP
-or MAC address and the client's authenticated common name,
-it allows a user\-defined script to configure firewall access
-policies with regard to the client's high\-level common name,
-rather than the low level client virtual addresses.
-.\"*********************************************************
-.TP
-.B \-\-auth\-user\-pass\-verify cmd method
-Require the client to provide a username/password (possibly
-in addition to a client certificate) for authentication.
-
-OpenVPN will run
-.B command cmd
-to validate the username/password
-provided by the client.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-If
-.B method
-is set to "via\-env", OpenVPN will call
-.B script
-with the environmental variables
-.B username
-and
-.B password
-set to the username/password strings provided by the client.
-Be aware that this method is insecure on some platforms which
-make the environment of a process publicly visible to other
-unprivileged processes.
-
-If
-.B method
-is set to "via\-file", OpenVPN will write the username and
-password to the first two lines of a temporary file. The filename
-will be passed as an argument to
-.B script,
-and the file will be automatically deleted by OpenVPN after
-the script returns. The location of the temporary file is
-controlled by the
-.B \-\-tmp\-dir
-option, and will default to the current directory if unspecified.
-For security, consider setting
-.B \-\-tmp\-dir
-to a volatile storage medium such as
-.B /dev/shm
-(if available) to prevent the username/password file from touching the hard drive.
-
-The script should examine the username
-and password,
-returning a success exit code (0) if the
-client's authentication request is to be accepted, or a failure
-code (1) to reject the client.
-
-This directive is designed to enable a plugin\-style interface
-for extending OpenVPN's authentication capabilities.
-
-To protect against a client passing a maliciously formed
-username or password string, the username string must
-consist only of these characters: alphanumeric, underbar
-('_'), dash ('\-'), dot ('.'), or at ('@'). The password
-string can consist of any printable characters except for
-CR or LF. Any illegal characters in either the username
-or password string will be converted to underbar ('_').
-
-Care must be taken by any user\-defined scripts to avoid
-creating a security vulnerability in the way that these
-strings are handled. Never use these strings in such a way
-that they might be escaped or evaluated by a shell interpreter.
-
-For a sample script that performs PAM authentication, see
-.B sample\-scripts/auth\-pam.pl
-in the OpenVPN source distribution.
-.\"*********************************************************
-.TP
-.B \-\-auth\-gen\-token [lifetime]
-After successful user/password authentication, the OpenVPN
-server will with this option generate a temporary
-authentication token and push that to client. On the following
-renegotiations, the OpenVPN client will pass this token instead
-of the users password. On the server side the server will do
-the token authentication internally and it will NOT do any
-additional authentications against configured external
-user/password authentication mechanisms.
-
-The
-.B lifetime
-argument defines how long the generated token is valid. The
-lifetime is defined in seconds. If lifetime is not set
-or it is set to 0, the token will never expire.
-
-This feature is useful for environments which is configured
-to use One Time Passwords (OTP) as part of the user/password
-authentications and that authentication mechanism does not
-implement any auth\-token support.
-.\"*********************************************************
+.B \-\-multihome
+Configure a multi\-homed UDP server. This option needs to be used when a
+server has more than one IP address (e.g. multiple interfaces, or
+secondary IP addresses), and is not using \fB\-\-local\fP to force binding
+to one specific address only. This option will add some extra lookups to
+the packet path to ensure that the UDP reply packets are always sent
+from the address that the client is talking to. This is not supported on
+all platforms, and it adds more processing, so it\(aqs not enabled by
+default.
+.INDENT 7.0
+.TP
+.B \fINotes:\fP
+.INDENT 7.0
+.IP \(bu 2
+This option is only relevant for UDP servers.
+.IP \(bu 2
+If you do an IPv6+IPv4 dual\-stack bind on a Linux machine with
+multiple IPv4 address, connections to IPv4 addresses will not
+work right on kernels before 3.15, due to missing kernel
+support for the IPv4\-mapped case (some distributions have
+ported this to earlier kernel versions, though).
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-iroute \ args
+Generate an internal route to a specific client. The \fBnetmask\fP
+parameter, if omitted, defaults to \fB255.255.255.255\fP\&.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+iroute network [netmask]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This directive can be used to route a fixed subnet from the server to a
+particular client, regardless of where the client is connecting from.
+Remember that you must also add the route to the system routing table as
+well (such as by using the \fB\-\-route\fP directive). The reason why two
+routes are needed is that the \fB\-\-route\fP directive routes the packet
+from the kernel to OpenVPN. Once in OpenVPN, the \fB\-\-iroute\fP directive
+routes to the specific client.
+.sp
+This option must be specified either in a client instance config file
+using \fB\-\-client\-config\-dir\fP or dynamically generated using a
+\fB\-\-client\-connect\fP script.
+.sp
+The \fB\-\-iroute\fP directive also has an important interaction with
+\fB\-\-push "route ..."\fP\&. \fB\-\-iroute\fP essentially defines a subnet which
+is owned by a particular client (we will call this client \fIA\fP). If you
+would like other clients to be able to reach \fIA\fP\(aqs subnet, you can use
+\fB\-\-push "route ..."\fP together with \fB\-\-client\-to\-client\fP to effect
+this. In order for all clients to see \fIA\fP\(aqs subnet, OpenVPN must push
+this route to all clients EXCEPT for \fIA\fP, since the subnet is already
+owned by \fIA\fP\&. OpenVPN accomplishes this by not not pushing a route to
+a client if it matches one of the client\(aqs iroutes.
+.TP
+.BI \-\-iroute\-ipv6 \ args
+for \fB\-\-client\-config\-dir\fP per\-client static IPv6 route configuration,
+see \fB\-\-iroute\fP for more details how to setup and use this, and how
+\fB\-\-iroute\fP and \fB\-\-route\fP interact.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+iroute\-ipv6 ipv6addr/bits
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-max\-clients \ n
+Limit server to a maximum of \fBn\fP concurrent clients.
+.TP
+.BI \-\-max\-routes\-per\-client \ n
+Allow a maximum of \fBn\fP internal routes per client (default
+\fB256\fP). This is designed to help contain DoS attacks where an
+authenticated client floods the server with packets appearing to come
+from many unique MAC addresses, forcing the server to deplete virtual
+memory as its internal routing table expands. This directive can be used
+in a \fB\-\-client\-config\-dir\fP file or auto\-generated by a
+\fB\-\-client\-connect\fP script to override the global value for a particular
+client.
+.sp
+Note that this directive affects OpenVPN\(aqs internal routing table, not
+the kernel routing table.
.TP
.B \-\-opt\-verify
-Clients that connect with options that are incompatible
-with those of the server will be disconnected.
-
-Options that will be compared for compatibility include
-dev\-type, link\-mtu, tun\-mtu, proto, ifconfig,
-comp\-lzo, fragment, keydir, cipher, auth, keysize, secret,
-no\-replay, no\-iv, tls\-auth, key\-method, tls\-server, and tls\-client.
-
-This option requires that
-.B \-\-disable\-occ
-NOT be used.
-.\"*********************************************************
-.TP
-.B \-\-auth\-user\-pass\-optional
-Allow connections by clients that do not specify a username/password.
-Normally, when
-.B \-\-auth\-user\-pass\-verify
-or
-.B \-\-management\-client\-auth
-is specified (or an authentication plugin module), the
-OpenVPN server daemon will require connecting clients to specify a
-username and password. This option makes the submission of a username/password
-by clients optional, passing the responsibility to the user\-defined authentication
-module/script to accept or deny the client based on other factors
-(such as the setting of X509 certificate fields). When this option is used,
-and a connecting client does not submit a username/password, the user\-defined
-authentication module/script will see the username and password as being set
-to empty strings (""). The authentication module/script MUST have logic
-to detect this condition and respond accordingly.
-.\"*********************************************************
-.TP
-.B \-\-client\-cert\-not\-required
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5
-
-Don't require client certificate, client will authenticate
-using username/password only. Be aware that using this directive
-is less secure than requiring certificates from all clients.
-
-.B Please note:
-This is replaced by
-.B \-\-verify\-client\-cert
-which allows for more flexibility. The option
-.B \-\-verify\-client\-cert none
-is functionally equivalent to
-.B \-\-client\-cert\-not\-required
-.
-
-.\"*********************************************************
+Clients that connect with options that are incompatible with those of the
+server will be disconnected.
+.sp
+Options that will be compared for compatibility include \fBdev\-type\fP,
+\fBlink\-mtu\fP, \fBtun\-mtu\fP, \fBproto\fP, \fBifconfig\fP,
+\fBcomp\-lzo\fP, \fBfragment\fP, \fBkeydir\fP, \fBcipher\fP,
+\fBauth\fP, \fBkeysize\fP, \fBsecret\fP, \fBno\-replay\fP,
+\fBtls\-auth\fP, \fBkey\-method\fP, \fBtls\-server\fP
+and \fBtls\-client\fP\&.
+.sp
+This option requires that \fB\-\-disable\-occ\fP NOT be used.
+.TP
+.BI \-\-port\-share \ args
+Share OpenVPN TCP with another service
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+port\-share host port [dir]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+When run in TCP server mode, share the OpenVPN port with another
+application, such as an HTTPS server. If OpenVPN senses a connection to
+its port which is using a non\-OpenVPN protocol, it will proxy the
+connection to the server at \fBhost\fP:\fBport\fP\&. Currently only designed to
+work with HTTP/HTTPS, though it would be theoretically possible to
+extend to other protocols such as ssh.
+.sp
+\fBdir\fP specifies an optional directory where a temporary file with name
+N containing content C will be dynamically generated for each proxy
+connection, where N is the source IP:port of the client connection and C
+is the source IP:port of the connection to the proxy receiver. This
+directory can be used as a dictionary by the proxy receiver to determine
+the origin of the connection. Each generated file will be automatically
+deleted when the proxied connection is torn down.
+.sp
+Not implemented on Windows.
.TP
-.B \-\-verify\-client\-cert none|optional|require
-Specify whether the client is required to supply a valid certificate.
-
-Possible options are
-
-.B none
-: a client certificate is not required. the client need to authenticate
-using username/password only. Be aware that using this directive
-is less secure than requiring certificates from all clients.
-
-If you use this directive, the
-entire responsibility of authentication will rest on your
-.B \-\-auth\-user\-pass\-verify
-script, so keep in mind that bugs in your script
-could potentially compromise the security of your VPN.
-
-.B \-\-verify\-client\-cert none
-is functionally equivalent to
-.B \-\-client\-cert\-not\-required.
-
-.B optional
-: a client may present a certificate but it is not required to do so.
-When using this directive, you should also use a
-.B \-\-auth\-user\-pass\-verify
-script to ensure that clients are authenticated using a
-certificate, a username and password, or possibly even both.
-
-Again, the entire responsibility of authentication will rest on your
-.B \-\-auth\-user\-pass\-verify
-script, so keep in mind that bugs in your script
-could potentially compromise the security of your VPN.
-
-.B require
-: this is the default option. A client is required to present a
-certificate, otherwise VPN access is refused.
-
-If you don't use this directive (or use
-.B \-\-verify\-client\-cert require
-) but you also specify an
-.B \-\-auth\-user\-pass\-verify
-script, then OpenVPN will perform double authentication. The
-client certificate verification AND the
-.B \-\-auth\-user\-pass\-verify
-script will need to succeed in order for a client to be
-authenticated and accepted onto the VPN.
-.\"*********************************************************
+.BI \-\-push \ option
+Push a config file option back to the client for remote execution. Note
+that \fBoption\fP must be enclosed in double quotes (\fB""\fP). The
+client must specify \fB\-\-pull\fP in its config file. The set of options
+which can be pushed is limited by both feasibility and security. Some
+options such as those which would execute scripts are banned, since they
+would effectively allow a compromised server to execute arbitrary code
+on the client. Other options such as TLS or MTU parameters cannot be
+pushed because the client needs to know them before the connection to the
+server can be initiated.
+.sp
+This is a partial list of options which can currently be pushed:
+\fB\-\-route\fP, \fB\-\-route\-gateway\fP, \fB\-\-route\-delay\fP,
+\fB\-\-redirect\-gateway\fP, \fB\-\-ip\-win32\fP, \fB\-\-dhcp\-option\fP,
+\fB\-\-inactive\fP, \fB\-\-ping\fP, \fB\-\-ping\-exit\fP, \fB\-\-ping\-restart\fP,
+\fB\-\-setenv\fP, \fB\-\-auth\-token\fP, \fB\-\-persist\-key\fP, \fB\-\-persist\-tun\fP,
+\fB\-\-echo\fP, \fB\-\-comp\-lzo\fP, \fB\-\-socket\-flags\fP, \fB\-\-sndbuf\fP,
+\fB\-\-rcvbuf\fP
.TP
-.B \-\-username\-as\-common\-name
-For
-.B \-\-auth\-user\-pass\-verify
-authentication, use
-the authenticated username as the common name,
-rather than the common name from the client cert.
-.\"*********************************************************
-.TP
-.B \-\-compat\-names [no\-remapping]
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5
-
-Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted
-like this:
-.IP
-.B
-/C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com
-.IP
-In addition the old behaviour was to remap any character other than
-alphanumeric, underscore ('_'), dash ('\-'), dot ('.'), and slash ('/') to
-underscore ('_'). The X.509 Subject string as returned by the
-.B tls_id
-environmental variable, could additionally contain colon (':') or equal ('=').
-.IP
-When using the
-.B \-\-compat\-names
-option, this old formatting and remapping will be re\-enabled again. This is
-purely implemented for compatibility reasons when using older plug\-ins or
-scripts which does not handle the new formatting or UTF\-8 characters.
-.IP
-In OpenVPN 2.3 the formatting of these fields changed into a more
-standardised format. It now looks like:
-.IP
-.B
-C=US, L=Somewhere, CN=John Doe, emailAddress=john@example.com
-.IP
-The new default format in OpenVPN 2.3 also does not do the character remapping
-which happened earlier. This new format enables proper support for UTF\-8
-characters in the usernames, X.509 Subject fields and Common Name variables and
-it complies to the RFC 2253, UTF\-8 String Representation of Distinguished
-Names.
-
-The
-.B no\-remapping
-mode flag can be used with the
-.B
-\-\-compat\-names
-option to be compatible with the now deprecated \-\-no\-name\-remapping option.
-It is only available at the server. When this mode flag is used, the Common Name,
-Subject, and username strings are allowed to include any printable character
-including space, but excluding control characters such as tab, newline, and
-carriage\-return. no\-remapping is only available on the server side.
-
-.B Please note:
-This option is immediately deprecated. It is only implemented
-to make the transition to the new formatting less intrusive. It will be
-removed in OpenVPN 2.5. So please update your scripts/plug\-ins where necessary.
-.\"*********************************************************
-.TP
-.B \-\-no\-name\-remapping
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5
-
-The
-.B \-\-no\-name\-remapping
-option is an alias for
-.B \-\-compat\-names\ no\-remapping.
-It ensures compatibility with server configurations using the
-.B \-\-no\-name\-remapping
-option.
-
-.B Please note:
-This option is now deprecated. It will be removed in OpenVPN 2.5.
-So please make sure you support the new X.509 name formatting
-described with the
-.B \-\-compat\-names
-option as soon as possible.
-.\"*********************************************************
-.TP
-.B \-\-port\-share host port [dir]
-When run in TCP server mode, share the OpenVPN port with
-another application, such as an HTTPS server. If OpenVPN
-senses a connection to its port which is using a non\-OpenVPN
-protocol, it will proxy the connection to the server at
-.B host:port.
-Currently only designed to work with HTTP/HTTPS,
-though it would be theoretically possible to extend to
-other protocols such as ssh.
-
-.B dir
-specifies an optional directory where a temporary file with name N
-containing content C will be dynamically generated for each proxy
-connection, where N is the source IP:port of the client connection
-and C is the source IP:port of the connection to the proxy
-receiver. This directory can be used as a dictionary by
-the proxy receiver to determine the origin of the connection.
-Each generated file will be automatically deleted when the proxied
-connection is torn down.
-
-Not implemented on Windows.
-.\"*********************************************************
-.SS Client Mode
-Use client mode when connecting to an OpenVPN server
-which has
-.B \-\-server, \-\-server\-bridge,
-or
-.B \-\-mode server
-in it's configuration.
-.\"*********************************************************
+.B \-\-push\-peer\-info
+Push additional information about the client to server. The following
+data is always pushed to the server:
+.INDENT 7.0
+.TP
+.B \fBIV_VER=<version>\fP
+The client OpenVPN version
+.TP
+.B \fBIV_PLAT=[linux|solaris|openbsd|mac|netbsd|freebsd|win]\fP
+The client OS platform
+.TP
+.B \fBIV_LZO_STUB=1\fP
+If client was built with LZO stub capability
+.TP
+.B \fBIV_LZ4=1\fP
+If the client supports LZ4 compressions.
+.TP
+.B \fBIV_PROTO\fP
+Details about protocol extensions that the peer supports. The
+variable is a bitfield and the bits are defined as follows
+(starting a bit 0 for the first (unused) bit:
+.INDENT 7.0
+.IP \(bu 2
+bit 1: The peer supports peer\-id floating mechanism
+.IP \(bu 2
+bit 2: The client expects a push\-reply and the server may
+send this reply without waiting for a push\-request first.
+.UNINDENT
+.TP
+.B \fBIV_NCP=2\fP
+Negotiable ciphers, client supports \fB\-\-cipher\fP pushed by
+the server, a value of 2 or greater indicates client supports
+\fIAES\-GCM\-128\fP and \fIAES\-GCM\-256\fP\&.
+.TP
+.B \fBIV_CIPHERS=<ncp\-ciphers>\fP
+The client announces the list of supported ciphers configured with the
+\fB\-\-data\-ciphers\fP option to the server.
+.TP
+.B \fBIV_GUI_VER=<gui_id> <version>\fP
+The UI version of a UI if one is running, for example
+\fBde.blinkt.openvpn 0.5.47\fP for the Android app.
+.TP
+.B \fBIV_SSO=[crtext,][openurl,][proxy_url]\fP
+Additional authentication methods supported by the client.
+This may be set by the client UI/GUI using \fB\-\-setenv\fP
+.UNINDENT
+.sp
+When \fB\-\-push\-peer\-info\fP is enabled the additional information consists
+of the following data:
+.INDENT 7.0
+.TP
+.B \fBIV_HWADDR=<string>\fP
+This is intended to be a unique and persistent ID of the client.
+The string value can be any readable ASCII string up to 64 bytes.
+OpenVPN 2.x and some other implementations use the MAC address of
+the client\(aqs interface used to reach the default gateway. If this
+string is generated by the client, it should be consistent and
+preserved across independent session and preferably
+re\-installations and upgrades.
+.TP
+.B \fBIV_SSL=<version string>\fP
+The ssl version used by the client, e.g.
+\fBOpenSSL 1.0.2f 28 Jan 2016\fP\&.
+.TP
+.B \fBIV_PLAT_VER=x.y\fP
+The version of the operating system, e.g. 6.1 for Windows 7.
+.TP
+.B \fBUV_<name>=<value>\fP
+Client environment variables whose names start with
+\fBUV_\fP
+.UNINDENT
+.TP
+.BI \-\-push\-remove \ opt
+Selectively remove all \fB\-\-push\fP options matching "opt" from the option
+list for a client. \fBopt\fP is matched as a substring against the whole
+option string to\-be\-pushed to the client, so \fB\-\-push\-remove route\fP
+would remove all \fB\-\-push route ...\fP and \fB\-\-push route\-ipv6 ...\fP
+statements, while \fB\-\-push\-remove "route\-ipv6 2001:"\fP would only remove
+IPv6 routes for \fB2001:...\fP networks.
+.sp
+\fB\-\-push\-remove\fP can only be used in a client\-specific context, like in
+a \fB\-\-client\-config\-dir\fP file, or \fB\-\-client\-connect\fP script or plugin
+\-\- similar to \fB\-\-push\-reset\fP, just more selective.
+.sp
+\fINOTE\fP: to \fIchange\fP an option, \fB\-\-push\-remove\fP can be used to first
+remove the old value, and then add a new \fB\-\-push\fP option with the new
+value.
+.sp
+\fINOTE 2\fP: due to implementation details, \(aqifconfig\(aq and \(aqifconfig\-ipv6\(aq
+can only be removed with an exact match on the option (
+\fBpush\-remove ifconfig\fP), no substring matching and no matching on
+the IPv4/IPv6 address argument is possible.
.TP
-.B \-\-client
-A helper directive designed to simplify the configuration
-of OpenVPN's client mode. This directive is equivalent to:
-
+.B \-\-push\-reset
+Don\(aqt inherit the global push list for a specific client instance.
+Specify this option in a client\-specific context such as with a
+\fB\-\-client\-config\-dir\fP configuration file. This option will ignore
+\fB\-\-push\fP options at the global config file level.
+.sp
+\fINOTE\fP: \fB\-\-push\-reset\fP is very thorough: it will remove almost
+all options from the list of to\-be\-pushed options. In many cases,
+some of these options will need to be re\-configured afterwards \-
+specifically, \fB\-\-topology subnet\fP and \fB\-\-route\-gateway\fP will get
+lost and this will break client configs in many cases. Thus, for most
+purposes, \fB\-\-push\-remove\fP is better suited to selectively remove
+push options for individual clients.
+.TP
+.BI \-\-server \ args
+A helper directive designed to simplify the configuration of OpenVPN\(aqs
+server mode. This directive will set up an OpenVPN server which will
+allocate addresses to clients out of the given network/netmask. The
+server itself will take the \fB\&.1\fP address of the given network for
+use as the server\-side endpoint of the local TUN/TAP interface. If the
+optional \fBnopool\fP flag is given, no dynamic IP address pool will
+prepared for VPN clients.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
- pull
- tls\-client
-.in -4
-.ft
+.ft C
+server network netmask [nopool]
+.ft P
.fi
-.\"*********************************************************
-.TP
-.B \-\-pull
-This option must be used on a client which is connecting
-to a multi\-client server. It indicates to OpenVPN that it
-should accept options pushed by the server, provided they
-are part of the legal set of pushable options (note that the
-.B \-\-pull
-option is implied by
-.B \-\-client
-).
-
-In particular,
-.B \-\-pull
-allows the server to push routes to the client, so you should
-not use
-.B \-\-pull
-or
-.B \-\-client
-in situations where you don't trust the server to have control
-over the client's routing table.
-.\"*********************************************************
-.TP
-.B \-\-pull\-filter accept|ignore|reject \fItext\fR
-Filter options received from the server if the option starts with
-\fItext\fR. Runs on client. The action flag
-.B accept
-allows the option,
-.B ignore
-removes it and
-.B reject
-flags an error and triggers a SIGUSR1 restart.
-The filters may be specified multiple times, and each filter is
-applied in the order it is specified. The filtering of each
-option stops as soon as a match is found. Unmatched options are accepted
-by default.
-
-Prefix comparison is used to match \fItext\fR against the
-received option so that
-
+.UNINDENT
+.UNINDENT
+.sp
+For example, \fB\-\-server 10.8.0.0 255.255.255.0\fP expands as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-\-\-pull\-filter ignore "route"
-.in -4
-.ft
+.ft C
+mode server
+tls\-server
+push "topology [topology]"
+
+if dev tun AND (topology == net30 OR topology == p2p):
+ ifconfig 10.8.0.1 10.8.0.2
+ if !nopool:
+ ifconfig\-pool 10.8.0.4 10.8.0.251
+ route 10.8.0.0 255.255.255.0
+ if client\-to\-client:
+ push "route 10.8.0.0 255.255.255.0"
+ else if topology == net30:
+ push "route 10.8.0.1"
+
+if dev tap OR (dev tun AND topology == subnet):
+ ifconfig 10.8.0.1 255.255.255.0
+ if !nopool:
+ ifconfig\-pool 10.8.0.2 10.8.0.253 255.255.255.0
+ push "route\-gateway 10.8.0.1"
+ if route\-gateway unset:
+ route\-gateway 10.8.0.2
+.ft P
.fi
-
-would remove all pushed options starting with
-.B route
-which would include, for example,
-.B route\-gateway.
-Enclose \fItext\fR in quotes to embed spaces.
-
+.UNINDENT
+.UNINDENT
+.sp
+Don\(aqt use \fB\-\-server\fP if you are ethernet bridging. Use
+\fB\-\-server\-bridge\fP instead.
+.TP
+.BI \-\-server\-bridge \ args
+A helper directive similar to \fB\-\-server\fP which is designed to simplify
+the configuration of OpenVPN\(aqs server mode in ethernet bridging
+configurations.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-\-\-pull\-filter accept "route 192.168.1."
-\-\-pull\-filter ignore "route "
-.in -4
-.ft
+.ft C
+server\-bridge gateway netmask pool\-start\-IP pool\-end\-IP
+server\-bridge [nogw]
+.ft P
.fi
+.UNINDENT
+.UNINDENT
+.sp
+If \fB\-\-server\-bridge\fP is used without any parameters, it will enable a
+DHCP\-proxy mode, where connecting OpenVPN clients will receive an IP
+address for their TAP adapter from the DHCP server running on the
+OpenVPN server\-side LAN. Note that only clients that support the binding
+of a DHCP client with the TAP adapter (such as Windows) can support this
+mode. The optional \fBnogw\fP flag (advanced) indicates that gateway
+information should not be pushed to the client.
+.sp
+To configure ethernet bridging, you must first use your OS\(aqs bridging
+capability to bridge the TAP interface with the ethernet NIC interface.
+For example, on Linux this is done with the \fBbrctl\fP tool, and with
+Windows XP it is done in the Network Connections Panel by selecting the
+ethernet and TAP adapters and right\-clicking on "Bridge Connections".
+.sp
+Next you you must manually set the IP/netmask on the bridge interface.
+The \fBgateway\fP and \fBnetmask\fP parameters to \fB\-\-server\-bridge\fP can be
+set to either the IP/netmask of the bridge interface, or the IP/netmask
+of the default gateway/router on the bridged subnet.
+.sp
+Finally, set aside a IP range in the bridged subnet, denoted by
+\fBpool\-start\-IP\fP and \fBpool\-end\-IP\fP, for OpenVPN to allocate to
+connecting clients.
+.sp
+For example, \fBserver\-bridge 10.8.0.4 255.255.255.0 10.8.0.128
+10.8.0.254\fP expands as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+mode server
+tls\-server
-would remove all routes that do not start with 192.168.1.
-
-This option may be used only on clients.
-Note that
-.B reject
-may result in a repeated cycle of failure and reconnect,
-unless multiple remotes are specified and connection to the next remote
-succeeds. To silently ignore an option pushed by the server, use
-.B ignore.
-.\"*********************************************************
-.TP
-.B \-\-auth\-user\-pass [up]
-Authenticate with server using username/password.
-.B up
-is a file containing username/password on 2 lines. If the
-password line is missing, OpenVPN will prompt for one.
-
-If
-.B up
-is omitted, username/password will be prompted from the
-console.
-
-The server configuration must specify an
-.B \-\-auth\-user\-pass\-verify
-script to verify the username/password provided by
-the client.
-.\"*********************************************************
-.TP
-.B \-\-auth\-retry type
-Controls how OpenVPN responds to username/password verification
-errors such as the client\-side response to an AUTH_FAILED message from the server
-or verification failure of the private key password.
-
-Normally used to prevent auth errors from being fatal
-on the client side, and to permit username/password requeries in case
-of error.
-
-An AUTH_FAILED message is generated by the server if the client
-fails
-.B \-\-auth\-user\-pass
-authentication, or if the server\-side
-.B \-\-client\-connect
-script returns an error status when the client
-tries to connect.
-
-.B type
-can be one of:
-
-.B none \-\-
-Client will exit with a fatal error (this is the default).
-.br
-.B nointeract \-\-
-Client will retry the connection without requerying for an
-.B \-\-auth\-user\-pass
-username/password. Use this option for unattended clients.
-.br
-.B interact \-\-
-Client will requery for an
-.B \-\-auth\-user\-pass
-username/password and/or private key password before attempting a reconnection.
-
-Note that while this option cannot be pushed, it can be controlled
-from the management interface.
-.\"*********************************************************
-.TP
-.B \-\-static\-challenge t e
-Enable static challenge/response protocol using challenge text
-.B t,
-with
-echo flag given by
-.B e
-(0|1).
-
-The echo flag indicates whether or not the user's response
-to the challenge should be echoed.
-
-See management\-notes.txt in the OpenVPN distribution for a
-description of the OpenVPN challenge/response protocol.
-.\"*********************************************************
-.TP
-\fB\-\-server\-poll\-timeout n\fR, \fB\-\-connect\-timeout n\fR
-When connecting to a remote server do not wait for more than
-.B n
-seconds waiting for a response before trying the next server.
-The default value is 120s. This timeout includes proxy and TCP
-connect timeouts.
-.\"*********************************************************
-.TP
-.B \-\-explicit\-exit\-notify [n]
-In UDP client mode or point\-to\-point mode, send server/peer an exit notification
-if tunnel is restarted or OpenVPN process is exited. In client mode, on
-exit/restart, this
-option will tell the server to immediately close its client instance object
-rather than waiting for a timeout. The
-.B n
-parameter (default=1) controls the maximum number of attempts that the client
-will try to resend the exit notification message.
-
-In UDP server mode, send RESTART control channel command to connected clients. The
-.B n
-parameter (default=1) controls client behavior. With
-.B n
-= 1 client will attempt to reconnect
-to the same server, with
-.B n
-= 2 client will advance to the next server.
+ifconfig\-pool 10.8.0.128 10.8.0.254 255.255.255.0
+push "route\-gateway 10.8.0.4"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+In another example, \fB\-\-server\-bridge\fP (without parameters) expands as
+follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+mode server
+tls\-server
-OpenVPN will not send any exit
-notifications unless this option is enabled.
+push "route\-gateway dhcp"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Or \fB\-\-server\-bridge nogw\fP expands as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+mode server
+tls\-server
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-server\-ipv6 \ args
+Convenience\-function to enable a number of IPv6 related options at once,
+namely \fB\-\-ifconfig\-ipv6\fP, \fB\-\-ifconfig\-ipv6\-pool\fP and
+\fB\-\-push tun\-ipv6\fP\&.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+server\-ipv6 ipv6addr/bits
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Pushing of the \fB\-\-tun\-ipv6\fP directive is done for older clients which
+require an explicit \fB\-\-tun\-ipv6\fP in their configuration.
+.TP
+.BI \-\-stale\-routes\-check \ args
+Remove routes which haven\(aqt had activity for \fBn\fP seconds (i.e. the ageing
+time). This check is run every \fBt\fP seconds (i.e. check interval).
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+stale\-routes\-check n [t]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If \fBt\fP is not present it defaults to \fBn\fP\&.
+.sp
+This option helps to keep the dynamic routing table small. See also
+\fB\-\-max\-routes\-per\-client\fP
.TP
-.B \-\-allow\-recursive\-routing
-When this option is set, OpenVPN will not drop incoming tun packets
-with same destination as host.
-.\"*********************************************************
-.SS Data Channel Encryption Options:
-These options are meaningful for both Static & TLS\-negotiated key modes
-(must be compatible between peers).
-.\"*********************************************************
-.TP
-.B \-\-secret file [direction]
-Enable Static Key encryption mode (non\-TLS).
-Use pre\-shared secret
-.B file
-which was generated with
-.B \-\-genkey.
-
-The optional
-.B direction
-parameter enables the use of 4 distinct keys
-(HMAC\-send, cipher\-encrypt, HMAC\-receive, cipher\-decrypt), so that
-each data flow direction has a different set of HMAC and cipher keys.
-This has a number of desirable security properties including
-eliminating certain kinds of DoS and message replay attacks.
-
-When the
-.B direction
-parameter is omitted, 2 keys are used bidirectionally, one for HMAC
-and the other for encryption/decryption.
-
-The
-.B direction
-parameter should always be complementary on either side of the connection,
-i.e. one side should use "0" and the other should use "1", or both sides
-should omit it altogether.
-
-The
-.B direction
-parameter requires that
-.B file
-contains a 2048 bit key. While pre\-1.5 versions of OpenVPN
-generate 1024 bit key files, any version of OpenVPN which
-supports the
-.B direction
-parameter, will also support 2048 bit key file generation
-using the
-.B \-\-genkey
-option.
-
-Static key encryption mode has certain advantages,
-the primary being ease of configuration.
-
-There are no certificates
-or certificate authorities or complicated negotiation handshakes and protocols.
-The only requirement is that you have a pre\-existing secure channel with
-your peer (such as
-.B ssh
-) to initially copy the key. This requirement, along with the
-fact that your key never changes unless you manually generate a new one,
-makes it somewhat less secure than TLS mode (see below). If an attacker
-manages to steal your key, everything that was ever encrypted with
-it is compromised. Contrast that to the perfect forward secrecy features of
-TLS mode (using Diffie Hellman key exchange), where even if an attacker
-was able to steal your private key, he would gain no information to help
-him decrypt past sessions.
-
-Another advantageous aspect of Static Key encryption mode is that
-it is a handshake\-free protocol
-without any distinguishing signature or feature
-(such as a header or protocol handshake sequence)
-that would mark the ciphertext packets as being
-generated by OpenVPN. Anyone eavesdropping on the wire
-would see nothing
-but random\-looking data.
-.\"*********************************************************
+.B \-\-username\-as\-common\-name
+Use the authenticated username as the common\-name, rather than the
+common\-name from the client certificate. Requires that some form of
+\fB\-\-auth\-user\-pass\fP verification is in effect. As the replacement happens
+after \fB\-\-auth\-user\-pass\fP verification, the verification script or
+plugin will still receive the common\-name from the certificate.
+.sp
+The common_name environment variable passed to scripts and plugins invoked
+after authentication (e.g, client\-connect script) and file names parsed in
+client\-config directory will match the username.
+.TP
+.BI \-\-verify\-client\-cert \ mode
+Specify whether the client is required to supply a valid certificate.
+.sp
+Possible \fBmode\fP options are:
+.INDENT 7.0
+.TP
+.B \fBnone\fP
+A client certificate is not required. the client needs to
+authenticate using username/password only. Be aware that using this
+directive is less secure than requiring certificates from all
+clients.
+.sp
+If you use this directive, the entire responsibility of authentication
+will rest on your \fB\-\-auth\-user\-pass\-verify\fP script, so keep in mind
+that bugs in your script could potentially compromise the security of
+your VPN.
+.sp
+\fB\-\-verify\-client\-cert none\fP is functionally equivalent to
+\fB\-\-client\-cert\-not\-required\fP\&.
+.TP
+.B \fBoptional\fP
+A client may present a certificate but it is not required to do so.
+When using this directive, you should also use a
+\fB\-\-auth\-user\-pass\-verify\fP script to ensure that clients are
+authenticated using a certificate, a username and password, or
+possibly even both.
+.sp
+Again, the entire responsibility of authentication will rest on your
+\fB\-\-auth\-user\-pass\-verify\fP script, so keep in mind that bugs in your
+script could potentially compromise the security of your VPN.
.TP
-.B \-\-key\-direction
-Alternative way of specifying the optional direction parameter for the
-.B \-\-tls\-auth
-and
-.B \-\-secret
-options. Useful when using inline files (See section on inline files).
-.\"*********************************************************
-.TP
-.B \-\-auth alg
-Authenticate data channel packets and (if enabled)
-.B tls\-auth
-control channel packets with HMAC using message digest algorithm
-.B alg.
-(The default is
-.B SHA1
-).
-HMAC is a commonly used message authentication algorithm (MAC) that uses
-a data string, a secure hash algorithm, and a key, to produce
-a digital signature.
-
-The OpenVPN data channel protocol uses encrypt\-then\-mac (i.e. first encrypt a
-packet, then HMAC the resulting ciphertext), which prevents padding oracle
-attacks.
-
-If an AEAD cipher mode (e.g. GCM) is chosen, the specified
-.B \-\-auth
-algorithm is ignored for the data channel, and the authentication method of the
-AEAD cipher is used instead. Note that
-.B alg
-still specifies the digest used for
-.B tls\-auth\fR.
-
-In static\-key encryption mode, the HMAC key
-is included in the key file generated by
-.B \-\-genkey.
-In TLS mode, the HMAC key is dynamically generated and shared
-between peers via the TLS control channel. If OpenVPN receives a packet with
-a bad HMAC it will drop the packet.
-HMAC usually adds 16 or 20 bytes per packet.
-Set
-.B alg=none
-to disable authentication.
-
-For more information on HMAC see
-.I http://www.cs.ucsd.edu/users/mihir/papers/hmac.html
-.\"*********************************************************
+.B \fBrequire\fP
+This is the default option. A client is required to present a
+certificate, otherwise VPN access is refused.
+.UNINDENT
+.sp
+If you don\(aqt use this directive (or use \fB\-\-verify\-client\-cert require\fP)
+but you also specify an \fB\-\-auth\-user\-pass\-verify\fP script, then OpenVPN
+will perform double authentication. The client certificate verification
+AND the \fB\-\-auth\-user\-pass\-verify\fP script will need to succeed in order
+for a client to be authenticated and accepted onto the VPN.
+.TP
+.B \-\-vlan\-tagging
+Server\-only option. Turns the OpenVPN server instance into a switch that
+understands VLAN\-tagging, based on IEEE 802.1Q.
+.sp
+The server TAP device and each of the connecting clients is seen as a
+port of the switch. All client ports are in untagged mode and the server
+TAP device is VLAN\-tagged, untagged or accepts both, depending on the
+\fB\-\-vlan\-accept\fP setting.
+.sp
+Ethernet frames with a prepended 802.1Q tag are called "tagged". If the
+VLAN Identifier (VID) field in such a tag is non\-zero, the frame is
+called "VLAN\-tagged". If the VID is zero, but the Priority Control Point
+(PCP) field is non\-zero, the frame is called "prio\-tagged". If there is
+no 802.1Q tag, the frame is "untagged".
+.sp
+Using the \fB\-\-vlan\-pvid v\fP option once per client (see
+\-\-client\-config\-dir), each port can be associated with a certain VID.
+Packets can only be forwarded between ports having the same VID.
+Therefore, clients with differing VIDs are completely separated from
+one\-another, even if \fB\-\-client\-to\-client\fP is activated.
+.sp
+The packet filtering takes place in the OpenVPN server. Clients should
+not have any VLAN tagging configuration applied.
+.sp
+The \fB\-\-vlan\-tagging\fP option is off by default. While turned off,
+OpenVPN accepts any Ethernet frame and does not perform any special
+processing for VLAN\-tagged packets.
+.sp
+This option can only be activated in \fB\-\-dev tap mode\fP\&.
+.TP
+.BI \-\-vlan\-accept \ args
+Configure the VLAN tagging policy for the server TAP device.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+vlan\-accept all|tagged|untagged
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The following modes are available:
+.INDENT 7.0
+.TP
+.B \fBtagged\fP
+Admit only VLAN\-tagged frames. Only VLAN\-tagged packets are accepted,
+while untagged or priority\-tagged packets are dropped when entering
+the server TAP device.
+.TP
+.B \fBuntagged\fP
+Admit only untagged and prio\-tagged frames. VLAN\-tagged packets are
+not accepted, while untagged or priority\-tagged packets entering the
+server TAP device are tagged with the value configured for the global
+\fB\-\-vlan\-pvid\fP setting.
+.TP
+.B \fBall\fP (default)
+Admit all frames. All packets are admitted and then treated like
+untagged or tagged mode respectively.
+.TP
+.B \fINote\fP:
+Some vendors refer to switch ports running in \fBtagged\fP mode
+as "trunk ports" and switch ports running in \fBuntagged\fP mode
+as "access ports".
+.UNINDENT
+.sp
+Packets forwarded from clients to the server are VLAN\-tagged with the
+originating client\(aqs PVID, unless the VID matches the global
+\fB\-\-vlan\-pvid\fP, in which case the tag is removed.
+.sp
+If no \fIPVID\fP is configured for a given client (see \-\-vlan\-pvid) packets
+are tagged with 1 by default.
+.TP
+.BI \-\-vlan\-pvid \ v
+Specifies which VLAN identifier a "port" is associated with. Only valid
+when \fB\-\-vlan\-tagging\fP is speficied.
+.sp
+In the client context, the setting specifies which VLAN ID a client is
+associated with. In the global context, the VLAN ID of the server TAP
+device is set. The latter only makes sense for \fB\-\-vlan\-accept
+untagged\fP and \fB\-\-vlan\-accept all\fP modes.
+.sp
+Valid values for \fBv\fP go from \fB1\fP through to \fB4094\fP\&. The
+global value defaults to \fB1\fP\&. If no \fB\-\-vlan\-pvid\fP is specified in
+the client context, the global value is inherited.
+.sp
+In some switch implementations, the \fIPVID\fP is also referred to as "Native
+VLAN".
+.UNINDENT
+.SH ENCRYPTION OPTIONS
+.SS SSL Library information
+.INDENT 0.0
.TP
-.B \-\-cipher alg
-Encrypt data channel packets with cipher algorithm
-.B alg.
-
-The default is
-.B BF\-CBC,
-an abbreviation for Blowfish in Cipher Block Chaining mode. When cipher
-negotiation (NCP) is allowed, OpenVPN 2.4 and newer on both client and server
-side will automatically upgrade to
-.B AES\-256\-GCM.
-See
-.B \-\-ncp\-ciphers
-and
-.B \-\-ncp\-disable
-for more details on NCP.
-
-Using
-.B BF\-CBC
-is no longer recommended, because of its 64\-bit block size. This
-small block size allows attacks based on collisions, as demonstrated by SWEET32.
-See https://community.openvpn.net/openvpn/wiki/SWEET32 for details. Due to
-this, support for
-.B BF\-CBC, DES, CAST5, IDEA
-and
-.B RC2
-ciphers will be removed in OpenVPN 2.6.
-
-To see other ciphers that are available with OpenVPN, use the
.B \-\-show\-ciphers
+(Standalone) Show all cipher algorithms to use with the \fB\-\-cipher\fP
option.
-
-Set
-.B alg=none
-to disable encryption.
-
-.\"*********************************************************
-.TP
-.B \-\-ncp\-ciphers cipher_list
-Restrict the allowed ciphers to be negotiated to the ciphers in
-.B cipher_list\fR.
-.B cipher_list
-is a colon\-separated list of ciphers, and defaults to
-"AES\-256\-GCM:AES\-128\-GCM".
-
-For servers, the first cipher from
-.B cipher_list
-will be pushed to clients that support cipher negotiation.
-
-Cipher negotiation is enabled in client\-server mode only. I.e. if
-.B \-\-mode
-is set to 'server' (server\-side, implied by setting
-.B \-\-server
-), or if
-.B \-\-pull
-is specified (client\-side, implied by setting \-\-client).
-
-If both peers support and do not disable NCP, the negotiated cipher will
-override the cipher specified by
-.B \-\-cipher\fR.
-
-Additionally, to allow for more smooth transition, if NCP is enabled, OpenVPN
-will inherit the cipher of the peer if that cipher is different from the local
-.B \-\-cipher
-setting, but the peer cipher is one of the ciphers specified in
-.B \-\-ncp\-ciphers\fR.
-E.g. a non\-NCP client (<=v2.3, or with \-\-ncp\-disabled set) connecting to a
-NCP server (v2.4+) with "\-\-cipher BF\-CBC" and "\-\-ncp\-ciphers
-AES\-256\-GCM:AES\-256\-CBC" set can either specify "\-\-cipher BF\-CBC" or
-"\-\-cipher AES\-256\-CBC" and both will work.
-
-.\"*********************************************************
.TP
-.B \-\-ncp\-disable
-Disable "negotiable crypto parameters". This completely disables cipher
-negotiation.
-.\"*********************************************************
+.B \-\-show\-digests
+(Standalone) Show all message digest algorithms to use with the
+\fB\-\-auth\fP option.
.TP
-.B \-\-keysize n
-.B DEPRECATED
-This option will be removed in OpenVPN 2.6.
-
-Size of cipher key in bits (optional).
-If unspecified, defaults to cipher\-specific default. The
-.B \-\-show\-ciphers
-option (see below) shows all available OpenSSL ciphers,
-their default key sizes, and whether the key size can
-be changed. Use care in changing a cipher's default
-key size. Many ciphers have not been extensively
-cryptanalyzed with non\-standard key lengths, and a
-larger key may offer no real guarantee of greater
-security, or may even reduce security.
-.\"*********************************************************
-.TP
-.B \-\-prng alg [nsl]
-(Advanced) For PRNG (Pseudo\-random number generator),
-use digest algorithm
-.B alg
-(default=sha1), and set
-.B nsl
-(default=16)
-to the size in bytes of the nonce secret length (between 16 and 64).
-
-Set
-.B alg=none
-to disable the PRNG and use the OpenSSL RAND_bytes function
-instead for all of OpenVPN's pseudo\-random number needs.
-.\"*********************************************************
+.B \-\-show\-tls
+(Standalone) Show all TLS ciphers supported by the crypto library.
+OpenVPN uses TLS to secure the control channel, over which the keys that
+are used to protect the actual VPN traffic are exchanged. The TLS
+ciphers will be sorted from highest preference (most secure) to lowest.
+.sp
+Be aware that whether a cipher suite in this list can actually work
+depends on the specific setup of both peers (e.g. both peers must
+support the cipher, and an ECDSA cipher suite will not work if you are
+using an RSA certificate, etc.).
.TP
-.B \-\-engine [engine\-name]
-Enable OpenSSL hardware\-based crypto engine functionality.
-
-If
-.B engine\-name
-is specified,
-use a specific crypto engine. Use the
.B \-\-show\-engines
-standalone option to list the crypto engines which are
-supported by OpenSSL.
-.\"*********************************************************
-.TP
-.B \-\-no\-replay
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5.
-
-(Advanced) Disable OpenVPN's protection against replay attacks.
-Don't use this option unless you are prepared to make
-a tradeoff of greater efficiency in exchange for less
-security.
-
-OpenVPN provides datagram replay protection by default.
-
-Replay protection is accomplished
-by tagging each outgoing datagram with an identifier
-that is guaranteed to be unique for the key being used.
-The peer that receives the datagram will check for
-the uniqueness of the identifier. If the identifier
-was already received in a previous datagram, OpenVPN
-will drop the packet. Replay protection is important
-to defeat attacks such as a SYN flood attack, where
-the attacker listens in the wire, intercepts a TCP
-SYN packet (identifying it by the context in which
-it occurs in relation to other packets), then floods
-the receiving peer with copies of this packet.
-
-OpenVPN's replay protection is implemented in slightly
-different ways, depending on the key management mode
-you have selected.
-
-In Static Key mode
-or when using an CFB or OFB mode cipher, OpenVPN uses a
-64 bit unique identifier that combines a time stamp with
-an incrementing sequence number.
-
-When using TLS mode for key exchange and a CBC cipher
-mode, OpenVPN uses only a 32 bit sequence number without
-a time stamp, since OpenVPN can guarantee the uniqueness
-of this value for each key. As in IPSec, if the sequence number is
-close to wrapping back to zero, OpenVPN will trigger
-a new key exchange.
-
-To check for replays, OpenVPN uses
-the
-.I sliding window
-algorithm used
-by IPSec.
-.\"*********************************************************
-.TP
-.B \-\-replay\-window n [t]
-Use a replay protection sliding\-window of size
-.B n
-and a time window of
-.B t
-seconds.
-
-By default
-.B n
-is 64 (the IPSec default) and
-.B t
-is 15 seconds.
-
-This option is only relevant in UDP mode, i.e.
-when either
-.B \-\-proto udp
-is specified, or no
-.B \-\-proto
-option is specified.
-
-When OpenVPN tunnels IP packets over UDP, there is the possibility that
-packets might be dropped or delivered out of order. Because OpenVPN, like IPSec,
-is emulating the physical network layer,
-it will accept an out\-of\-order packet sequence, and
-will deliver such packets in the same order they were received to
-the TCP/IP protocol stack, provided they satisfy several constraints.
-
-.B (a)
-The packet cannot be a replay (unless
-.B \-\-no\-replay
-is specified, which disables replay protection altogether).
-
-.B (b)
-If a packet arrives out of order, it will only be accepted if the difference
-between its sequence number and the highest sequence number received
-so far is less than
-.B n.
-
-.B (c)
-If a packet arrives out of order, it will only be accepted if it arrives no later
-than
-.B t
-seconds after any packet containing a higher sequence number.
-
-If you are using a network link with a large pipeline (meaning that
-the product of bandwidth and latency is high), you may want to use
-a larger value for
-.B n.
-Satellite links in particular often require this.
-
-If you run OpenVPN at
-.B \-\-verb 4,
-you will see the message "Replay\-window backtrack occurred [x]"
-every time the maximum sequence number backtrack seen thus far
-increases. This can be used to calibrate
-.B n.
-
-There is some controversy on the appropriate method of handling packet
-reordering at the security layer.
-
-Namely, to what extent should the
-security layer protect the encapsulated protocol from attacks which masquerade
-as the kinds of normal packet loss and reordering that occur over IP networks?
-
-The IPSec and OpenVPN approach is to allow packet reordering within a certain
-fixed sequence number window.
-
-OpenVPN adds to the IPSec model by limiting the window size in time as well as
-sequence space.
-
-OpenVPN also adds TCP transport as an option (not offered by IPSec) in which
-case OpenVPN can adopt a very strict attitude towards message deletion and
-reordering: Don't allow it. Since TCP guarantees reliability, any packet
-loss or reordering event can be assumed to be an attack.
-
-In this sense, it could be argued that TCP tunnel transport is preferred when
-tunneling non\-IP or UDP application protocols which might be vulnerable to a
-message deletion or reordering attack which falls within the normal
-operational parameters of IP networks.
-
-So I would make the statement that one should never tunnel a non\-IP protocol
-or UDP application protocol over UDP, if the protocol might be vulnerable to a
-message deletion or reordering attack that falls within the normal operating
-parameters of what is to be expected from the physical IP layer. The problem
-is easily fixed by simply using TCP as the VPN transport layer.
-.\"*********************************************************
-.TP
-.B \-\-mute\-replay\-warnings
-Silence the output of replay warnings, which are a common
-false alarm on WiFi networks. This option preserves
-the security of the replay protection code without
-the verbosity associated with warnings about duplicate
-packets.
-.\"*********************************************************
-.TP
-.B \-\-replay\-persist file
-Persist replay\-protection state across sessions using
-.B file
-to save and reload the state.
-
-This option will strengthen protection against replay attacks,
-especially when you are using OpenVPN in a dynamic context (such
-as with
-.B \-\-inetd)
-when OpenVPN sessions are frequently started and stopped.
-
-This option will keep a disk copy of the current replay protection
-state (i.e. the most recent packet timestamp and sequence number
-received from the remote peer), so that if an OpenVPN session
-is stopped and restarted, it will reject any replays of packets
-which were already received by the prior session.
-
-This option only makes sense when replay protection is enabled
-(the default) and you are using either
-.B \-\-secret
-(shared\-secret key mode) or TLS mode with
-.B \-\-tls\-auth.
-.\"*********************************************************
-.TP
-.B \-\-no\-iv
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5.
-
-(Advanced) Disable OpenVPN's use of IV (cipher initialization vector).
-Don't use this option unless you are prepared to make
-a tradeoff of greater efficiency in exchange for less
-security.
-
-OpenVPN uses an IV by default, and requires it for CFB and
-OFB cipher modes (which are totally insecure without it).
-Using an IV is important for security when multiple
-messages are being encrypted/decrypted with the same key.
-
-IV is implemented differently depending on the cipher mode used.
-
-In CBC mode, OpenVPN uses a pseudo\-random IV for each packet.
-
-In CFB/OFB mode, OpenVPN uses a unique sequence number and time stamp
-as the IV. In fact, in CFB/OFB mode, OpenVPN uses a datagram
-space\-saving optimization that uses the unique identifier for
-datagram replay protection as the IV.
-.\"*********************************************************
+(Standalone) Show currently available hardware\-based crypto acceleration
+engines supported by the OpenSSL library.
.TP
-.B \-\-use\-prediction\-resistance
-Enable prediction resistance on mbed TLS's RNG.
-
-Enabling prediction resistance causes the RNG to reseed in each
-call for random. Reseeding this often can quickly deplete the kernel
-entropy pool.
-
-If you need this option, please consider running a daemon that adds
-entropy to the kernel pool.
-
-.\"*********************************************************
+.B \-\-show\-groups
+(Standalone) Show all available elliptic curves/groups to use with the
+\fB\-\-ecdh\-curve\fP and \fBtls\-groups\fP options.
+.UNINDENT
+.SS Generating key material
+.INDENT 0.0
+.TP
+.BI \-\-genkey \ args
+(Standalone) Generate a key to be used of the type keytype. if keyfile
+is left out or empty the key will be output on stdout. See the following
+sections for the different keytypes.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-genkey keytype keyfile
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Valid keytype arguments are:
+.sp
+\fBsecret\fP Standard OpenVPN shared secret keys
+.sp
+\fBtls\-crypt\fP Alias for \fBsecret\fP
+.sp
+\fBtls\-auth\fP Alias for \fBsecret\fP
+.sp
+\fBauth\-token\fP Key used for \fB\-\-auth\-gen\-token\-key\fP
+.sp
+\fBtls\-crypt\-v2\-server\fP TLS Crypt v2 server key
+.sp
+\fBtls\-crypt\-v2\-client\fP TLS Crypt v2 client key
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ openvpn \-\-genkey secret shared.key
+$ openvpn \-\-genkey tls\-crypt shared.key
+$ openvpn \-\-genkey tls\-auth shared.key
+$ openvpn \-\-genkey tls\-crypt\-v2\-server v2crypt\-server.key
+$ openvpn \-\-tls\-crypt\-v2 v2crypt\-server.key \-\-genkey tls\-crypt\-v2\-client v2crypt\-client\-1.key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.INDENT 7.0
+.IP \(bu 2
+Generating \fIShared Secret Keys\fP
+Generate a shared secret, for use with the \fB\-\-secret\fP, \fB\-\-tls\-auth\fP
+or \fB\-\-tls\-crypt\fP options.
+.sp
+Syntax:
+.INDENT 2.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ openvpn \-\-genkey secret|tls\-crypt|tls\-auth keyfile
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The key is saved in \fBkeyfile\fP\&. All three variants (\fB\-\-secret\fP,
+\fBtls\-crypt\fP and \fBtls\-auth\fP) generate the same type of key. The
+aliases are added for convenience.
+.sp
+If using this for \fB\-\-secret\fP, this file must be shared with the peer
+over a pre\-existing secure channel such as \fBscp\fP(1).
+.IP \(bu 2
+Generating \fITLS Crypt v2 Server key\fP
+Generate a \fB\-\-tls\-crypt\-v2\fP key to be used by an OpenVPN server.
+The key is stored in \fBkeyfile\fP\&.
+.sp
+Syntax:
+.INDENT 2.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-genkey tls\-crypt\-v2\-server keyfile
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP \(bu 2
+Generating \fITLS Crypt v2 Client key\fP
+Generate a \-\-tls\-crypt\-v2 key to be used by OpenVPN clients. The
+key is stored in \fBkeyfile\fP\&.
+.sp
+Syntax
+.INDENT 2.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-genkey tls\-crypt\-v2\-client keyfile [metadata]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If supplied, include the supplied \fBmetadata\fP in the wrapped client
+key. This metadata must be supplied in base64\-encoded form. The
+metadata must be at most 735 bytes long (980 bytes in base64).
+.sp
+If no metadata is supplied, OpenVPN will use a 64\-bit unix timestamp
+representing the current time in UTC, encoded in network order, as
+metadata for the generated key.
+.sp
+A tls\-crypt\-v2 client key is wrapped using a server key. To generate a
+client key, the user must therefore supply the server key using the
+\fB\-\-tls\-crypt\-v2\fP option.
+.sp
+Servers can use \fB\-\-tls\-crypt\-v2\-verify\fP to specify a metadata
+verification command.
+.IP \(bu 2
+Generate \fIAuthentication Token key\fP
+Generate a new secret that can be used with \fB\-\-auth\-gen\-token\-secret\fP
+.sp
+Syntax:
+.INDENT 2.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-genkey auth\-token [keyfile]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.INDENT 2.0
+.TP
+.B \fINote:\fP
+This file should be kept secret to the server as anyone that has
+access to this file will be able to generate auth tokens that the
+OpenVPN server will accept as valid.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SS Data Channel Renegotiation
+.sp
+When running OpenVPN in client/server mode, the data channel will use a
+separate ephemeral encryption key which is rotated at regular intervals.
+.INDENT 0.0
+.TP
+.BI \-\-reneg\-bytes \ n
+Renegotiate data channel key after \fBn\fP bytes sent or received
+(disabled by default with an exception, see below). OpenVPN allows the
+lifetime of a key to be expressed as a number of bytes
+encrypted/decrypted, a number of packets, or a number of seconds. A key
+renegotiation will be forced if any of these three criteria are met by
+either peer.
+.sp
+If using ciphers with cipher block sizes less than 128\-bits,
+\fB\-\-reneg\-bytes\fP is set to 64MB by default, unless it is explicitly
+disabled by setting the value to \fB0\fP, but this is
+\fBHIGHLY DISCOURAGED\fP as this is designed to add some protection against
+the SWEET32 attack vector. For more information see the \fB\-\-cipher\fP
+option.
.TP
-.B \-\-test\-crypto
-Do a self\-test of OpenVPN's crypto options by encrypting and
-decrypting test packets using the data channel encryption options
-specified above. This option does not require a peer to function,
-and therefore can be specified without
-.B \-\-dev
-or
-.B \-\-remote.
-
-The typical usage of
-.B \-\-test\-crypto
-would be something like this:
-
-.B openvpn \-\-test\-crypto \-\-secret key
-
-or
-
-.B openvpn \-\-test\-crypto \-\-secret key \-\-verb 9
-
-This option is very useful to test OpenVPN after it has been ported to
-a new platform, or to isolate problems in the compiler, OpenSSL
-crypto library, or OpenVPN's crypto code. Since it is a self\-test mode,
-problems with encryption and authentication can be debugged independently
-of network and tunnel issues.
-.\"*********************************************************
-.SS TLS Mode Options:
-TLS mode is the most powerful crypto mode of OpenVPN in both security and flexibility.
-TLS mode works by establishing control and
-data channels which are multiplexed over a single TCP/UDP port. OpenVPN initiates
-a TLS session over the control channel and uses it to exchange cipher
-and HMAC keys to protect the data channel. TLS mode uses a robust reliability
-layer over the UDP connection for all control channel communication, while
-the data channel, over which encrypted tunnel data passes, is forwarded without
-any mediation. The result is the best of both worlds: a fast data channel
-that forwards over UDP with only the overhead of encrypt,
-decrypt, and HMAC functions,
-and a control channel that provides all of the security features of TLS,
-including certificate\-based authentication and Diffie Hellman forward secrecy.
-
+.BI \-\-reneg\-pkts \ n
+Renegotiate data channel key after \fBn\fP packets sent and received
+(disabled by default).
+.TP
+.BI \-\-reneg\-sec \ args
+Renegotiate data channel key after at most \fBmax\fP seconds
+(default \fB3600\fP) and at least \fBmin\fP seconds (default is 90% of
+\fBmax\fP for servers, and equal to \fBmax\fP for clients).
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+reneg\-sec max [min]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The effective \fB\-\-reneg\-sec\fP value used is per session
+pseudo\-uniform\-randomized between \fBmin\fP and \fBmax\fP\&.
+.sp
+With the default value of \fB3600\fP this results in an effective per
+session value in the range of \fB3240\fP .. \fB3600\fP seconds for
+servers, or just 3600 for clients.
+.sp
+When using dual\-factor authentication, note that this default value may
+cause the end user to be challenged to reauthorize once per hour.
+.sp
+Also, keep in mind that this option can be used on both the client and
+server, and whichever uses the lower value will be the one to trigger
+the renegotiation. A common mistake is to set \fB\-\-reneg\-sec\fP to a
+higher value on either the client or server, while the other side of the
+connection is still using the default value of \fB3600\fP seconds,
+meaning that the renegotiation will still occur once per \fB3600\fP
+seconds. The solution is to increase \-\-reneg\-sec on both the client and
+server, or set it to \fB0\fP on one side of the connection (to
+disable), and to your chosen value on the other side.
+.UNINDENT
+.SS TLS Mode Options
+.sp
+TLS mode is the most powerful crypto mode of OpenVPN in both security
+and flexibility. TLS mode works by establishing control and data
+channels which are multiplexed over a single TCP/UDP port. OpenVPN
+initiates a TLS session over the control channel and uses it to exchange
+cipher and HMAC keys to protect the data channel. TLS mode uses a robust
+reliability layer over the UDP connection for all control channel
+communication, while the data channel, over which encrypted tunnel data
+passes, is forwarded without any mediation. The result is the best of
+both worlds: a fast data channel that forwards over UDP with only the
+overhead of encrypt, decrypt, and HMAC functions, and a control channel
+that provides all of the security features of TLS, including
+certificate\-based authentication and Diffie Hellman forward secrecy.
+.sp
To use TLS mode, each peer that runs OpenVPN should have its own local
-certificate/key pair (
-.B \-\-cert
-and
-.B \-\-key
-), signed by the root certificate which is specified
-in
-.B \-\-ca.
-
-When two OpenVPN peers connect, each presents its local certificate to the
-other. Each peer will then check that its partner peer presented a
-certificate which was signed by the master root certificate as specified in
-.B \-\-ca.
-
-If that check on both peers succeeds, then the TLS negotiation
-will succeed, both OpenVPN
-peers will exchange temporary session keys, and the tunnel will begin
-passing data.
-
-The OpenVPN project provides a set of scripts for
-managing RSA certificates & keys:
-.I https://github.com/OpenVPN/easy\-rsa
-.\"*********************************************************
+certificate/key pair (\fB\-\-cert\fP and \fB\-\-key\fP), signed by the root
+certificate which is specified in \fB\-\-ca\fP\&.
+.sp
+When two OpenVPN peers connect, each presents its local certificate to
+the other. Each peer will then check that its partner peer presented a
+certificate which was signed by the master root certificate as specified
+in \fB\-\-ca\fP\&.
+.sp
+If that check on both peers succeeds, then the TLS negotiation will
+succeed, both OpenVPN peers will exchange temporary session keys, and
+the tunnel will begin passing data.
+.sp
+The OpenVPN project provides a set of scripts for managing RSA
+certificates and keys: \fI\%https://github.com/OpenVPN/easy\-rsa\fP
+.INDENT 0.0
+.TP
+.BI \-\-askpass \ file
+Get certificate password from console or \fBfile\fP before we daemonize.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+askpass
+askpass file
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+For the extremely security conscious, it is possible to protect your
+private key with a password. Of course this means that every time the
+OpenVPN daemon is started you must be there to type the password. The
+\fB\-\-askpass\fP option allows you to start OpenVPN from the command line.
+It will query you for a password before it daemonizes. To protect a
+private key with a password you should omit the \fB\-nodes\fP option when
+you use the \fBopenssl\fP command line tool to manage certificates and
+private keys.
+.sp
+If \fBfile\fP is specified, read the password from the first line of
+\fBfile\fP\&. Keep in mind that storing your password in a file to a certain
+extent invalidates the extra security provided by using an encrypted
+key.
+.TP
+.BI \-\-ca \ file
+Certificate authority (CA) file in .pem format, also referred to as the
+\fIroot\fP certificate. This file can have multiple certificates in .pem
+format, concatenated together. You can construct your own certificate
+authority certificate and private key by using a command such as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openssl req \-nodes \-new \-x509 \-keyout ca.key \-out ca.crt
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Then edit your openssl.cnf file and edit the \fBcertificate\fP variable to
+point to your new root certificate \fBca.crt\fP\&.
+.sp
+For testing purposes only, the OpenVPN distribution includes a sample CA
+certificate (ca.crt). Of course you should never use the test
+certificates and test keys distributed with OpenVPN in a production
+environment, since by virtue of the fact that they are distributed with
+OpenVPN, they are totally insecure.
+.TP
+.BI \-\-capath \ dir
+Directory containing trusted certificates (CAs and CRLs). Not available
+with mbed TLS.
+.sp
+CAs in the capath directory are expected to be named <hash>.<n>. CRLs
+are expected to be named <hash>.r<n>. See the \fB\-CApath\fP option of
+\fBopenssl verify\fP, and the \fB\-hash\fP option of \fBopenssl x509\fP,
+\fBopenssl crl\fP and \fBX509_LOOKUP_hash_dir()\fP(3)
+for more information.
+.sp
+Similar to the \fB\-\-crl\-verify\fP option, CRLs are not mandatory \-
+OpenVPN will log the usual warning in the logs if the relevant CRL is
+missing, but the connection will be allowed.
+.TP
+.BI \-\-cert \ file
+Local peer\(aqs signed certificate in .pem format \-\- must be signed by a
+certificate authority whose certificate is in \fB\-\-ca file\fP\&. Each peer
+in an OpenVPN link running in TLS mode should have its own certificate
+and private key file. In addition, each certificate should have been
+signed by the key of a certificate authority whose public key resides in
+the \fB\-\-ca\fP certificate authority file. You can easily make your own
+certificate authority (see above) or pay money to use a commercial
+service such as thawte.com (in which case you will be helping to finance
+the world\(aqs second space tourist :). To generate a certificate, you can
+use a command such as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openssl req \-nodes \-new \-keyout mycert.key \-out mycert.csr
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If your certificate authority private key lives on another machine, copy
+the certificate signing request (mycert.csr) to this other machine (this
+can be done over an insecure channel such as email). Now sign the
+certificate with a command such as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openssl ca \-out mycert.crt \-in mycert.csr
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Now copy the certificate (mycert.crt) back to the peer which initially
+generated the .csr file (this can be over a public medium). Note that
+the \fBopenssl ca\fP command reads the location of the certificate
+authority key from its configuration file such as
+\fB/usr/share/ssl/openssl.cnf\fP \-\- note also that for certificate
+authority functions, you must set up the files \fBindex.txt\fP (may be
+empty) and \fBserial\fP (initialize to \fB01\fP).
+.TP
+.BI \-\-crl\-verify \ args
+Check peer certificate against a Certificate Revocation List.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+crl\-verify file/directory flag
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+crl\-verify crl\-file.pem
+crl\-verify /etc/openvpn/crls dir
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+A CRL (certificate revocation list) is used when a particular key is
+compromised but when the overall PKI is still intact.
+.sp
+Suppose you had a PKI consisting of a CA, root certificate, and a number
+of client certificates. Suppose a laptop computer containing a client
+key and certificate was stolen. By adding the stolen certificate to the
+CRL file, you could reject any connection which attempts to use it,
+while preserving the overall integrity of the PKI.
+.sp
+The only time when it would be necessary to rebuild the entire PKI from
+scratch would be if the root certificate key itself was compromised.
+.sp
+The option is not mandatory \- if the relevant CRL is missing, OpenVPN
+will log a warning in the logs \- e.g.
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+VERIFY WARNING: depth=0, unable to get certificate CRL
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+but the connection will be allowed. If the optional \fBdir\fP flag
+is specified, enable a different mode where the \fBcrl\-verify\fP is
+pointed at a directory containing files named as revoked serial numbers
+(the files may be empty, the contents are never read). If a client
+requests a connection, where the client certificate serial number
+(decimal string) is the name of a file present in the directory, it will
+be rejected.
+.INDENT 7.0
+.TP
+.B \fINote:\fP
+As the crl file (or directory) is read every time a peer
+connects, if you are dropping root privileges with
+\fB\-\-user\fP, make sure that this user has sufficient
+privileges to read the file.
+.UNINDENT
+.TP
+.BI \-\-dh \ file
+File containing Diffie Hellman parameters in .pem format (required for
+\fB\-\-tls\-server\fP only).
+.sp
+Set \fBfile\fP to \fBnone\fP to disable Diffie Hellman key exchange (and
+use ECDH only). Note that this requires peers to be using an SSL library
+that supports ECDH TLS cipher suites (e.g. OpenSSL 1.0.1+, or
+mbed TLS 2.0+).
+.sp
+Use \fBopenssl dhparam \-out dh2048.pem 2048\fP to generate 2048\-bit DH
+parameters. Diffie Hellman parameters may be considered public.
+.TP
+.BI \-\-ecdh\-curve \ name
+Specify the curve to use for elliptic curve Diffie Hellman. Available
+curves can be listed with \fB\-\-show\-curves\fP\&. The specified curve will
+only be used for ECDH TLS\-ciphers.
+.sp
+This option is not supported in mbed TLS builds of OpenVPN.
.TP
-.B \-\-tls\-server
-Enable TLS and assume server role during TLS handshake. Note that
-OpenVPN is designed as a peer\-to\-peer application. The designation
-of client or server is only for the purpose of negotiating the TLS
-control channel.
-.\"*********************************************************
+.BI \-\-extra\-certs \ file
+Specify a \fBfile\fP containing one or more PEM certs (concatenated
+together) that complete the local certificate chain.
+.sp
+This option is useful for "split" CAs, where the CA for server certs is
+different than the CA for client certs. Putting certs in this file
+allows them to be used to complete the local certificate chain without
+trusting them to verify the peer\-submitted certificate, as would be the
+case if the certs were placed in the \fBca\fP file.
+.TP
+.BI \-\-hand\-window \ n
+Handshake Window \-\- the TLS\-based key exchange must finalize within
+\fBn\fP seconds of handshake initiation by any peer (default \fB60\fP
+seconds). If the handshake fails we will attempt to reset our connection
+with our peer and try again. Even in the event of handshake failure we
+will still use our expiring key for up to \fB\-\-tran\-window\fP seconds to
+maintain continuity of transmission of tunnel data.
+.TP
+.BI \-\-key \ file
+Local peer\(aqs private key in .pem format. Use the private key which was
+generated when you built your peer\(aqs certificate (see \fB\-\-cert file\fP
+above).
+.TP
+.BI \-\-pkcs12 \ file
+Specify a PKCS #12 file containing local private key, local certificate,
+and root CA certificate. This option can be used instead of \fB\-\-ca\fP,
+\fB\-\-cert\fP, and \fB\-\-key\fP\&. Not available with mbed TLS.
+.TP
+.BI \-\-remote\-cert\-eku \ oid
+Require that peer certificate was signed with an explicit \fIextended key
+usage\fP\&.
+.sp
+This is a useful security option for clients, to ensure that the host
+they connect to is a designated server.
+.sp
+The extended key usage should be encoded in \fIoid notation\fP, or \fIOpenSSL
+symbolic representation\fP\&.
+.TP
+.BI \-\-remote\-cert\-ku \ key\-usage
+Require that peer certificate was signed with an explicit
+\fBkey\-usage\fP\&.
+.sp
+If present in the certificate, the \fBkeyUsage\fP value is validated by
+the TLS library during the TLS handshake. Specifying this option without
+arguments requires this extension to be present (so the TLS library will
+verify it).
+.sp
+If \fBkey\-usage\fP is a list of usage bits, the \fBkeyUsage\fP field
+must have \fIat least\fP the same bits set as the bits in \fIone of\fP the values
+supplied in the \fBkey\-usage\fP list.
+.sp
+The \fBkey\-usage\fP values in the list must be encoded in hex, e.g.
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote\-cert\-ku a0
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-remote\-cert\-tls \ type
+Require that peer certificate was signed with an explicit \fIkey usage\fP
+and \fIextended key usage\fP based on RFC3280 TLS rules.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote\-cert\-tls server
+remote\-cert\-tls client
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This is a useful security option for clients, to ensure that the host
+they connect to is a designated server. Or the other way around; for a
+server to verify that only hosts with a client certificate can connect.
+.sp
+The \fB\-\-remote\-cert\-tls client\fP option is equivalent to
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote\-cert\-ku
+remote\-cert\-eku "TLS Web Client Authentication"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fB\-\-remote\-cert\-tls server\fP option is equivalent to
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote\-cert\-ku
+remote\-cert\-eku "TLS Web Server Authentication"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This is an important security precaution to protect against a
+man\-in\-the\-middle attack where an authorized client attempts to connect
+to another client by impersonating the server. The attack is easily
+prevented by having clients verify the server certificate using any one
+of \fB\-\-remote\-cert\-tls\fP, \fB\-\-verify\-x509\-name\fP, or \fB\-\-tls\-verify\fP\&.
+.TP
+.BI \-\-tls\-auth \ args
+Add an additional layer of HMAC authentication on top of the TLS control
+channel to mitigate DoS attacks and attacks on the TLS stack.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+tls\-auth file
+tls\-auth file 0
+tls\-auth file 1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+In a nutshell, \fB\-\-tls\-auth\fP enables a kind of "HMAC firewall" on
+OpenVPN\(aqs TCP/UDP port, where TLS control channel packets bearing an
+incorrect HMAC signature can be dropped immediately without response.
+.sp
+\fBfile\fP (required) is a file in OpenVPN static key format which can be
+generated by \fB\-\-genkey\fP\&.
+.sp
+Older versions (up to OpenVPN 2.3) supported a freeform passphrase file.
+This is no longer supported in newer versions (v2.4+).
+.sp
+See the \fB\-\-secret\fP option for more information on the optional
+\fBdirection\fP parameter.
+.sp
+\fB\-\-tls\-auth\fP is recommended when you are running OpenVPN in a mode
+where it is listening for packets from any IP address, such as when
+\fB\-\-remote\fP is not specified, or \fB\-\-remote\fP is specified with
+\fB\-\-float\fP\&.
+.sp
+The rationale for this feature is as follows. TLS requires a
+multi\-packet exchange before it is able to authenticate a peer. During
+this time before authentication, OpenVPN is allocating resources (memory
+and CPU) to this potential peer. The potential peer is also exposing
+many parts of OpenVPN and the OpenSSL library to the packets it is
+sending. Most successful network attacks today seek to either exploit
+bugs in programs (such as buffer overflow attacks) or force a program to
+consume so many resources that it becomes unusable. Of course the first
+line of defense is always to produce clean, well\-audited code. OpenVPN
+has been written with buffer overflow attack prevention as a top
+priority. But as history has shown, many of the most widely used network
+applications have, from time to time, fallen to buffer overflow attacks.
+.sp
+So as a second line of defense, OpenVPN offers this special layer of
+authentication on top of the TLS control channel so that every packet on
+the control channel is authenticated by an HMAC signature and a unique
+ID for replay protection. This signature will also help protect against
+DoS (Denial of Service) attacks. An important rule of thumb in reducing
+vulnerability to DoS attacks is to minimize the amount of resources a
+potential, but as yet unauthenticated, client is able to consume.
+.sp
+\fB\-\-tls\-auth\fP does this by signing every TLS control channel packet
+with an HMAC signature, including packets which are sent before the TLS
+level has had a chance to authenticate the peer. The result is that
+packets without the correct signature can be dropped immediately upon
+reception, before they have a chance to consume additional system
+resources such as by initiating a TLS handshake. \fB\-\-tls\-auth\fP can be
+strengthened by adding the \fB\-\-replay\-persist\fP option which will keep
+OpenVPN\(aqs replay protection state in a file so that it is not lost
+across restarts.
+.sp
+It should be emphasized that this feature is optional and that the key
+file used with \fB\-\-tls\-auth\fP gives a peer nothing more than the power
+to initiate a TLS handshake. It is not used to encrypt or authenticate
+any tunnel data.
+.sp
+Use \fB\-\-tls\-crypt\fP instead if you want to use the key file to not only
+authenticate, but also encrypt the TLS control channel.
+.TP
+.BI \-\-tls\-groups \ list
+A list of allowable groups/curves in order of preference.
+.sp
+Set the allowed elliptic curves/groups for the TLS session.
+These groups are allowed to be used in signatures and key exchange.
+.sp
+mbedTLS currently allows all known curves per default.
+.sp
+OpenSSL 1.1+ restricts the list per default to
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+"X25519:secp256r1:X448:secp521r1:secp384r1".
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If you use certificates that use non\-standard curves, you
+might need to add them here. If you do not force the ecdh curve
+by using \fB\-\-ecdh\-curve\fP, the groups for ecdh will also be picked
+from this list.
+.sp
+OpenVPN maps the curve name \fIsecp256r1\fP to \fIprime256v1\fP to allow
+specifying the same tls\-groups option for mbedTLS and OpenSSL.
+.sp
+Warning: this option not only affects elliptic curve certificates
+but also the key exchange in TLS 1.3 and using this option improperly
+will disable TLS 1.3.
+.TP
+.BI \-\-tls\-cert\-profile \ profile
+Set the allowed cryptographic algorithms for certificates according to
+\fBprofile\fP\&.
+.sp
+The following profiles are supported:
+.INDENT 7.0
+.TP
+.B \fBlegacy\fP (default)
+SHA1 and newer, RSA 2048\-bit+, any elliptic curve.
+.TP
+.B \fBpreferred\fP
+SHA2 and newer, RSA 2048\-bit+, any elliptic curve.
+.TP
+.B \fBsuiteb\fP
+SHA256/SHA384, ECDSA with P\-256 or P\-384.
+.UNINDENT
+.sp
+This option is only fully supported for mbed TLS builds. OpenSSL builds
+use the following approximation:
+.INDENT 7.0
+.TP
+.B \fBlegacy\fP (default)
+sets "security level 1"
+.TP
+.B \fBpreferred\fP
+sets "security level 2"
+.TP
+.B \fBsuiteb\fP
+sets "security level 3" and \fB\-\-tls\-cipher "SUITEB128"\fP\&.
+.UNINDENT
+.sp
+OpenVPN will migrate to \(aqpreferred\(aq as default in the future. Please
+ensure that your keys already comply.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \fIWARNING:\fP \fB\-\-tls\-ciphers\fP, \fB\-\-tls\-ciphersuites\fP and \fBtls\-groups\fP
+These options are expert features, which \- if used correctly \- can
+improve the security of your VPN connection. But it is also easy to
+unwittingly use them to carefully align a gun with your foot, or just
+break your connection. Use with care!
+.UNINDENT
+.INDENT 0.0
+.TP
+.BI \-\-tls\-cipher \ l
+A list \fBl\fP of allowable TLS ciphers delimited by a colon ("\fB:\fP").
+.sp
+These setting can be used to ensure that certain cipher suites are used
+(or not used) for the TLS connection. OpenVPN uses TLS to secure the
+control channel, over which the keys that are used to protect the actual
+VPN traffic are exchanged.
+.sp
+The supplied list of ciphers is (after potential OpenSSL/IANA name
+translation) simply supplied to the crypto library. Please see the
+OpenSSL and/or mbed TLS documentation for details on the cipher list
+interpretation.
+.sp
+For OpenSSL, the \fB\-\-tls\-cipher\fP is used for TLS 1.2 and below.
+.sp
+Use \fB\-\-show\-tls\fP to see a list of TLS ciphers supported by your crypto
+library.
+.sp
+The default for \fB\-\-tls\-cipher\fP is to use mbed TLS\(aqs default cipher list
+when using mbed TLS or
+\fBDEFAULT:!EXP:!LOW:!MEDIUM:!kDH:!kECDH:!DSS:!PSK:!SRP:!kRSA\fP when
+using OpenSSL.
+.TP
+.BI \-\-tls\-ciphersuites \ l
+Same as \fB\-\-tls\-cipher\fP but for TLS 1.3 and up. mbed TLS has no
+TLS 1.3 support yet and only the \fB\-\-tls\-cipher\fP setting is used.
+.sp
+The default for \fI\-\-tls\-ciphersuites\fP is to use the crypto library\(aqs
+default.
.TP
.B \-\-tls\-client
Enable TLS and assume client role during TLS handshake.
-.\"*********************************************************
.TP
-.B \-\-ca file
-Certificate authority (CA) file in .pem format, also referred to as the
-.I root
-certificate. This file can have multiple
-certificates in .pem format, concatenated together. You can construct your own
-certificate authority certificate and private key by using a command such as:
-
-.B openssl req \-nodes \-new \-x509 \-keyout ca.key \-out ca.crt
-
-Then edit your openssl.cnf file and edit the
-.B certificate
-variable to point to your new root certificate
-.B ca.crt.
-
-For testing purposes only, the OpenVPN distribution includes a sample
-CA certificate (ca.crt).
-Of course you should never use
-the test certificates and test keys distributed with OpenVPN in a
-production environment, since by virtue of the fact that
-they are distributed with OpenVPN, they are totally insecure.
-.\"*********************************************************
-.TP
-.B \-\-capath dir
-Directory containing trusted certificates (CAs and CRLs).
-Not available with mbed TLS.
-
-CAs in the capath directory are expected to be named <hash>.<n>. CRLs are
-expected to be named <hash>.r<n>. See the
-.B \-CApath
-option of
-.B openssl verify
-, and the
-.B \-hash
-option of
-.B openssl x509
-,
-.B openssl crl
-and
-.BR X509_LOOKUP_hash_dir (3)
-for more information.
-
-Similarly to the
-.B \-\-crl\-verify
-option CRLs are not mandatory \- OpenVPN will log the usual warning in the logs
-if the relevant CRL is missing, but the connection will be allowed.
-.\"*********************************************************
-.TP
-.B \-\-dh file
-File containing Diffie Hellman parameters
-in .pem format (required for
-.B \-\-tls\-server
-only).
-
-Set
-.B file=none
-to disable Diffie Hellman key exchange (and use ECDH only). Note that this
-requires peers to be using an SSL library that supports ECDH TLS cipher suites
-(e.g. OpenSSL 1.0.1+, or mbed TLS 2.0+).
-
-Use
-.B openssl dhparam \-out dh2048.pem 2048
-to generate 2048\-bit DH parameters. Diffie Hellman parameters may be considered
-public.
-.\"*********************************************************
+.BI \-\-tls\-crypt \ keyfile
+Encrypt and authenticate all control channel packets with the key from
+\fBkeyfile\fP\&. (See \fB\-\-tls\-auth\fP for more background.)
+.sp
+Encrypting (and authenticating) control channel packets:
+.INDENT 7.0
+.IP \(bu 2
+provides more privacy by hiding the certificate used for the TLS
+connection,
+.IP \(bu 2
+makes it harder to identify OpenVPN traffic as such,
+.IP \(bu 2
+provides "poor\-man\(aqs" post\-quantum security, against attackers who will
+never know the pre\-shared key (i.e. no forward secrecy).
+.UNINDENT
+.sp
+In contrast to \fB\-\-tls\-auth\fP, \fB\-\-tls\-crypt\fP does \fInot\fP require the
+user to set \fB\-\-key\-direction\fP\&.
+.sp
+\fBSecurity Considerations\fP
+.sp
+All peers use the same \fB\-\-tls\-crypt\fP pre\-shared group key to
+authenticate and encrypt control channel messages. To ensure that IV
+collisions remain unlikely, this key should not be used to encrypt more
+than 2^48 client\-to\-server or 2^48 server\-to\-client control channel
+messages. A typical initial negotiation is about 10 packets in each
+direction. Assuming both initial negotiation and renegotiations are at
+most 2^16 (65536) packets (to be conservative), and (re)negotiations
+happen each minute for each user (24/7), this limits the tls\-crypt key
+lifetime to 8171 years divided by the number of users. So a setup with
+1000 users should rotate the key at least once each eight years. (And a
+setup with 8000 users each year.)
+.sp
+If IV collisions were to occur, this could result in the security of
+\fB\-\-tls\-crypt\fP degrading to the same security as using \fB\-\-tls\-auth\fP\&.
+That is, the control channel still benefits from the extra protection
+against active man\-in\-the\-middle\-attacks and DoS attacks, but may no
+longer offer extra privacy and post\-quantum security on top of what TLS
+itself offers.
+.sp
+For large setups or setups where clients are not trusted, consider using
+\fB\-\-tls\-crypt\-v2\fP instead. That uses per\-client unique keys, and
+thereby improves the bounds to \(aqrotate a client key at least once per
+8000 years\(aq.
+.TP
+.BI \-\-tls\-crypt\-v2 \ keyfile
+Use client\-specific tls\-crypt keys.
+.sp
+For clients, \fBkeyfile\fP is a client\-specific tls\-crypt key. Such a key
+can be generated using the \fB\-\-genkey tls\-crypt\-v2\-client\fP option.
+.sp
+For servers, \fBkeyfile\fP is used to unwrap client\-specific keys supplied
+by the client during connection setup. This key must be the same as the
+key used to generate the client\-specific key (see \fB\-\-genkey
+tls\-crypt\-v2\-client\fP).
+.sp
+On servers, this option can be used together with the \fB\-\-tls\-auth\fP or
+\fB\-\-tls\-crypt\fP option. In that case, the server will detect whether the
+client is using client\-specific keys, and automatically select the right
+mode.
+.TP
+.BI \-\-tls\-crypt\-v2\-verify \ cmd
+Run command \fBcmd\fP to verify the metadata of the client\-specific
+tls\-crypt\-v2 key of a connecting client. This allows server
+administrators to reject client connections, before exposing the TLS
+stack (including the notoriously dangerous X.509 and ASN.1 stacks) to
+the connecting client.
+.sp
+OpenVPN supplies the following environment variables to the command:
+.INDENT 7.0
+.IP \(bu 2
+\fBscript_type\fP is set to \fBtls\-crypt\-v2\-verify\fP
+.IP \(bu 2
+\fBmetadata_type\fP is set to \fB0\fP if the metadata was user
+supplied, or \fB1\fP if it\(aqs a 64\-bit unix timestamp representing
+the key creation time.
+.IP \(bu 2
+\fBmetadata_file\fP contains the filename of a temporary file that
+contains the client metadata.
+.UNINDENT
+.sp
+The command can reject the connection by exiting with a non\-zero exit
+code.
.TP
-.B \-\-ecdh\-curve name
-Specify the curve to use for elliptic curve Diffie Hellman. Available
-curves can be listed with
-.BR \-\-show\-curves .
-The specified curve will only be used for ECDH TLS\-ciphers.
-
-This option is not supported in mbed TLS builds of OpenVPN.
-.\"*********************************************************
-.TP
-.B \-\-cert file
-Local peer's signed certificate in .pem format \-\- must be signed
-by a certificate authority whose certificate is in
-.B \-\-ca file.
-Each peer in an OpenVPN link running in TLS mode should have its own
-certificate and private key file. In addition, each certificate should
-have been signed by the key of a certificate
-authority whose public key resides in the
-.B \-\-ca
-certificate authority file.
-You can easily make your own certificate authority (see above) or pay money
-to use a commercial service such as thawte.com (in which case you will be
-helping to finance the world's second space tourist :).
-To generate a certificate,
-you can use a command such as:
-
-.B openssl req \-nodes \-new \-keyout mycert.key \-out mycert.csr
-
-If your certificate authority private key lives on another machine, copy
-the certificate signing request (mycert.csr) to this other machine (this can
-be done over an insecure channel such as email). Now sign the certificate
-with a command such as:
-
-.B openssl ca \-out mycert.crt \-in mycert.csr
-
-Now copy the certificate (mycert.crt)
-back to the peer which initially generated the .csr file (this
-can be over a public medium).
-Note that the
-.B openssl ca
-command reads the location of the certificate authority key from its
-configuration file such as
-.B /usr/share/ssl/openssl.cnf
-\-\- note also
-that for certificate authority functions, you must set up the files
-.B index.txt
-(may be empty) and
-.B serial
-(initialize to
-.B
-01
-).
-.\"*********************************************************
-.TP
-.B \-\-extra\-certs file
-Specify a
-.B file
-containing one or more PEM certs (concatenated together)
-that complete the
-local certificate chain.
-
-This option is useful for "split" CAs, where the CA for server
-certs is different than the CA for client certs. Putting certs
-in this file allows them to be used to complete the local
-certificate chain without trusting them to verify the peer\-submitted
-certificate, as would be the case if the certs were placed in the
-.B ca
-file.
-.\"*********************************************************
-.TP
-.B \-\-key file
-Local peer's private key in .pem format. Use the private key which was generated
-when you built your peer's certificate (see
-.B \-\-cert file
-above).
-.\"*********************************************************
-.TP
-.B \-\-tls\-version\-min version ['or\-highest']
-Sets the minimum
-TLS version we will accept from the peer (default is "1.0").
-Examples for version
-include "1.0", "1.1", or "1.2". If 'or\-highest' is specified
-and version is not recognized, we will only accept the highest TLS
-version supported by the local SSL implementation.
-.\"*********************************************************
-.TP
-.B \-\-tls\-version\-max version
+.B \-\-tls\-exit
+Exit on TLS negotiation failure.
+.TP
+.BI \-\-tls\-export\-cert \ directory
+Store the certificates the clients use upon connection to this
+directory. This will be done before \fB\-\-tls\-verify\fP is called. The
+certificates will use a temporary name and will be deleted when the
+tls\-verify script returns. The file name used for the certificate is
+available via the \fBpeer_cert\fP environment variable.
+.TP
+.B \-\-tls\-server
+Enable TLS and assume server role during TLS handshake. Note that
+OpenVPN is designed as a peer\-to\-peer application. The designation of
+client or server is only for the purpose of negotiating the TLS control
+channel.
+.TP
+.BI \-\-tls\-timeout \ n
+Packet retransmit timeout on TLS control channel if no acknowledgment
+from remote within \fBn\fP seconds (default \fB2\fP). When OpenVPN sends
+a control packet to its peer, it will expect to receive an
+acknowledgement within \fBn\fP seconds or it will retransmit the packet,
+subject to a TCP\-like exponential backoff algorithm. This parameter only
+applies to control channel packets. Data channel packets (which carry
+encrypted tunnel data) are never acknowledged, sequenced, or
+retransmitted by OpenVPN because the higher level network protocols
+running on top of the tunnel such as TCP expect this role to be left to
+them.
+.TP
+.BI \-\-tls\-version\-min \ args
+Sets the minimum TLS version we will accept from the peer (default is
+"1.0").
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+tls\-version\-min version [\(aqor\-highest\(aq]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Examples for version include \fB1.0\fP, \fB1.1\fP, or \fB1.2\fP\&. If
+\fBor\-highest\fP is specified and version is not recognized, we will
+only accept the highest TLS version supported by the local SSL
+implementation.
+.TP
+.BI \-\-tls\-version\-max \ version
Set the maximum TLS version we will use (default is the highest version
-supported). Examples for version include "1.0", "1.1", or "1.2".
-.\"*********************************************************
-.TP
-.B \-\-pkcs12 file
-Specify a PKCS #12 file containing local private key,
-local certificate, and root CA certificate.
-This option can be used instead of
-.B \-\-ca, \-\-cert,
-and
-.B \-\-key.
-Not available with mbed TLS.
-.\"*********************************************************
-.TP
-.B \-\-verify\-hash hash [algo]
-Specify SHA1 or SHA256 fingerprint for level\-1 cert. The level\-1 cert is the
-CA (or intermediate cert) that signs the leaf certificate, and is
-one removed from the leaf certificate in the direction of the root.
-When accepting a connection from a peer, the level\-1 cert
-fingerprint must match
-.B hash
-or certificate verification will fail. Hash is specified
-as XX:XX:... For example:
-
+supported). Examples for version include \fB1.0\fP, \fB1.1\fP, or
+\fB1.2\fP\&.
+.TP
+.BI \-\-verify\-hash \ args
+Specify SHA1 or SHA256 fingerprint for level\-1 cert.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
+.ft C
+verify\-hash hash [algo]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The level\-1 cert is the CA (or intermediate cert) that signs the leaf
+certificate, and is one removed from the leaf certificate in the
+direction of the root. When accepting a connection from a peer, the
+level\-1 cert fingerprint must match \fBhash\fP or certificate verification
+will fail. Hash is specified as XX:XX:... For example:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
-.in -4
-.ft
+.ft P
.fi
-
-The
-.B algo
-flag can be either SHA1 or SHA256. If not provided, it defaults to SHA1.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.sp
+The \fBalgo\fP flag can be either \fBSHA1\fP or \fBSHA256\fP\&. If not
+provided, it defaults to \fBSHA1\fP\&.
+.TP
+.BI \-\-verify\-x509\-name \ args
+Accept connections only if a host\(aqs X.509 name is equal to \fBname.\fP The
+remote host must also pass all other tests of verification.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+verify\-x509 name type
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Which X.509 name is compared to \fBname\fP depends on the setting of type.
+\fBtype\fP can be \fBsubject\fP to match the complete subject DN
+(default), \fBname\fP to match a subject RDN or \fBname\-prefix\fP to
+match a subject RDN prefix. Which RDN is verified as name depends on the
+\fB\-\-x509\-username\-field\fP option. But it defaults to the common name
+(CN), e.g. a certificate with a subject DN
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+C=KG, ST=NA, L=Bishkek, CN=Server\-1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+would be matched by:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+verify\-x509\-name \(aqC=KG, ST=NA, L=Bishkek, CN=Server\-1\(aq
+verify\-x509\-name Server\-1 name
+verify\-x509\-name Server\- name\-prefix
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The last example is useful if you want a client to only accept
+connections to \fBServer\-1\fP, \fBServer\-2\fP, etc.
+.sp
+\fB\-\-verify\-x509\-name\fP is a useful replacement for the \fB\-\-tls\-verify\fP
+option to verify the remote host, because \fB\-\-verify\-x509\-name\fP works
+in a \fB\-\-chroot\fP environment without any dependencies.
+.sp
+Using a name prefix is a useful alternative to managing a CRL
+(Certificate Revocation List) on the client, since it allows the client
+to refuse all certificates except for those associated with designated
+servers.
+.INDENT 7.0
+.TP
+.B \fINOTE:\fP
+Test against a name prefix only when you are using OpenVPN
+with a custom CA certificate that is under your control. Never use
+this option with type \fBname\-prefix\fP when your client
+certificates are signed by a third party, such as a commercial
+web CA.
+.UNINDENT
+.TP
+.BI \-\-x509\-track \ attribute
+Save peer X509 \fBattribute\fP value in environment for use by plugins and
+management interface. Prepend a \fB+\fP to \fBattribute\fP to save values
+from full cert chain. Values will be encoded as
+\fBX509_<depth>_<attribute>=<value>\fP\&. Multiple \fB\-\-x509\-track\fP
+options can be defined to track multiple attributes.
.TP
-.B \-\-pkcs11\-cert\-private [0|1]...
+.BI \-\-x509\-username\-field \ args
+Field in the X.509 certificate subject to be used as the username
+(default \fBCN\fP).
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+x509\-username\-field [ext:]fieldname
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Typically, this option is specified with \fBfieldname\fP as
+either of the following:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+x509\-username\-field emailAddress
+x509\-username\-field ext:subjectAltName
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The first example uses the value of the \fBemailAddress\fP attribute
+in the certificate\(aqs Subject field as the username. The second example
+uses the \fBext:\fP prefix to signify that the X.509 extension
+\fBfieldname\fP \fBsubjectAltName\fP be searched for an rfc822Name
+(email) field to be used as the username. In cases where there are
+multiple email addresses in \fBext:fieldname\fP, the last occurrence
+is chosen.
+.sp
+When this option is used, the \fB\-\-verify\-x509\-name\fP option will match
+against the chosen \fBfieldname\fP instead of the Common Name.
+.sp
+Only the \fBsubjectAltName\fP and \fBissuerAltName\fP X.509
+extensions are supported.
+.sp
+\fBPlease note:\fP This option has a feature which will convert an
+all\-lowercase \fBfieldname\fP to uppercase characters, e.g.,
+\fBou\fP \-> \fBOU\fP\&. A mixed\-case \fBfieldname\fP or one having the
+\fBext:\fP prefix will be left as\-is. This automatic upcasing feature is
+deprecated and will be removed in a future release.
+.UNINDENT
+.SS PKCS#11 / SmartCard options
+.INDENT 0.0
+.TP
+.BI \-\-pkcs11\-cert\-private \ args
Set if access to certificate object should be performed after login.
Every provider has its own setting.
-.\"*********************************************************
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+pkcs11\-cert\-private 0
+pkcs11\-cert\-private 1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
.TP
-.B \-\-pkcs11\-id name
+.BI \-\-pkcs11\-id \ name
Specify the serialized certificate id to be used. The id can be gotten
-by the standalone
-.B \-\-show\-pkcs11\-ids
-option.
-.\"*********************************************************
+by the standalone \fB\-\-show\-pkcs11\-ids\fP option.
.TP
.B \-\-pkcs11\-id\-management
-Acquire PKCS#11 id from management interface. In this case a NEED\-STR 'pkcs11\-id\-request'
-real\-time message will be triggered, application may use pkcs11\-id\-count command to
-retrieve available number of certificates, and pkcs11\-id\-get command to retrieve certificate
-id and certificate body.
-.\"*********************************************************
-.TP
-.B \-\-pkcs11\-pin\-cache seconds
-Specify how many seconds the PIN can be cached, the default is until the token is removed.
-.\"*********************************************************
-.TP
-.B \-\-pkcs11\-protected\-authentication [0|1]...
-Use PKCS#11 protected authentication path, useful for biometric and external
-keypad devices.
-Every provider has its own setting.
-.\"*********************************************************
-.TP
-.B \-\-pkcs11\-providers provider...
-Specify a RSA Security Inc. PKCS #11 Cryptographic Token Interface (Cryptoki) providers
-to load.
-This option can be used instead of
-.B \-\-cert, \-\-key,
-and
-.B \-\-pkcs12.
-
-If p11\-kit is present on the system, its
-.B p11\-kit\-proxy.so
-module will be loaded by default if either the
-.B \-\-pkcs11\-id
-or
-.B \-\-pkcs11\-id\-management
-options are specified without
-.B \-\-pkcs11\-provider
-being given.
-.\"*********************************************************
+Acquire PKCS#11 id from management interface. In this case a
+\fBNEED\-STR \(aqpkcs11\-id\-request\(aq\fP real\-time message will be triggered,
+application may use pkcs11\-id\-count command to retrieve available number of
+certificates, and pkcs11\-id\-get command to retrieve certificate id and
+certificate body.
.TP
-.B \-\-pkcs11\-private\-mode mode...
-Specify which method to use in order to perform private key operations.
-A different mode can be specified for each provider.
-Mode is encoded as hex number, and can be a mask one of the following:
-
-.B 0
-(default) \-\- Try to determine automatically.
-.br
-.B 1
-\-\- Use sign.
-.br
-.B 2
-\-\- Use sign recover.
-.br
-.B 4
-\-\- Use decrypt.
-.br
-.B 8
-\-\- Use unwrap.
-.br
-.\"*********************************************************
-.TP
-.B \-\-cryptoapicert select\-string
-Load the certificate and private key from the
-Windows Certificate System Store (Windows/OpenSSL Only).
-
-Use this option instead of
-.B \-\-cert
-and
-.B \-\-key.
-
-This makes
-it possible to use any smart card, supported by Windows, but also any
-kind of certificate, residing in the Cert Store, where you have access to
-the private key. This option has been tested with a couple of different
-smart cards (GemSAFE, Cryptoflex, and Swedish Post Office eID) on the
-client side, and also an imported PKCS12 software certificate on the
-server side.
-
-To select a certificate, based on a substring search in the
-certificate's subject:
-
-.B cryptoapicert
-"SUBJ:Peter Runestig"
-
-To select a certificate, based on certificate's thumbprint:
-
-.B cryptoapicert
-"THUMB:f6 49 24 41 01 b4 ..."
-
-The thumbprint hex string can easily be copy\-and\-pasted from the Windows
-Certificate Store GUI.
-
-.\"*********************************************************
+.BI \-\-pkcs11\-pin\-cache \ seconds
+Specify how many seconds the PIN can be cached, the default is until the
+token is removed.
.TP
-.B \-\-key\-method m
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5
-
-Use data channel key negotiation method
-.B m.
-The key method must match on both sides of the connection.
-
-After OpenVPN negotiates a TLS session, a new set of keys
-for protecting the tunnel data channel is generated and
-exchanged over the TLS session.
-
-In method 1 (the default for OpenVPN 1.x), both sides generate
-random encrypt and HMAC\-send keys which are forwarded to
-the other host over the TLS channel. Method 1 is
-.B deprecated in OpenVPN 2.4
-, and
-.B will be removed in OpenVPN 2.5\fR.
-
-In method 2, (the default for OpenVPN 2.0)
-the client generates a random key. Both client
-and server also generate some random seed material. All key source
-material is exchanged over the TLS channel. The actual
-keys are generated using the TLS PRF function, taking source
-entropy from both client and server. Method 2 is designed to
-closely parallel the key generation process used by TLS 1.0.
-
-Note that in TLS mode, two separate levels
-of keying occur:
-
-(1) The TLS connection is initially negotiated, with both sides
-of the connection producing certificates and verifying the certificate
-(or other authentication info provided) of
-the other side. The
-.B \-\-key\-method
-parameter has no effect on this process.
-
-(2) After the TLS connection is established, the tunnel session keys are
-separately negotiated over the existing secure TLS channel. Here,
-.B \-\-key\-method
-determines the derivation of the tunnel session keys.
-.\"*********************************************************
-.TP
-.B \-\-tls\-cipher l
-.TQ
-.B \-\-tls\-ciphersuites l
-A list
-.B l
-of allowable TLS ciphers delimited by a colon (":").
-
-These setting can be used to ensure that certain cipher suites are used (or
-not used) for the TLS connection. OpenVPN uses TLS to secure the control
-channel, over which the keys that are used to protect the actual VPN traffic
-are exchanged.
-
-The supplied list of ciphers is (after potential OpenSSL/IANA name translation)
-simply supplied to the crypto library. Please see the OpenSSL and/or mbed TLS
-documentation for details on the cipher list interpretation.
-
-For OpenSSL, the
-.B \-\-tls-cipher
-is used for TLS 1.2 and below. For TLS 1.3 and up, the
-.B \-\-tls\-ciphersuites
-setting is used. mbed TLS has no TLS 1.3 support yet and only the
-.B \-\-tls-cipher
-setting is used.
-
-Use
-.B \-\-show\-tls
-to see a list of TLS ciphers supported by your crypto library.
-
-Warning!
-.B \-\-tls\-cipher
-and
-.B \-\-tls\-ciphersuites
-are expert features, which \- if used correcly \- can improve the security of
-your VPN connection. But it is also easy to unwittingly use them to carefully
-align a gun with your foot, or just break your connection. Use with care!
-
-The default for \-\-tls\-cipher is to use mbed TLS's default cipher list
-when using mbed TLS or
-"DEFAULT:!EXP:!LOW:!MEDIUM:!kDH:!kECDH:!DSS:!PSK:!SRP:!kRSA" when using
-OpenSSL.
-
-The default for \-\-tls\-ciphersuites is to use the crypto library's default.
-.\"*********************************************************
+.BI \-\-pkcs11\-private\-mode \ mode
+Specify which method to use in order to perform private key operations.
+A different mode can be specified for each provider. Mode is encoded as
+hex number, and can be a mask one of the following:
+.sp
+\fB0\fP (default) Try to determine automatically.
+.sp
+\fB1\fP Use sign.
+.sp
+\fB2\fP Use sign recover.
+.sp
+\fB4\fP Use decrypt.
+.sp
+\fB8\fP Use unwrap.
+.TP
+.BI \-\-pkcs11\-protected\-authentication \ args
+Use PKCS#11 protected authentication path, useful for biometric and
+external keypad devices. Every provider has its own setting.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+pkcs11\-protected\-authentication 0
+pkcs11\-protected\-authentication 1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-pkcs11\-providers \ provider
+Specify an RSA Security Inc. PKCS #11 Cryptographic Token Interface
+(Cryptoki) providers to load. This option can be used instead of
+\fB\-\-cert\fP, \fB\-\-key\fP and \fB\-\-pkcs12\fP\&.
+.sp
+If p11\-kit is present on the system, its \fBp11\-kit\-proxy.so\fP module
+will be loaded by default if either the \fB\-\-pkcs11\-id\fP or
+\fB\-\-pkcs11\-id\-management\fP options are specified without
+\fB\-\-pkcs11\-provider\fP being given.
+.TP
+.BI \-\-show\-pkcs11\-ids \ args
+(Standalone) Show PKCS#11 token object list.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+show\-pkcs11 [provider] [cert_private]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Specify \fBcert_private\fP as \fB1\fP if certificates are stored as
+private objects.
+.sp
+If \fIp11\-kit\fP is present on the system, the \fBprovider\fP argument is
+optional; if omitted the default \fBp11\-kit\-proxy.so\fP module will be
+queried.
+.sp
+\fB\-\-verb\fP option can be used BEFORE this option to produce debugging
+information.
+.UNINDENT
+.SH DATA CHANNEL CIPHER NEGOTIATION
+.sp
+OpenVPN 2.4 and higher have the capability to negotiate the data cipher that
+is used to encrypt data packets. This section describes the mechanism in more detail and the
+different backwards compatibility mechanism with older server and clients.
+.SS OpenVPN 2.5 and higher behaviour
+.sp
+When both client and server are at least running OpenVPN 2.5, that the order of
+the ciphers of the server\(aqs \fB\-\-data\-ciphers\fP is used to pick the the data cipher.
+That means that the first cipher in that list that is also in the client\(aqs
+\fB\-\-data\-ciphers\fP list is chosen. If no common cipher is found the client is rejected
+with a AUTH_FAILED message (as seen in client log):
+.INDENT 0.0
+.INDENT 3.5
+AUTH: Received control message: AUTH_FAILED,Data channel cipher negotiation failed (no shared cipher)
+.UNINDENT
+.UNINDENT
+.sp
+OpenVPN 2.5 will only allow the ciphers specified in \fB\-\-data\-ciphers\fP\&. To ensure
+backwards compatibility also if a cipher is specified using the \fB\-\-cipher\fP option
+it is automatically added to this list. If both options are unset the default is
+\fBAES\-256\-GCM:AES\-128\-GCM\fP\&.
+.SS OpenVPN 2.4 clients
+.sp
+The negotiation support in OpenVPN 2.4 was the first iteration of the implementation
+and still had some quirks. Its main goal was "upgrade to AES\-256\-GCM when possible".
+An OpenVPN 2.4 client that is built against a crypto library that supports AES in GCM
+mode and does not have \fB\-\-ncp\-disable\fP will always announce support for
+\fIAES\-256\-GCM\fP and \fIAES\-128\-GCM\fP to a server by sending \fBIV_NCP=2\fP\&.
+.sp
+This only causes a problem if \fB\-\-ncp\-ciphers\fP option has been changed from the
+default of \fBAES\-256\-GCM:AES\-128\-GCM\fP to a value that does not include
+these two ciphers. When a OpenVPN servers try to use \fIAES\-256\-GCM\fP or
+\fIAES\-128\-GCM\fP the connection will then fail. It is therefore recommended to
+always have the \fIAES\-256\-GCM\fP and \fIAES\-128\-GCM\fP ciphers to the \fB\-\-ncp\-ciphers\fP
+options to avoid this behaviour.
+.SS OpenVPN 3 clients
+.sp
+Clients based on the OpenVPN 3.x library (\fI\%https://github.com/openvpn/openvpn3/\fP)
+do not have a configurable \fB\-\-ncp\-ciphers\fP or \fB\-\-data\-ciphers\fP option. Instead
+these clients will announce support for all their supported AEAD ciphers
+(\fIAES\-256\-GCM\fP, \fIAES\-128\-GCM\fP and in newer versions also \fIChacha20\-Poly1305\fP).
+.sp
+To support OpenVPN 3.x based clients at least one of these ciphers needs to be
+included in the server\(aqs \fB\-\-data\-ciphers\fP option.
+.SS OpenVPN 2.3 and older clients (and clients with \fB\-\-ncp\-disable\fP)
+.sp
+When a client without cipher negotiation support connects to a server the
+cipher specified with the \fB\-\-cipher\fP option in the client configuration
+must be included in the \fB\-\-data\-ciphers\fP option of the server to allow
+the client to connect. Otherwise the client will be sent the \fBAUTH_FAILED\fP
+message that indicates no shared cipher.
+.sp
+If the client is 2.3 or older and has been configured with the
+\fB\-\-enable\-small\fP \fB\&./configure\fP argument, using
+\fBdata\-ciphers\-fallback cipher\fP in the server config file with the explicit
+cipher used by the client is necessary.
+.SS OpenVPN 2.4 server
+.sp
+When a client indicates support for \fIAES\-128\-GCM\fP and \fIAES\-256\-GCM\fP
+(with \fBIV_NCP=2\fP) an OpenVPN 2.4 server will send the first
+cipher of the \fB\-\-ncp\-ciphers\fP to the OpenVPN client regardless of what
+the cipher is. To emulate the behaviour of an OpenVPN 2.4 client as close
+as possible and have compatibility to a setup that depends on this quirk,
+adding \fIAES\-128\-GCM\fP and \fIAES\-256\-GCM\fP to the client\(aqs \fB\-\-data\-ciphers\fP
+option is required. OpenVPN 2.5+ will only announce the \fBIV_NCP=2\fP flag if
+those ciphers are present.
+.SS OpenVPN 2.3 and older servers (and servers with \fB\-\-ncp\-disable\fP)
+.sp
+The cipher used by the server must be included in \fB\-\-data\-ciphers\fP to
+allow the client connecting to a server without cipher negotiation
+support.
+(For compatibility OpenVPN 2.5 will also accept the cipher set with
+\fB\-\-cipher\fP)
+.sp
+If the server is 2.3 or older and has been configured with the
+\fB\-\-enable\-small\fP \fB\&./configure\fP argument, adding
+\fBdata\-ciphers\-fallback cipher\fP to the client config with the explicit
+cipher used by the server is necessary.
+.SS Blowfish in CBC mode (BF\-CBC) deprecation
+.sp
+The \fB\-\-cipher\fP option defaulted to \fBBF\-CBC\fP in OpenVPN 2.4 and older
+version. The default was never changed to ensure backwards compatibility.
+In OpenVPN 2.5 this behaviour has now been changed so that if the \fB\-\-cipher\fP
+is not explicitly set it does not allow the weak \fBBF\-CBC\fP cipher any more
+and needs to explicitly added as \fB\-\-cipher BFC\-CBC\fP or added to
+\fB\-\-data\-ciphers\fP\&.
+.sp
+We strongly recommend to switching away from BF\-CBC to a
+more secure cipher as soon as possible instead.
+.SH NETWORK CONFIGURATION
+.sp
+OpenVPN consists of two sides of network configuration. One side is the
+\fIlink\fP between the local and remote side, the other side is the \fIvirtual
+network adapter\fP (tun/tap device).
+.SS Link Options
+.sp
+This link options section covers options related to the connection between
+the local and the remote host.
+.INDENT 0.0
+.TP
+.BI \-\-bind \ keywords
+Bind to local address and port. This is the default unless any of
+\fB\-\-proto tcp\-client\fP , \fB\-\-http\-proxy\fP or \fB\-\-socks\-proxy\fP are used.
+.sp
+If the optional \fBipv6only\fP keyword is present OpenVPN will bind only
+to IPv6 (as opposed to IPv6 and IPv4) when a IPv6 socket is opened.
.TP
-.B \-\-tls\-cert\-profile profile
-Set the allowed cryptographic algorithms for certificates according to
-.B profile\fN.
-
-The following profiles are supported:
-
-.B legacy
-(default): SHA1 and newer, RSA 2048-bit+, any elliptic curve.
-
-.B preferred
-: SHA2 and newer, RSA 2048-bit+, any elliptic curve.
-
-.B suiteb
-: SHA256/SHA384, ECDSA with P-256 or P-384.
-
-This option is only fully supported for mbed TLS builds. OpenSSL builds use
-the following approximation:
-
-.B legacy
-(default): sets "security level 1"
-
-.B preferred
-: sets "security level 2"
-
-.B suiteb
-: sets "security level 3" and \-\-tls\-cipher "SUITEB128".
-
-OpenVPN will migrate to 'preferred' as default in the future. Please ensure
-that your keys already comply.
-.\"*********************************************************
-.TP
-.B \-\-tls\-timeout n
-Packet retransmit timeout on TLS control channel
-if no acknowledgment from remote within
-.B n
-seconds (default=2). When OpenVPN sends a control
-packet to its peer, it will expect to receive an
-acknowledgement within
-.B n
-seconds or it will retransmit the packet, subject
-to a TCP\-like exponential backoff algorithm. This parameter
-only applies to control channel packets. Data channel
-packets (which carry encrypted tunnel data) are never
-acknowledged, sequenced, or retransmitted by OpenVPN because
-the higher level network protocols running on top of the tunnel
-such as TCP expect this role to be left to them.
-.\"*********************************************************
-.TP
-.B \-\-reneg\-bytes n
-Renegotiate data channel key after
-.B n
-bytes sent or received (disabled by default with an exception, see below).
-OpenVPN allows the lifetime of a key
-to be expressed as a number of bytes encrypted/decrypted, a number of packets,
-or a number of seconds. A key renegotiation will be forced
-if any of these three criteria are met by either peer.
-
-If using ciphers with cipher block sizes less than 128\-bits, \-\-reneg\-bytes is
-set to 64MB by default, unless it is explicitly disabled by setting the value to
-0, but this is
-.B HIGHLY DISCOURAGED
-as this is designed to add some protection against the SWEET32 attack vector.
-For more information see the \-\-cipher option.
-.\"*********************************************************
-.TP
-.B \-\-reneg\-pkts n
-Renegotiate data channel key after
-.B n
-packets sent and received (disabled by default).
-.\"*********************************************************
-.TP
-.B \-\-reneg\-sec n
-Renegotiate data channel key after
-.B n
-seconds (default=3600).
-
-When using dual\-factor authentication, note that this default value may
-cause the end user to be challenged to reauthorize once per hour.
-
-Also, keep in mind that this option can be used on both the client and server,
-and whichever uses the lower value will be the one to trigger the renegotiation.
-A common mistake is to set
-.B \-\-reneg\-sec
-to a higher value on either the client or server, while the other side of the connection
-is still using the default value of 3600 seconds, meaning that the renegotiation will
-still occur once per 3600 seconds. The solution is to increase \-\-reneg\-sec on both the
-client and server, or set it to 0 on one side of the connection (to disable), and to
-your chosen value on the other side.
-.\"*********************************************************
-.TP
-.B \-\-hand\-window n
-Handshake Window \-\- the TLS\-based key exchange must finalize within
-.B n
-seconds
-of handshake initiation by any peer (default = 60 seconds).
-If the handshake fails
-we will attempt to reset our connection with our peer and try again.
-Even in the event of handshake failure we will still use
-our expiring key for up to
-.B \-\-tran\-window
-seconds to maintain continuity of transmission of tunnel
-data.
-.\"*********************************************************
-.TP
-.B \-\-tran\-window n
-Transition window \-\- our old key can live this many seconds
-after a new a key renegotiation begins (default = 3600 seconds).
-This feature allows for a graceful transition from old to new
-key, and removes the key renegotiation sequence from the critical
-path of tunnel data forwarding.
-.\"*********************************************************
+.B \-\-float
+Allow remote peer to change its IP address and/or port number, such as
+due to DHCP (this is the default if \fB\-\-remote\fP is not used).
+\fB\-\-float\fP when specified with \fB\-\-remote\fP allows an OpenVPN session
+to initially connect to a peer at a known address, however if packets
+arrive from a new address and pass all authentication tests, the new
+address will take control of the session. This is useful when you are
+connecting to a peer which holds a dynamic address such as a dial\-in
+user or DHCP client.
+.sp
+Essentially, \fB\-\-float\fP tells OpenVPN to accept authenticated packets
+from any address, not only the address which was specified in the
+\fB\-\-remote\fP option.
+.TP
+.BI \-\-fragment \ max
+Enable internal datagram fragmentation so that no UDP datagrams are sent
+which are larger than \fBmax\fP bytes.
+.sp
+The \fBmax\fP parameter is interpreted in the same way as the
+\fB\-\-link\-mtu\fP parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including the UDP header itself.
+.sp
+The \fB\-\-fragment\fP option only makes sense when you are using the UDP
+protocol (\fB\-\-proto udp\fP).
+.sp
+\fB\-\-fragment\fP adds 4 bytes of overhead per datagram.
+.sp
+See the \fB\-\-mssfix\fP option below for an important related option to
+\fB\-\-fragment\fP\&.
+.sp
+It should also be noted that this option is not meant to replace UDP
+fragmentation at the IP stack level. It is only meant as a last resort
+when path MTU discovery is broken. Using this option is less efficient
+than fixing path MTU discovery for your IP link and using native IP
+fragmentation instead.
+.sp
+Having said that, there are circumstances where using OpenVPN\(aqs internal
+fragmentation capability may be your only option, such as tunneling a
+UDP multicast stream which requires fragmentation.
+.TP
+.BI \-\-keepalive \ args
+A helper directive designed to simplify the expression of \fB\-\-ping\fP and
+\fB\-\-ping\-restart\fP\&.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+keepalive interval timeout
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This option can be used on both client and server side, but it is enough
+to add this on the server side as it will push appropriate \fB\-\-ping\fP
+and \fB\-\-ping\-restart\fP options to the client. If used on both server and
+client, the values pushed from server will override the client local
+values.
+.sp
+The \fBtimeout\fP argument will be twice as long on the server side. This
+ensures that a timeout is detected on client side before the server side
+drops the connection.
+.sp
+For example, \fB\-\-keepalive 10 60\fP expands as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+if mode server:
+ ping 10 # Argument: interval
+ ping\-restart 120 # Argument: timeout*2
+ push "ping 10" # Argument: interval
+ push "ping\-restart 60" # Argument: timeout
+else
+ ping 10 # Argument: interval
+ ping\-restart 60 # Argument: timeout
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
.TP
-.B \-\-single\-session
-After initially connecting to a remote peer, disallow any new connections.
-Using this
-option means that a remote peer cannot connect, disconnect, and then
-reconnect.
-
-If the daemon is reset by a signal or
-.B \-\-ping\-restart,
-it will allow one new connection.
-
-.B \-\-single\-session
-can be used with
-.B \-\-ping\-exit
-or
-.B \-\-inactive
-to create a single dynamic session that will exit when finished.
-.\"*********************************************************
+.BI \-\-link\-mtu \ n
+Sets an upper bound on the size of UDP packets which are sent between
+OpenVPN peers. \fIIt\(aqs best not to set this parameter unless you know what
+you\(aqre doing.\fP
.TP
-.B \-\-tls\-exit
-Exit on TLS negotiation failure.
-.\"*********************************************************
+.BI \-\-local \ host
+Local host name or IP address for bind. If specified, OpenVPN will bind
+to this address only. If unspecified, OpenVPN will bind to all
+interfaces.
.TP
-.B \-\-tls\-auth file [direction]
-Add an additional layer of HMAC authentication on top of the TLS control channel
-to mitigate DoS attacks and attacks on the TLS stack.
-
-In a nutshell,
-.B \-\-tls\-auth
-enables a kind of "HMAC firewall" on OpenVPN's TCP/UDP port,
-where TLS control channel packets
-bearing an incorrect HMAC signature can be dropped immediately without
-response.
-
-.B file
-(required) is a file in OpenVPN static key format which can be generated by
-.B \-\-genkey
-
-Older versions (up to OpenVPN 2.3) supported a freeform passphrase file.
-This is no longer supported in newer versions (v2.4+).
-
-See the
-.B \-\-secret
-option for more information on the optional
-.B direction
-parameter.
-
-.B \-\-tls\-auth
-is recommended when you are running OpenVPN in a mode where
-it is listening for packets from any IP address, such as when
-.B \-\-remote
-is not specified, or
-.B \-\-remote
-is specified with
-.B \-\-float.
-
-The rationale for
-this feature is as follows. TLS requires a multi\-packet exchange
-before it is able to authenticate a peer. During this time
-before authentication, OpenVPN is allocating resources (memory
-and CPU) to this potential peer. The potential peer is also
-exposing many parts of OpenVPN and the OpenSSL library to the packets
-it is sending. Most successful network attacks today seek
-to either exploit bugs in programs (such as buffer overflow attacks) or
-force a program to consume so many resources that it becomes unusable.
-Of course the first line of defense is always to produce clean,
-well\-audited code. OpenVPN has been written with buffer overflow
-attack prevention as a top priority.
-But as history has shown, many of the most widely used
-network applications have, from time to time,
-fallen to buffer overflow attacks.
-
-So as a second line of defense, OpenVPN offers
-this special layer of authentication on top of the TLS control channel so that
-every packet on the control channel is authenticated by an
-HMAC signature and a unique ID for replay protection.
-This signature will also help protect against DoS (Denial of Service) attacks.
-An important rule of thumb in reducing vulnerability to DoS attacks is to
-minimize the amount of resources a potential, but as yet unauthenticated,
-client is able to consume.
-
-.B \-\-tls\-auth
-does this by signing every TLS control channel packet with an HMAC signature,
-including packets which are sent before the TLS level has had a chance
-to authenticate the peer.
-The result is that packets without
-the correct signature can be dropped immediately upon reception,
-before they have a chance to consume additional system resources
-such as by initiating a TLS handshake.
-.B \-\-tls\-auth
-can be strengthened by adding the
-.B \-\-replay\-persist
-option which will keep OpenVPN's replay protection state
-in a file so that it is not lost across restarts.
-
-It should be emphasized that this feature is optional and that the
-key file used with
-.B \-\-tls\-auth
-gives a peer nothing more than the power to initiate a TLS
-handshake. It is not used to encrypt or authenticate any tunnel data.
-
-Use
-.B \-\-tls\-crypt
-instead if you want to use the key file to not only authenticate, but also
-encrypt the TLS control channel.
-.\"*********************************************************
+.BI \-\-lport \ port
+Set local TCP/UDP port number or name. Cannot be used together with
+\fB\-\-nobind\fP option.
.TP
-.B \-\-tls\-crypt keyfile
-
-Encrypt and authenticate all control channel packets with the key from
-.B keyfile.
-(See
-.B \-\-tls\-auth
-for more background.)
-
-Encrypting (and authenticating) control channel packets:
-.RS
-.IP \[bu] 2
-provides more privacy by hiding the certificate used for the TLS connection,
-.IP \[bu]
-makes it harder to identify OpenVPN traffic as such,
-.IP \[bu]
-provides "poor\-man's" post\-quantum security, against attackers who will never
-know the pre\-shared key (i.e. no forward secrecy).
-.RE
-
-.IP
-In contrast to
-.B \-\-tls\-auth\fR,
-.B \-\-tls\-crypt
-does *not* require the user to set
-.B \-\-key\-direction\fR.
-
-.B Security Considerations
-
-All peers use the same
-.B \-\-tls\-crypt
-pre\-shared group key to authenticate and encrypt control channel messages. To
-ensure that IV collisions remain unlikely, this key should not be used to
-encrypt more than 2^48 client\-to\-server or 2^48 server\-to\-client control
-channel messages. A typical initial negotiation is about 10 packets in each
-direction. Assuming both initial negotiation and renegotiations are at most
-2^16 (65536) packets (to be conservative), and (re)negotiations happen each
-minute for each user (24/7), this limits the tls\-crypt key lifetime to 8171
-years divided by the number of users. So a setup with 1000 users should rotate
-the key at least once each eight years. (And a setup with 8000 users each
-year.)
-
-If IV collisions were to occur, this could result in the security of
-.B \-\-tls\-crypt
-degrading to the same security as using
-.B \-\-tls\-auth\fR.
-That is, the control channel still benefits from the extra protection against
-active man\-in\-the\-middle\-attacks and DoS attacks, but may no longer offer
-extra privacy and post\-quantum security on top of what TLS itself offers.
-.\"*********************************************************
-.TP
-.B \-\-askpass [file]
-Get certificate password from console or
-.B file
-before we daemonize.
-
-For the extremely
-security conscious, it is possible to protect your private key with
-a password. Of course this means that every time the OpenVPN
-daemon is started you must be there to type the password. The
-.B \-\-askpass
-option allows you to start OpenVPN from the command line. It will
-query you for a password before it daemonizes. To protect a private
-key with a password you should omit the
-.B \-nodes
-option when you use the
-.B openssl
-command line tool to manage certificates and private keys.
-
-If
-.B file
-is specified, read the password from the first line of
-.B file.
-Keep in mind that storing your password in a file
-to a certain extent invalidates the extra security provided by
-using an encrypted key.
-.\"*********************************************************
+.BI \-\-mark \ value
+Mark encrypted packets being sent with value. The mark value can be
+matched in policy routing and packetfilter rules. This option is only
+supported in Linux and does nothing on other operating systems.
.TP
-.B \-\-auth\-nocache
-Don't cache
-.B \-\-askpass
-or
-.B \-\-auth\-user\-pass
-username/passwords in virtual memory.
-
-If specified, this directive will cause OpenVPN to immediately
-forget username/password inputs after they are used. As a result,
-when OpenVPN needs a username/password, it will prompt for input
-from stdin, which may be multiple times during the duration of an
-OpenVPN session.
-
-When using \-\-auth\-nocache in combination with a user/password file
-and \-\-chroot or \-\-daemon, make sure to use an absolute path.
-
-This directive does not affect the
-.B \-\-http\-proxy
-username/password. It is always cached.
-.\"*********************************************************
+.BI \-\-mode \ m
+Set OpenVPN major mode. By default, OpenVPN runs in point\-to\-point mode
+(\fBp2p\fP). OpenVPN 2.0 introduces a new mode (\fBserver\fP) which
+implements a multi\-client server capability.
.TP
-.B \-\-auth\-token token
-This is not an option to be used directly in any configuration files,
-but rather push this option from a
-.B \-\-client\-connect
-script or a
-.B \-\-plugin
-which hooks into the OPENVPN_PLUGIN_CLIENT_CONNECT or
-OPENVPN_PLUGIN_CLIENT_CONNECT_V2 calls. This option provides
-a possibility to replace the clients password with an authentication
-token during the lifetime of the OpenVPN client.
-
-Whenever the connection is renegotiated and the
-.B \-\-auth\-user\-pass\-verify
-script or
-.B \-\-plugin
-making use of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY hook is
-triggered, it will pass over this token as the password
-instead of the password the user provided. The authentication
-token can only be reset by a full reconnect where the server
-can push new options to the client. The password the user entered
-is never preserved once an authentication token have been set. If
-the OpenVPN server side rejects the authentication token, the
-client will receive an AUTH_FAIL and disconnect.
-
-The purpose of this is to enable two factor authentication
-methods, such as HOTP or TOTP, to be used without needing to
-retrieve a new OTP code each time the connection is renegotiated.
-Another use case is to cache authentication data on the client
-without needing to have the users password cached in memory
-during the life time of the session.
-
-To make use of this feature, the
-.B \-\-client\-connect
-script or
-.B \-\-plugin
-needs to put
-
+.BI \-\-mssfix \ max
+Announce to TCP sessions running over the tunnel that they should limit
+their send packet sizes such that after OpenVPN has encapsulated them,
+the resulting UDP packet size that OpenVPN sends to its peer will not
+exceed \fBmax\fP bytes. The default value is \fB1450\fP\&.
+.sp
+The \fBmax\fP parameter is interpreted in the same way as the
+\fB\-\-link\-mtu\fP parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including the UDP header itself.
+Resulting packet would be at most 28 bytes larger for IPv4 and 48 bytes
+for IPv6 (20/40 bytes for IP header and 8 bytes for UDP header). Default
+value of 1450 allows IPv4 packets to be transmitted over a link with MTU
+1473 or higher without IP level fragmentation.
+.sp
+The \fB\-\-mssfix\fP option only makes sense when you are using the UDP
+protocol for OpenVPN peer\-to\-peer communication, i.e. \fB\-\-proto udp\fP\&.
+.sp
+\fB\-\-mssfix\fP and \fB\-\-fragment\fP can be ideally used together, where
+\fB\-\-mssfix\fP will try to keep TCP from needing packet fragmentation in
+the first place, and if big packets come through anyhow (from protocols
+other than TCP), \fB\-\-fragment\fP will internally fragment them.
+.sp
+Both \fB\-\-fragment\fP and \fB\-\-mssfix\fP are designed to work around cases
+where Path MTU discovery is broken on the network path between OpenVPN
+peers.
+.sp
+The usual symptom of such a breakdown is an OpenVPN connection which
+successfully starts, but then stalls during active usage.
+.sp
+If \fB\-\-fragment\fP and \fB\-\-mssfix\fP are used together, \fB\-\-mssfix\fP will
+take its default \fBmax\fP parameter from the \fB\-\-fragment max\fP option.
+.sp
+Therefore, one could lower the maximum UDP packet size to 1300 (a good
+first try for solving MTU\-related connection problems) with the
+following options:
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
-push "auth\-token UNIQUE_TOKEN_VALUE"
-.in -4
-.ft
+.ft C
+\-\-tun\-mtu 1500 \-\-fragment 1300 \-\-mssfix
+.ft P
.fi
-
-into the file/buffer for dynamic configuration data. This
-will then make the OpenVPN server to push this value to the
-client, which replaces the local password with the
-UNIQUE_TOKEN_VALUE.
-
-Newer clients (2.4.7+) will fall back to the original password method
-after a failed auth. Older clients will keep using the token value
-and react acording to
-.B \-\-auth-retry
-.
-.\"*********************************************************
-.TP
-.B \-\-tls\-verify cmd
-Run command
-.B cmd
-to verify the X509 name of a
-pending TLS connection that has otherwise passed all other
-tests of certification (except for revocation via
-.B \-\-crl\-verify
-directive; the revocation test occurs after the
-.B \-\-tls\-verify
-test).
-
-.B cmd
-should return 0 to allow the TLS handshake to proceed, or 1 to fail.
-
-.B cmd
-consists of a path to script (or executable program), optionally
-followed by arguments. The path and arguments may be single\- or double\-quoted
-and/or escaped using a backslash, and should be separated by one or more spaces.
-
-When
-.B cmd
-is executed two arguments are appended after any arguments specified in
-.B cmd
-, as follows:
-
-.B cmd certificate_depth subject
-
-These arguments are, respectively, the current certificate depth and
-the X509 subject distinguished name (dn) of the peer.
-
-This feature is useful if the peer you want to trust has a certificate
-which was signed by a certificate authority who also signed many
-other certificates, where you don't necessarily want to trust all of them,
-but rather be selective about which
-peer certificate you will accept. This feature allows you to write a script
-which will test the X509 name on a certificate and decide whether or
-not it should be accepted. For a simple perl script which will test
-the common name field on the certificate, see the file
-.B verify\-cn
-in the OpenVPN distribution.
-
-See the "Environmental Variables" section below for
-additional parameters passed as environmental variables.
-.\"*********************************************************
-.TP
-.B \-\-tls\-export\-cert directory
-Store the certificates the clients uses upon connection to this
-directory. This will be done before \-\-tls\-verify is called. The
-certificates will use a temporary name and will be deleted when
-the tls\-verify script returns. The file name used for the certificate
-is available via the peer_cert environment variable.
-.\"*********************************************************
-.TP
-.B \-\-x509\-username\-field [ext:\]fieldname
-Field in the X.509 certificate subject to be used as the username (default=CN).
-Typically, this option is specified with
-.B fieldname
-as either of the following:
-
-.B \-\-x509\-username\-field
-emailAddress
-.br
-.B \-\-x509\-username\-field ext:\fRsubjectAltName
-
-The first example uses the value of the "emailAddress" attribute in the
-certificate's Subject field as the username. The second example uses
-the
-.B ext:
-prefix to signify that the X.509 extension
-.B fieldname
-"subjectAltName" be searched for an rfc822Name (email) field to be used
-as the username. In cases where there are multiple email addresses
-in
-.B ext:fieldname\fR,
-the last occurrence is chosen.
-
-When this option is used, the
-.B \-\-verify\-x509\-name
-option will match against the chosen
-.B fieldname
-instead of the Common Name.
-
-Only the subjectAltName and issuerAltName X.509 extensions are supported.
-
-.B Please note:
-This option has a feature which will convert an all\-lowercase
-.B fieldname
-to uppercase characters, e.g., ou \-> OU. A mixed\-case
-.B fieldname
-or one having the
-.B ext:
-prefix will be left as\-is. This automatic upcasing feature
-is deprecated and will be removed in a future release.
-.\"*********************************************************
-.TP
-.B \-\-verify\-x509\-name name type
-Accept connections only if a host's X.509 name is equal to
-.B name.
-The remote host must also pass all other tests of verification.
-
-Which X.509 name is compared to
-.B name
-depends on the setting of type.
-.B type
-can be "subject" to match the complete subject DN (default),
-"name" to match a subject RDN or "name\-prefix" to match a subject RDN prefix.
-Which RDN is verified as name depends on the
-.B \-\-x509\-username\-field
-option. But it defaults to the common name (CN), e.g. a certificate with a
-subject DN "C=KG, ST=NA, L=Bishkek, CN=Server\-1" would be matched by:
-
-.B \-\-verify\-x509\-name 'C=KG, ST=NA, L=Bishkek, CN=Server\-1'
-and
-.B \-\-verify\-x509\-name Server\-1 name
-or you could use
-.B \-\-verify\-x509\-name Server\- name\-prefix
-if you want a client to only accept connections to "Server\-1", "Server\-2", etc.
-
-.B \-\-verify\-x509\-name
-is a useful replacement for the
-.B \-\-tls\-verify
-option to verify the remote host, because
-.B \-\-verify\-x509\-name
-works in a
-.B \-\-chroot
-environment without any dependencies.
-
-Using a name prefix is a useful alternative to managing
-a CRL (Certificate Revocation List) on the client, since it allows the client
-to refuse all certificates except for those associated
-with designated servers.
-
-.B NOTE:
-Test against a name prefix only when you are using OpenVPN with
-a custom CA certificate that is under your control.
-Never use this option with type "name\-prefix" when your client certificates
-are signed by a third party, such as a commercial web CA.
-.\"*********************************************************
-.TP
-.B \-\-x509\-track attribute
-Save peer X509
-.B attribute
-value in environment for use by plugins and management interface.
-Prepend a '+' to
-.B attribute
-to save values from full cert chain. Values will be encoded
-as X509_<depth>_<attribute>=<value>. Multiple
-.B \-\-x509\-track
-options can be defined to track multiple attributes.
-.\"*********************************************************
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-mtu\-disc \ type
+Should we do Path MTU discovery on TCP/UDP channel? Only supported on
+OSes such as Linux that supports the necessary system call to set.
+.sp
+Valid types:
+.sp
+\fBno\fP Never send DF (Don\(aqt Fragment) frames
+.sp
+\fBmaybe\fP Use per\-route hints
+.sp
+\fByes\fP Always DF (Don\(aqt Fragment)
.TP
-.B \-\-ns\-cert\-type client|server
-.B DEPRECATED
-This option will be removed in OpenVPN 2.5. Use the more modern equivalent
-.B \-\-remote\-cert\-tls
-instead. This option will be removed in OpenVPN 2.5.
-
-Require that peer certificate was signed with an explicit
-.B nsCertType
-designation of "client" or "server".
-
-This is a useful security option for clients, to ensure that
-the host they connect with is a designated server.
-
-See the easy\-rsa/build\-key\-server script for an example
-of how to generate a certificate with the
-.B nsCertType
-field set to "server".
-
-If the server certificate's nsCertType field is set
-to "server", then the clients can verify this with
-.B \-\-ns\-cert\-type server.
-
-This is an important security precaution to protect against
-a man\-in\-the\-middle attack where an authorized client
-attempts to connect to another client by impersonating the server.
-The attack is easily prevented by having clients verify
-the server certificate using any one of
-.B \-\-ns\-cert\-type, \-\-verify\-x509\-name,
-or
-.B \-\-tls\-verify.
-.\"*********************************************************
+.B \-\-mtu\-test
+To empirically measure MTU on connection startup, add the \fB\-\-mtu\-test\fP
+option to your configuration. OpenVPN will send ping packets of various
+sizes to the remote peer and measure the largest packets which were
+successfully received. The \fB\-\-mtu\-test\fP process normally takes about 3
+minutes to complete.
.TP
-.B \-\-remote\-cert\-ku [v...]
-Require that peer certificate was signed with an explicit
-.B key usage.
-
-If present in the certificate, the keyUsage value is validated by the TLS
-library during the TLS handshake. Specifying this option without arguments
-requires this extension to be present (so the TLS library will verify it).
-
-If the list
-.B v...
-is also supplied, the keyUsage field must have
-.B at least
-the same bits set as the bits in
-.B one of
-the values supplied in the list
-.B v...
-
-The key usage values in the list must be encoded in hex, e.g.
-"\-\-remote\-cert\-ku a0"
-.\"*********************************************************
+.B \-\-nobind
+Do not bind to local address and port. The IP stack will allocate a
+dynamic port for returning packets. Since the value of the dynamic port
+could not be known in advance by a peer, this option is only suitable
+for peers which will be initiating connections by using the \-\-remote
+option.
.TP
-.B \-\-remote\-cert\-eku oid
-Require that peer certificate was signed with an explicit
-.B extended key usage.
-
-This is a useful security option for clients, to ensure that
-the host they connect to is a designated server.
-
-The extended key usage should be encoded in oid notation, or
-OpenSSL symbolic representation.
-.\"*********************************************************
+.B \-\-passtos
+Set the TOS field of the tunnel packet to what the payload\(aqs TOS is.
+.TP
+.BI \-\-ping \ n
+Ping remote over the TCP/UDP control channel if no packets have been
+sent for at least \fBn\fP seconds (specify \fB\-\-ping\fP on both peers to
+cause ping packets to be sent in both directions since OpenVPN ping
+packets are not echoed like IP ping packets). When used in one of
+OpenVPN\(aqs secure modes (where \fB\-\-secret\fP, \fB\-\-tls\-server\fP or
+\fB\-\-tls\-client\fP is specified), the ping packet will be
+cryptographically secure.
+.sp
+This option has two intended uses:
+.INDENT 7.0
+.IP 1. 3
+Compatibility with stateful firewalls. The periodic ping will ensure
+that a stateful firewall rule which allows OpenVPN UDP packets to
+pass will not time out.
+.IP 2. 3
+To provide a basis for the remote to test the existence of its peer
+using the \fB\-\-ping\-exit\fP option.
+.UNINDENT
+.TP
+.BI \-\-ping\-exit \ n
+Causes OpenVPN to exit after \fBn\fP seconds pass without reception of a
+ping or other packet from remote. This option can be combined with
+\fB\-\-inactive\fP, \fB\-\-ping\fP and \fB\-\-ping\-exit\fP to create a two\-tiered
+inactivity disconnect.
+.sp
+For example,
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn [options...] \-\-inactive 3600 \-\-ping 10 \-\-ping\-exit 60
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+when used on both peers will cause OpenVPN to exit within 60 seconds if
+its peer disconnects, but will exit after one hour if no actual tunnel
+data is exchanged.
+.TP
+.BI \-\-ping\-restart \ n
+Similar to \fB\-\-ping\-exit\fP, but trigger a \fBSIGUSR1\fP restart after
+\fBn\fP seconds pass without reception of a ping or other packet from
+remote.
+.sp
+This option is useful in cases where the remote peer has a dynamic IP
+address and a low\-TTL DNS name is used to track the IP address using a
+service such as \fI\%http://dyndns.org/\fP + a dynamic DNS client such as
+\fBddclient\fP\&.
+.sp
+If the peer cannot be reached, a restart will be triggered, causing the
+hostname used with \fB\-\-remote\fP to be re\-resolved (if \fB\-\-resolv\-retry\fP
+is also specified).
+.sp
+In server mode, \fB\-\-ping\-restart\fP, \fB\-\-inactive\fP or any other type of
+internally generated signal will always be applied to individual client
+instance objects, never to whole server itself. Note also in server mode
+that any internally generated signal which would normally cause a
+restart, will cause the deletion of the client instance object instead.
+.sp
+In client mode, the \fB\-\-ping\-restart\fP parameter is set to 120 seconds
+by default. This default will hold until the client pulls a replacement
+value from the server, based on the \fB\-\-keepalive\fP setting in the
+server configuration. To disable the 120 second default, set
+\fB\-\-ping\-restart 0\fP on the client.
+.sp
+See the signals section below for more information on \fBSIGUSR1\fP\&.
+.sp
+Note that the behavior of \fBSIGUSR1\fP can be modified by the
+\fB\-\-persist\-tun\fP, \fB\-\-persist\-key\fP, \fB\-\-persist\-local\-ip\fP and
+\fB\-\-persist\-remote\-ip\fP options.
+.sp
+Also note that \fB\-\-ping\-exit\fP and \fB\-\-ping\-restart\fP are mutually
+exclusive and cannot be used together.
.TP
-.B \-\-remote\-cert\-tls client|server
-Require that peer certificate was signed with an explicit
-.B key usage
-and
-.B extended key usage
-based on RFC3280 TLS rules.
-
-This is a useful security option for clients, to ensure that the host they
-connect to is a designated server. Or the other way around; for a server to
-verify that only hosts with a client certificate can connect.
-
-The
-.B \-\-remote\-cert\-tls client
-option is equivalent to
-.B
-\-\-remote\-cert\-ku \-\-remote\-cert\-eku "TLS Web Client Authentication"
-
-The
-.B \-\-remote\-cert\-tls server
-option is equivalent to
-.B
-\-\-remote\-cert\-ku \-\-remote\-cert\-eku "TLS Web Server Authentication"
-
-This is an important security precaution to protect against
-a man\-in\-the\-middle attack where an authorized client
-attempts to connect to another client by impersonating the server.
-The attack is easily prevented by having clients verify
-the server certificate using any one of
-.B \-\-remote\-cert\-tls, \-\-verify\-x509\-name,
-or
-.B \-\-tls\-verify.
-.\"*********************************************************
+.B \-\-ping\-timer\-rem
+Run the \fB\-\-ping\-exit\fP / \fB\-\-ping\-restart\fP timer only if we have a
+remote address. Use this option if you are starting the daemon in listen
+mode (i.e. without an explicit \fB\-\-remote\fP peer), and you don\(aqt want to
+start clocking timeouts until a remote peer connects.
+.TP
+.BI \-\-proto \ p
+Use protocol \fBp\fP for communicating with remote host. \fBp\fP can be
+\fBudp\fP, \fBtcp\-client\fP, or \fBtcp\-server\fP\&.
+.sp
+The default protocol is \fBudp\fP when \fB\-\-proto\fP is not specified.
+.sp
+For UDP operation, \fB\-\-proto udp\fP should be specified on both peers.
+.sp
+For TCP operation, one peer must use \fB\-\-proto tcp\-server\fP and the
+other must use \fB\-\-proto tcp\-client\fP\&. A peer started with
+\fBtcp\-server\fP will wait indefinitely for an incoming connection. A peer
+started with \fBtcp\-client\fP will attempt to connect, and if that fails,
+will sleep for 5 seconds (adjustable via the \fB\-\-connect\-retry\fP option)
+and try again infinite or up to N retries (adjustable via the
+\fB\-\-connect\-retry\-max\fP option). Both TCP client and server will
+simulate a SIGUSR1 restart signal if either side resets the connection.
+.sp
+OpenVPN is designed to operate optimally over UDP, but TCP capability is
+provided for situations where UDP cannot be used. In comparison with
+UDP, TCP will usually be somewhat less efficient and less robust when
+used over unreliable or congested networks.
+.sp
+This article outlines some of problems with tunneling IP over TCP:
+\fI\%http://sites.inka.de/sites/bigred/devel/tcp\-tcp.html\fP
+.sp
+There are certain cases, however, where using TCP may be advantageous
+from a security and robustness perspective, such as tunneling non\-IP or
+application\-level UDP protocols, or tunneling protocols which don\(aqt
+possess a built\-in reliability layer.
.TP
-.B \-\-crl\-verify crl ['dir']
-Check peer certificate against the file
-.B crl
-in PEM format.
-
-A CRL (certificate revocation list) is used when a particular key is
-compromised but when the overall PKI is still intact.
-
-Suppose you had a PKI consisting of a CA, root certificate, and a number of
-client certificates. Suppose a laptop computer containing a client key and
-certificate was stolen. By adding the stolen certificate to the CRL file,
-you could reject any connection which attempts to use it, while preserving the
-overall integrity of the PKI.
-
-The only time when it would be necessary to rebuild the entire PKI from scratch would be
-if the root certificate key itself was compromised.
-
-The option is not mandatory \- if the relevant CRL is missing, OpenVPN will log
-a warning in the logs \- e.g. "\fIVERIFY WARNING: depth=0, unable to get
-certificate CRL\fR" \- but the connection will be allowed.
-
-If the optional
-.B dir
-flag is specified, enable a different mode where
-.B crl
-is a directory containing files named as revoked serial numbers
-(the files may be empty, the contents are never read). If a client
-requests a connection, where the client certificate serial number
-(decimal string) is the name of a file present in the directory,
-it will be rejected.
-
-Note: As the crl file (or directory) is read every time a peer connects,
-if you are dropping root privileges with
-.B \-\-user,
-make sure that this user has sufficient privileges to read the file.
-.\"*********************************************************
-.SS SSL Library information:
-.\"*********************************************************
+.BI \-\-port \ port
+TCP/UDP port number or port name for both local and remote (sets both
+\fB\-\-lport\fP and \fB\-\-rport\fP options to given port). The current default
+of 1194 represents the official IANA port number assignment for OpenVPN
+and has been used since version 2.0\-beta17. Previous versions used port
+5000 as the default.
+.TP
+.BI \-\-rport \ port
+Set TCP/UDP port number or name used by the \fB\-\-remote\fP option. The
+port can also be set directly using the \fB\-\-remote\fP option.
+.TP
+.BI \-\-replay\-window \ args
+Modify the replay protection sliding\-window size and time window.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+replay\-window n [t]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Use a replay protection sliding\-window of size \fBn\fP and a time window
+of \fBt\fP seconds.
+.sp
+By default \fBn\fP is 64 (the IPSec default) and \fBt\fP is 15 seconds.
+.sp
+This option is only relevant in UDP mode, i.e. when either \fB\-\-proto
+udp\fP is specified, or no \fB\-\-proto\fP option is specified.
+.sp
+When OpenVPN tunnels IP packets over UDP, there is the possibility that
+packets might be dropped or delivered out of order. Because OpenVPN,
+like IPSec, is emulating the physical network layer, it will accept an
+out\-of\-order packet sequence, and will deliver such packets in the same
+order they were received to the TCP/IP protocol stack, provided they
+satisfy several constraints.
+.INDENT 7.0
+.IP a. 3
+The packet cannot be a replay (unless \fB\-\-no\-replay\fP is
+specified, which disables replay protection altogether).
+.IP b. 3
+If a packet arrives out of order, it will only be accepted if
+the difference between its sequence number and the highest sequence
+number received so far is less than \fBn\fP\&.
+.IP c. 3
+If a packet arrives out of order, it will only be accepted if it
+arrives no later than \fBt\fP seconds after any packet containing a higher
+sequence number.
+.UNINDENT
+.sp
+If you are using a network link with a large pipeline (meaning that the
+product of bandwidth and latency is high), you may want to use a larger
+value for \fBn\fP\&. Satellite links in particular often require this.
+.sp
+If you run OpenVPN at \fB\-\-verb 4\fP, you will see the message
+"Replay\-window backtrack occurred [x]" every time the maximum sequence
+number backtrack seen thus far increases. This can be used to calibrate
+\fBn\fP\&.
+.sp
+There is some controversy on the appropriate method of handling packet
+reordering at the security layer.
+.sp
+Namely, to what extent should the security layer protect the
+encapsulated protocol from attacks which masquerade as the kinds of
+normal packet loss and reordering that occur over IP networks?
+.sp
+The IPSec and OpenVPN approach is to allow packet reordering within a
+certain fixed sequence number window.
+.sp
+OpenVPN adds to the IPSec model by limiting the window size in time as
+well as sequence space.
+.sp
+OpenVPN also adds TCP transport as an option (not offered by IPSec) in
+which case OpenVPN can adopt a very strict attitude towards message
+deletion and reordering: Don\(aqt allow it. Since TCP guarantees
+reliability, any packet loss or reordering event can be assumed to be an
+attack.
+.sp
+In this sense, it could be argued that TCP tunnel transport is preferred
+when tunneling non\-IP or UDP application protocols which might be
+vulnerable to a message deletion or reordering attack which falls within
+the normal operational parameters of IP networks.
+.sp
+So I would make the statement that one should never tunnel a non\-IP
+protocol or UDP application protocol over UDP, if the protocol might be
+vulnerable to a message deletion or reordering attack that falls within
+the normal operating parameters of what is to be expected from the
+physical IP layer. The problem is easily fixed by simply using TCP as
+the VPN transport layer.
+.TP
+.BI \-\-replay\-persist \ file
+Persist replay\-protection state across sessions using \fBfile\fP to save
+and reload the state.
+.sp
+This option will strengthen protection against replay attacks,
+especially when you are using OpenVPN in a dynamic context (such as with
+\fB\-\-inetd\fP) when OpenVPN sessions are frequently started and stopped.
+.sp
+This option will keep a disk copy of the current replay protection state
+(i.e. the most recent packet timestamp and sequence number received from
+the remote peer), so that if an OpenVPN session is stopped and
+restarted, it will reject any replays of packets which were already
+received by the prior session.
+.sp
+This option only makes sense when replay protection is enabled (the
+default) and you are using either \fB\-\-secret\fP (shared\-secret key mode)
+or TLS mode with \fB\-\-tls\-auth\fP\&.
+.TP
+.BI \-\-socket\-flags \ flags
+Apply the given flags to the OpenVPN transport socket. Currently, only
+\fBTCP_NODELAY\fP is supported.
+.sp
+The \fBTCP_NODELAY\fP socket flag is useful in TCP mode, and causes the
+kernel to send tunnel packets immediately over the TCP connection without
+trying to group several smaller packets into a larger packet. This can
+result in a considerably improvement in latency.
+.sp
+This option is pushable from server to client, and should be used on
+both client and server for maximum effect.
.TP
-.B \-\-show\-ciphers
-(Standalone)
-Show all cipher algorithms to use with the
-.B \-\-cipher
-option.
-.\"*********************************************************
+.B \-\-tcp\-nodelay
+This macro sets the \fBTCP_NODELAY\fP socket flag on the server as well
+as pushes it to connecting clients. The \fBTCP_NODELAY\fP flag disables
+the Nagle algorithm on TCP sockets causing packets to be transmitted
+immediately with low latency, rather than waiting a short period of time
+in order to aggregate several packets into a larger containing packet.
+In VPN applications over TCP, \fBTCP_NODELAY\fP is generally a good
+latency optimization.
+.sp
+The macro expands as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+if mode server:
+ socket\-flags TCP_NODELAY
+ push "socket\-flags TCP_NODELAY"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SS Virtual Network Adapter (VPN interface)
+.sp
+Options in this section relates to configuration of the virtual tun/tap
+network interface, including setting the VPN IP address and network
+routing.
+.INDENT 0.0
+.TP
+.BI \-\-bind\-dev \ device
+(Linux only) Set \fBdevice\fP to bind the server socket to a
+\fI\%Virtual Routing and Forwarding\fP device
+.TP
+.B \-\-block\-ipv6
+On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6 no
+route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available. \fB\-\-block\-ipv6\fP
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use \fBfe80::7\fP as source address.
+.sp
+For this option to make sense you actually have to route traffic to the
+tun interface. The following example config block would send all IPv6
+traffic to OpenVPN and answer all requests with no route to host,
+effectively blocking IPv6 (to avoid IPv6 connections from dual\-stacked
+clients leaking around IPv4\-only VPN services).
+.INDENT 7.0
+.TP
+.B \fBClient config\fP
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-ifconfig\-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1
+\-\-redirect\-gateway ipv6
+\-\-block\-ipv6
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.B \fBServer config\fP
+Push a "valid" ipv6 config to the client and block on the server
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-push "ifconfig\-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1"
+\-\-push "redirect\-gateway ipv6"
+\-\-block\-ipv6
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+Note: this option does not influence traffic sent from the server
+towards the client (neither on the server nor on the client side).
+This is not seen as necessary, as such traffic can be most easily
+avoided by not configuring IPv6 on the server tun, or setting up a
+server\-side firewall rule.
+.TP
+.BI \-\-dev \ device
+TUN/TAP virtual network device which can be \fBtunX\fP, \fBtapX\fP,
+\fBnull\fP or an arbitrary name string (\fBX\fP can be omitted for
+a dynamic device.)
+.sp
+See examples section below for an example on setting up a TUN device.
+.sp
+You must use either tun devices on both ends of the connection or tap
+devices on both ends. You cannot mix them, as they represent different
+underlying network layers:
+.INDENT 7.0
+.TP
+.B \fBtun\fP
+devices encapsulate IPv4 or IPv6 (OSI Layer 3)
+.TP
+.B \fBtap\fP
+devices encapsulate Ethernet 802.3 (OSI Layer 2).
+.UNINDENT
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+dev tun2
+dev tap4
+dev ovpn
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+When the device name starts with \fBtun\fP or \fBtap\fP, the device
+type is extracted automatically. Otherwise the \fB\-\-dev\-type\fP option
+needs to be added as well.
+.TP
+.BI \-\-dev\-node \ node
+Explicitly set the device node rather than using \fB/dev/net/tun\fP,
+\fB/dev/tun\fP, \fB/dev/tap\fP, etc. If OpenVPN cannot figure out
+whether \fBnode\fP is a TUN or TAP device based on the name, you should
+also specify \fB\-\-dev\-type tun\fP or \fB\-\-dev\-type tap\fP\&.
+.sp
+Under Mac OS X this option can be used to specify the default tun
+implementation. Using \fB\-\-dev\-node utun\fP forces usage of the native
+Darwin tun kernel support. Use \fB\-\-dev\-node utunN\fP to select a specific
+utun instance. To force using the \fBtun.kext\fP (\fB/dev/tunX\fP)
+use \fB\-\-dev\-node tun\fP\&. When not specifying a \fB\-\-dev\-node\fP option
+openvpn will first try to open utun, and fall back to tun.kext.
+.sp
+On Windows systems, select the TAP\-Win32 adapter which is named \fBnode\fP
+in the Network Connections Control Panel or the raw GUID of the adapter
+enclosed by braces. The \fB\-\-show\-adapters\fP option under Windows can
+also be used to enumerate all available TAP\-Win32 adapters and will show
+both the network connections control panel name and the GUID for each
+TAP\-Win32 adapter.
+.TP
+.BI \-\-dev\-type \ device\-type
+Which device type are we using? \fBdevice\-type\fP should be \fBtun\fP
+(OSI Layer 3) or \fBtap\fP (OSI Layer 2). Use this option only if
+the TUN/TAP device used with \fB\-\-dev\fP does not begin with \fBtun\fP
+or \fBtap\fP\&.
+.TP
+.BI \-\-dhcp\-option \ args
+Set additional network parameters on supported platforms. May be specified
+on the client or pushed from the server. On Windows these options are
+handled by the \fBtap\-windows6\fP driver by default or directly by OpenVPN
+if dhcp is disabled or the \fBwintun\fP driver is in use. The
+\fBOpenVPN for Android\fP client also handles them internally.
+.sp
+On all other platforms these options are only saved in the client\(aqs
+environment under the name \fBforeign_options_{n}\fP before the
+\fB\-\-up\fP script is called. A plugin or an \fB\-\-up\fP script must be used to
+pick up and interpret these as required. Many Linux distributions include
+such scripts and some third\-party user interfaces such as tunnelblick also
+come with scripts that process these options.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+dhcp\-options type [parm]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.INDENT 7.0
+.TP
+.B \fBDOMAIN\fP \fBname\fP
+Set Connection\-specific DNS Suffix to \fBname\fP\&.
+.TP
+.B \fBADAPTER_DOMAIN_SUFFIX\fP \fBname\fP
+Alias to \fBDOMAIN\fP\&. This is a compatibility option, it
+should not be used in new deployments.
+.TP
+.B \fBDOMAIN\-SEARCH\fP \fBname\fP
+Add \fBname\fP to the domain search list.
+Repeat this option to add more entries. Up to
+10 domains are supported.
+.TP
+.B \fBDNS\fP \fBaddress\fP
+Set primary domain name server IPv4 or IPv6 address.
+Repeat this option to set secondary DNS server addresses.
+.sp
+Note: DNS IPv6 servers are currently set using netsh (the existing
+DHCP code can only do IPv4 DHCP, and that protocol only permits
+IPv4 addresses anywhere). The option will be put into the
+environment, so an \fB\-\-up\fP script could act upon it if needed.
.TP
-.B \-\-show\-digests
-(Standalone)
-Show all message digest algorithms to use with the
-.B \-\-auth
-option.
-.\"*********************************************************
+.B \fBWINS\fP \fBaddress\fP
+Set primary WINS server address (NetBIOS over TCP/IP Name Server).
+Repeat this option to set secondary WINS server addresses.
.TP
-.B \-\-show\-tls
-(Standalone)
-Show all TLS ciphers supported by the crypto library. OpenVPN uses TLS to
-secure the control channel, over which the keys that are used to protect the
-actual VPN traffic are exchanged. The TLS ciphers will be sorted from highest
-preference (most secure) to lowest.
+.B \fBNBDD\fP \fBaddress\fP
+Set primary NBDD server address (NetBIOS over TCP/IP Datagram
+Distribution Server). Repeat this option to set secondary NBDD
+server addresses.
+.TP
+.B \fBNTP\fP \fBaddress\fP
+Set primary NTP server address (Network Time Protocol).
+Repeat this option to set secondary NTP server addresses.
+.TP
+.B \fBNBT\fP \fBtype\fP
+Set NetBIOS over TCP/IP Node type. Possible options:
+.INDENT 7.0
+.TP
+.B \fB1\fP
+b\-node (broadcasts)
+.TP
+.B \fB2\fP
+p\-node (point\-to\-point name queries to a WINS server)
+.TP
+.B \fB4\fP
+m\-node (broadcast then query name server)
+.TP
+.B \fB8\fP
+h\-node (query name server, then broadcast).
+.UNINDENT
+.TP
+.B \fBNBS\fP \fBscope\-id\fP
+Set NetBIOS over TCP/IP Scope. A NetBIOS Scope ID provides an
+extended naming service for the NetBIOS over TCP/IP (Known as NBT)
+module. The primary purpose of a NetBIOS scope ID is to isolate
+NetBIOS traffic on a single network to only those nodes with the
+same NetBIOS scope ID. The NetBIOS scope ID is a character string
+that is appended to the NetBIOS name. The NetBIOS scope ID on two
+hosts must match, or the two hosts will not be able to communicate.
+The NetBIOS Scope ID also allows computers to use the same computer
+name, as they have different scope IDs. The Scope ID becomes a part
+of the NetBIOS name, making the name unique. (This description of
+NetBIOS scopes courtesy of \fI\%NeonSurge@abyss.com\fP)
+.TP
+.B \fBDISABLE\-NBT\fP
+Disable Netbios\-over\-TCP/IP.
+.UNINDENT
+.TP
+.BI \-\-ifconfig \ args
+Set TUN/TAP adapter parameters. It requires the \fIIP address\fP of the local
+VPN endpoint. For TUN devices in point\-to\-point mode, the next argument
+must be the VPN IP address of the remote VPN endpoint. For TAP devices,
+or TUN devices used with \fB\-\-topology subnet\fP, the second argument
+is the subnet mask of the virtual network segment which is being created
+or connected to.
+.sp
+For TUN devices, which facilitate virtual point\-to\-point IP connections
+(when used in \fB\-\-topology net30\fP or \fBp2p\fP mode), the proper usage of
+\fB\-\-ifconfig\fP is to use two private IP addresses which are not a member
+of any existing subnet which is in use. The IP addresses may be
+consecutive and should have their order reversed on the remote peer.
+After the VPN is established, by pinging \fBrn\fP, you will be pinging
+across the VPN.
+.sp
+For TAP devices, which provide the ability to create virtual ethernet
+segments, or TUN devices in \fB\-\-topology subnet\fP mode (which create
+virtual "multipoint networks"), \fB\-\-ifconfig\fP is used to set an IP
+address and subnet mask just as a physical ethernet adapter would be
+similarly configured. If you are attempting to connect to a remote
+ethernet bridge, the IP address and subnet should be set to values which
+would be valid on the the bridged ethernet segment (note also that DHCP
+can be used for the same purpose).
+.sp
+This option, while primarily a proxy for the \fBifconfig\fP(8) command,
+is designed to simplify TUN/TAP tunnel configuration by providing a
+standard interface to the different ifconfig implementations on
+different platforms.
+.sp
+\fB\-\-ifconfig\fP parameters which are IP addresses can also be specified
+as a DNS or /etc/hosts file resolvable name.
+.sp
+For TAP devices, \fB\-\-ifconfig\fP should not be used if the TAP interface
+will be getting an IP address lease from a DHCP server.
+.sp
+Examples:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+# tun device in net30/p2p mode
+ifconfig 10.8.0.2 10.8.0.1
-Be aware that whether a cipher suite in this list can actually work depends on
-the specific setup of both peers (e.g. both peers must support the cipher, and
-an ECDSA cipher suite will not work if you are using an RSA certificate, etc.).
-.\"*********************************************************
+# tun/tap device in subnet mode
+ifconfig 10.8.0.2 255.255.255.0
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.BI \-\-ifconfig\-ipv6 \ args
+Configure an IPv6 address on the \fItun\fP device.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ifconfig\-ipv6 ipv6addr/bits [ipv6remote]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fBipv6addr/bits\fP argument is the IPv6 address to use. The
+second parameter is used as route target for \fB\-\-route\-ipv6\fP if no
+gateway is specified.
+.sp
+The \fB\-\-topology\fP option has no influence with \fB\-\-ifconfig\-ipv6\fP
.TP
-.B \-\-show\-engines
-(Standalone)
-Show currently available hardware\-based crypto acceleration
-engines supported by the OpenSSL library.
-.\"*********************************************************
+.B \-\-ifconfig\-noexec
+Don\(aqt actually execute ifconfig/netsh commands, instead pass
+\fB\-\-ifconfig\fP parameters to scripts using environmental variables.
.TP
-.B \-\-show\-curves
-(Standalone)
-Show all available elliptic curves to use with the
-.B \-\-ecdh\-curve
-option.
-.\"*********************************************************
-.SS Generate a random key:
-Used only for non\-TLS static key encryption mode.
-.\"*********************************************************
-.TP
-.B \-\-genkey
-(Standalone)
-Generate a random key to be used as a shared secret,
-for use with the
-.B \-\-secret
-option. This file must be shared with the
-peer over a pre\-existing secure channel such as
-.BR scp (1)
-.
-.\"*********************************************************
+.B \-\-ifconfig\-nowarn
+Don\(aqt output an options consistency check warning if the \fB\-\-ifconfig\fP
+option on this side of the connection doesn\(aqt match the remote side.
+This is useful when you want to retain the overall benefits of the
+options consistency check (also see \fB\-\-disable\-occ\fP option) while only
+disabling the ifconfig component of the check.
+.sp
+For example, if you have a configuration where the local host uses
+\fB\-\-ifconfig\fP but the remote host does not, use \fB\-\-ifconfig\-nowarn\fP
+on the local host.
+.sp
+This option will also silence warnings about potential address conflicts
+which occasionally annoy more experienced users by triggering "false
+positive" warnings.
.TP
-.B \-\-secret file
-Write key to
-.B file.
-.\"*********************************************************
-.SS TUN/TAP persistent tunnel config mode:
-Available with Linux 2.4.7+. These options comprise a standalone mode
-of OpenVPN which can be used to create and delete persistent tunnels.
-.\"*********************************************************
+.BI \-\-lladdr \ address
+Specify the link layer address, more commonly known as the MAC address.
+Only applied to TAP devices.
.TP
-.B \-\-mktun
-(Standalone)
-Create a persistent tunnel on platforms which support them such
-as Linux. Normally TUN/TAP tunnels exist only for
-the period of time that an application has them open. This option
-takes advantage of the TUN/TAP driver's ability to build persistent
-tunnels that live through multiple instantiations of OpenVPN and die
-only when they are deleted or the machine is rebooted.
-
-One of the advantages of persistent tunnels is that they eliminate the
-need for separate
-.B \-\-up
-and
-.B \-\-down
-scripts to run the appropriate
-.BR ifconfig (8)
-and
-.BR route (8)
-commands. These commands can be placed in the the same shell script
-which starts or terminates an OpenVPN session.
-
-Another advantage is that open connections through the TUN/TAP\-based tunnel
-will not be reset if the OpenVPN peer restarts. This can be useful to
-provide uninterrupted connectivity through the tunnel in the event of a DHCP
-reset of the peer's public IP address (see the
-.B \-\-ipchange
-option above).
-
-One disadvantage of persistent tunnels is that it is harder to automatically
-configure their MTU value (see
-.B \-\-link\-mtu
-and
-.B \-\-tun\-mtu
-above).
-
-On some platforms such as Windows, TAP\-Win32 tunnels are persistent by
-default.
-.\"*********************************************************
+.B \-\-persist\-tun
+Don\(aqt close and reopen TUN/TAP device or run up/down scripts across
+\fBSIGUSR1\fP or \fB\-\-ping\-restart\fP restarts.
+.sp
+\fBSIGUSR1\fP is a restart signal similar to \fBSIGHUP\fP, but which
+offers finer\-grained control over reset options.
.TP
-.B \-\-rmtun
-(Standalone)
-Remove a persistent tunnel.
-.\"*********************************************************
+.BI \-\-redirect\-gateway \ flags
+Automatically execute routing commands to cause all outgoing IP traffic
+to be redirected over the VPN. This is a client\-side option.
+.sp
+This option performs three steps:
+.INDENT 7.0
+.IP 1. 3
+Create a static route for the \fB\-\-remote\fP address which
+forwards to the pre\-existing default gateway. This is done so that
+\fB(3)\fP will not create a routing loop.
+.IP 2. 3
+Delete the default gateway route.
+.IP 3. 3
+Set the new default gateway to be the VPN endpoint address
+(derived either from \fB\-\-route\-gateway\fP or the second parameter to
+\fB\-\-ifconfig\fP when \fB\-\-dev tun\fP is specified).
+.UNINDENT
+.sp
+When the tunnel is torn down, all of the above steps are reversed so
+that the original default route is restored.
+.sp
+Option flags:
+.INDENT 7.0
.TP
-.B \-\-dev tunX | tapX
-TUN/TAP device
-.\"*********************************************************
+.B \fBlocal\fP
+Add the \fBlocal\fP flag if both OpenVPN peers are directly
+connected via a common subnet, such as with wireless. The
+\fBlocal\fP flag will cause step \fB(1)\fP above to be omitted.
.TP
-.B \-\-user user
-Optional user to be owner of this tunnel.
-.\"*********************************************************
+.B \fBautolocal\fP
+Try to automatically determine whether to enable \fBlocal\fP
+flag above.
.TP
-.B \-\-group group
-Optional group to be owner of this tunnel.
-.\"*********************************************************
-.SS Windows\-Specific Options:
-.\"*********************************************************
+.B \fBdef1\fP
+Use this flag to override the default gateway by using
+\fB0.0.0.0/1\fP and \fB128.0.0.0/1\fP rather than
+\fB0.0.0.0/0\fP\&. This has the benefit of overriding but not
+wiping out the original default gateway.
.TP
-.B \-\-win\-sys path
-Set the Windows system directory pathname to use when looking for system
-executables such as
-.B route.exe
-and
-.B netsh.exe.
-By default, if this directive is
-not specified, OpenVPN will use the SystemRoot environment variable.
-
-This option have changed behaviour in OpenVPN 2.3. Earlier you had to
-define
-.B \-\-win\-sys env
-to use the SystemRoot environment variable, otherwise it defaulted to C:\\WINDOWS.
-It is not needed to use the
-.B env
-keyword any more, and it will just be ignored. A warning is logged when this
-is found in the configuration file.
-.\"*********************************************************
-.TP
-.B \-\-ip\-win32 method
-When using
-.B \-\-ifconfig
-on Windows, set the TAP\-Win32 adapter
-IP address and netmask using
-.B method.
-Don't use this option unless you are also using
-.B \-\-ifconfig.
-
-.B manual \-\-
-Don't set the IP address or netmask automatically.
-Instead output a message
-to the console telling the user to configure the
-adapter manually and indicating the IP/netmask which
-OpenVPN expects the adapter to be set to.
-
-.B dynamic [offset] [lease\-time] \-\-
-Automatically set the IP address and netmask by replying to
-DHCP query messages generated by the kernel. This mode is
-probably the "cleanest" solution
-for setting the TCP/IP properties since it uses the well\-known
-DHCP protocol. There are, however, two prerequisites for using
-this mode: (1) The TCP/IP properties for the TAP\-Win32
-adapter must be set to "Obtain an IP address automatically," and
-(2) OpenVPN needs to claim an IP address in the subnet for use
-as the virtual DHCP server address. By default in
-.B \-\-dev tap
-mode, OpenVPN will
-take the normally unused first address in the subnet. For example,
-if your subnet is 192.168.4.0 netmask 255.255.255.0, then
-OpenVPN will take the IP address 192.168.4.0 to use as the
-virtual DHCP server address. In
-.B \-\-dev tun
-mode, OpenVPN will cause the DHCP server to masquerade as if it were
-coming from the remote endpoint. The optional offset parameter is
-an integer which is > \-256 and < 256 and which defaults to \-1.
-If offset is positive, the DHCP server will masquerade as the IP
-address at network address + offset.
-If offset is negative, the DHCP server will masquerade as the IP
-address at broadcast address + offset. The Windows
-.B ipconfig /all
-command can be used to show what Windows thinks the DHCP server
-address is. OpenVPN will "claim" this address, so make sure to
-use a free address. Having said that, different OpenVPN instantiations,
-including different ends of the same connection, can share the same
-virtual DHCP server address. The
-.B lease\-time
-parameter controls the lease time of the DHCP assignment given to
-the TAP\-Win32 adapter, and is denoted in seconds.
-Normally a very long lease time is preferred
-because it prevents routes involving the TAP\-Win32 adapter from
-being lost when the system goes to sleep. The default
-lease time is one year.
-
-.B netsh \-\-
-Automatically set the IP address and netmask using
-the Windows command\-line "netsh"
-command. This method appears to work correctly on
-Windows XP but not Windows 2000.
-
-.B ipapi \-\-
-Automatically set the IP address and netmask using the
-Windows IP Helper API. This approach
-does not have ideal semantics, though testing has indicated
-that it works okay in practice. If you use this option,
-it is best to leave the TCP/IP properties for the TAP\-Win32
-adapter in their default state, i.e. "Obtain an IP address
-automatically."
-
-.B adaptive \-\-
-(Default) Try
-.B dynamic
-method initially and fail over to
-.B netsh
-if the DHCP negotiation with the TAP\-Win32 adapter does
-not succeed in 20 seconds. Such failures have been known
-to occur when certain third\-party firewall packages installed
-on the client machine block the DHCP negotiation used by
-the TAP\-Win32 adapter.
-Note that if the
-.B netsh
-failover occurs, the TAP\-Win32 adapter
-TCP/IP properties will be reset from DHCP to static, and this
-will cause future OpenVPN startups using the
-.B adaptive
-mode to use
-.B netsh
-immediately, rather than trying
-.B dynamic
-first. To "unstick" the
-.B adaptive
-mode from using
-.B netsh,
-run OpenVPN at least once using the
-.B dynamic
-mode to restore the TAP\-Win32 adapter TCP/IP properties
-to a DHCP configuration.
-.\"*********************************************************
-.TP
-.B \-\-route\-method m
-Which method
-.B m
-to use for adding routes on Windows?
-
-.B adaptive
-(default) \-\- Try IP helper API first. If that fails, fall
-back to the route.exe shell command.
-.br
-.B ipapi
-\-\- Use IP helper API.
-.br
-.B exe
-\-\- Call the route.exe shell command.
-.\"*********************************************************
-.TP
-.B \-\-dhcp\-option type [parm]
-Set extended TAP\-Win32 TCP/IP properties, must
-be used with
-.B \-\-ip\-win32 dynamic
-or
-.B \-\-ip\-win32 adaptive.
-This option can be used to set additional TCP/IP properties
-on the TAP\-Win32 adapter, and is particularly useful for
-configuring an OpenVPN client to access a Samba server
-across the VPN.
-
-.B DOMAIN name \-\-
-Set Connection\-specific DNS Suffix.
-
-.B DNS addr \-\-
-Set primary domain name server IPv4 or IPv6 address. Repeat
-this option to set secondary DNS server addresses.
-
-Note: DNS IPv6 servers are currently set using netsh (the existing
-DHCP code can only do IPv4 DHCP, and that protocol only permits IPv4
-addresses anywhere). The option will be put into the environment, so
-an
-.B \-\-up
-script could act upon it if needed.
-
-.B WINS addr \-\-
-Set primary WINS server address (NetBIOS over TCP/IP Name Server).
-Repeat this option to set secondary WINS server addresses.
-
-.B NBDD addr \-\-
-Set primary NBDD server address (NetBIOS over TCP/IP Datagram Distribution Server)
-Repeat this option
-to set secondary NBDD server addresses.
-
-.B NTP addr \-\-
-Set primary NTP server address (Network Time Protocol).
-Repeat this option
-to set secondary NTP server addresses.
-
-.B NBT type \-\-
-Set NetBIOS over TCP/IP Node type. Possible options:
-.B 1
-= b\-node (broadcasts),
-.B 2
-= p\-node (point\-to\-point
-name queries to a WINS server),
-.B 4
-= m\-node (broadcast
-then query name server), and
-.B 8
-= h\-node (query name server, then broadcast).
-
-.B NBS scope\-id \-\-
-Set NetBIOS over TCP/IP Scope. A NetBIOS Scope ID provides an extended
-naming service for the NetBIOS over TCP/IP (Known as NBT) module. The
-primary purpose of a NetBIOS scope ID is to isolate NetBIOS traffic on
-a single network to only those nodes with the same NetBIOS scope ID.
-The NetBIOS scope ID is a character string that is appended to the NetBIOS
-name. The NetBIOS scope ID on two hosts must match, or the two hosts
-will not be able to communicate. The NetBIOS Scope ID also allows
-computers to use the same computer name, as they have different
-scope IDs. The Scope ID becomes a part of the NetBIOS name, making the name unique.
-(This description of NetBIOS scopes courtesy of NeonSurge@abyss.com)
-
-.B DISABLE\-NBT \-\-
-Disable Netbios\-over\-TCP/IP.
-
-Note that if
-.B \-\-dhcp\-option
-is pushed via
-.B \-\-push
-to a non\-windows client, the option will be saved in the client's
-environment before the up script is called, under
-the name "foreign_option_{n}".
-.\"*********************************************************
-.TP
-.B \-\-tap\-sleep n
-Cause OpenVPN to sleep for
-.B n
-seconds immediately after the TAP\-Win32 adapter state
-is set to "connected".
-
-This option is intended to be used to troubleshoot problems
-with the
-.B \-\-ifconfig
-and
-.B \-\-ip\-win32
-options, and is used to give
-the TAP\-Win32 adapter time to come up before
-Windows IP Helper API operations are applied to it.
-.\"*********************************************************
+.B \fBbypass\-dhcp\fP
+Add a direct route to the DHCP server (if it is non\-local) which
+bypasses the tunnel (Available on Windows clients, may not be
+available on non\-Windows clients).
.TP
-.B \-\-show\-net\-up
-Output OpenVPN's view of the system routing table and network
-adapter list to the syslog or log file after the TUN/TAP adapter
-has been brought up and any routes have been added.
-.\"*********************************************************
+.B \fBbypass\-dns\fP
+Add a direct route to the DNS server(s) (if they are non\-local)
+which bypasses the tunnel (Available on Windows clients, may
+not be available on non\-Windows clients).
.TP
-.B \-\-block\-outside\-dns
-Block DNS servers on other network adapters to prevent
-DNS leaks. This option prevents any application from accessing
-TCP or UDP port 53 except one inside the tunnel. It uses
-Windows Filtering Platform (WFP) and works on Windows Vista or
-later.
-
-This option is considered unknown on non\-Windows platforms
-and unsupported on Windows XP, resulting in fatal error.
-You may want to use
-.B \-\-setenv opt
-or
-.B \-\-ignore\-unknown\-option
-(not suitable for Windows XP) to ignore said error.
-Note that pushing unknown options from server does not trigger
-fatal errors.
-.\"*********************************************************
+.B \fBblock\-local\fP
+Block access to local LAN when the tunnel is active, except for
+the LAN gateway itself. This is accomplished by routing the local
+LAN (except for the LAN gateway address) into the tunnel.
.TP
-.B \-\-dhcp\-renew
-Ask Windows to renew the TAP adapter lease on startup.
-This option is normally unnecessary, as Windows automatically
-triggers a DHCP renegotiation on the TAP adapter when it
-comes up, however if you set the TAP\-Win32 adapter
-Media Status property to "Always Connected", you may need this
-flag.
-.\"*********************************************************
+.B \fBipv6\fP
+Redirect IPv6 routing into the tunnel. This works similar to
+the \fBdef1\fP flag, that is, more specific IPv6 routes are added
+(\fB2000::/4\fP, \fB3000::/4\fP), covering the whole IPv6
+unicast space.
.TP
-.B \-\-dhcp\-release
-Ask Windows to release the TAP adapter lease on shutdown.
-This option has no effect now, as it is enabled by default starting with OpenVPN 2.4.1.
-.\"*********************************************************
+.B \fB!ipv4\fP
+Do not redirect IPv4 traffic \- typically used in the flag pair
+\fBipv6 !ipv4\fP to redirect IPv6\-only.
+.UNINDENT
+.TP
+.BI \-\-redirect\-private \ flags
+Like \fB\-\-redirect\-gateway\fP, but omit actually changing the default gateway.
+Useful when pushing private subnets.
+.TP
+.BI \-\-route \ args
+Add route to routing table after connection is established. Multiple
+routes can be specified. Routes will be automatically torn down in
+reverse order prior to TUN/TAP device close.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route network/IP
+route network/IP netmask
+route network/IP netmask gateway
+route network/IP netmask gateway metric
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This option is intended as a convenience proxy for the \fBroute\fP(8)
+shell command, while at the same time providing portable semantics
+across OpenVPN\(aqs platform space.
+.INDENT 7.0
+.TP
+.B \fBnetmask\fP
+defaults to \fB255.255.255.255\fP when not given
+.TP
+.B \fBgateway\fP
+default taken from \fB\-\-route\-gateway\fP or the second
+parameter to \fB\-\-ifconfig\fP when \fB\-\-dev tun\fP is specified.
+.TP
+.B \fBmetric\fP
+default taken from \fB\-\-route\-metric\fP if set, otherwise \fB0\fP\&.
+.UNINDENT
+.sp
+The default can be specified by leaving an option blank or setting it to
+\fBdefault\fP\&.
+.sp
+The \fBnetwork\fP and \fBgateway\fP parameters can also be specified as a
+DNS or \fB/etc/hosts\fP file resolvable name, or as one of three special
+keywords:
+.INDENT 7.0
+.TP
+.B \fBvpn_gateway\fP
+The remote VPN endpoint address (derived either from
+\fB\-\-route\-gateway\fP or the second parameter to \fB\-\-ifconfig\fP
+when \fB\-\-dev tun\fP is specified).
+.TP
+.B \fBnet_gateway\fP
+The pre\-existing IP default gateway, read from the
+routing table (not supported on all OSes).
+.TP
+.B \fBremote_host\fP
+The \fB\-\-remote\fP address if OpenVPN is being run in
+client mode, and is undefined in server mode.
+.UNINDENT
+.TP
+.BI \-\-route\-delay \ args
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route\-delay
+route\-delay n
+route\-delay n m
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Delay \fBn\fP seconds (default \fB0\fP) after connection establishment,
+before adding routes. If \fBn\fP is \fB0\fP, routes will be added
+immediately upon connection establishment. If \fB\-\-route\-delay\fP is
+omitted, routes will be added immediately after TUN/TAP device open and
+\fB\-\-up\fP script execution, before any \fB\-\-user\fP or \fB\-\-group\fP privilege
+downgrade (or \fB\-\-chroot\fP execution.)
+.sp
+This option is designed to be useful in scenarios where DHCP is used to
+set tap adapter addresses. The delay will give the DHCP handshake time
+to complete before routes are added.
+.sp
+On Windows, \fB\-\-route\-delay\fP tries to be more intelligent by waiting
+\fBw\fP seconds (default \fB30\fP by default) for the TAP\-Win32 adapter
+to come up before adding routes.
+.TP
+.BI \-\-route\-ipv6 \ args
+Setup IPv6 routing in the system to send the specified IPv6 network into
+OpenVPN\(aqs \fItun\fP\&.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route\-ipv6 ipv6addr/bits [gateway] [metric]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The gateway parameter is only used for IPv6 routes across \fItap\fP devices,
+and if missing, the \fBipv6remote\fP field from \fB\-\-ifconfig\-ipv6\fP or
+\fB\-\-route\-ipv6\-gateway\fP is used.
+.TP
+.BI \-\-route\-gateway \ arg
+Specify a default \fIgateway\fP for use with \fB\-\-route\fP\&.
+.sp
+If \fBdhcp\fP is specified as the parameter, the gateway address will
+be extracted from a DHCP negotiation with the OpenVPN server\-side LAN.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route\-gateway gateway
+route\-gateway dhcp
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
.TP
-.B \-\-register\-dns
-Run ipconfig /flushdns and ipconfig /registerdns on connection initiation.
-This is known to kick Windows into
-recognizing pushed DNS servers.
-.\"*********************************************************
+.BI \-\-route\-ipv6\-gateway \ gw
+Specify a default gateway \fBgw\fP for use with \fB\-\-route\-ipv6\fP\&.
.TP
-.B \-\-pause\-exit
-Put up a "press any key to continue" message on the console prior
-to OpenVPN program exit. This option is automatically used by the
-Windows explorer when OpenVPN is run on a configuration
-file using the right\-click explorer menu.
-.\"*********************************************************
+.BI \-\-route\-metric \ m
+Specify a default metric \fBm\fP for use with \fB\-\-route\fP\&.
.TP
-.B \-\-service exit\-event [0|1]
-Should be used when OpenVPN is being automatically executed by another
-program in such
-a context that no interaction with the user via display or keyboard
-is possible. In general, end\-users should never need to explicitly
-use this option, as it is automatically added by the OpenVPN service wrapper
-when a given OpenVPN configuration is being run as a service.
-
-.B exit\-event
-is the name of a Windows global event object, and OpenVPN will continuously
-monitor the state of this event object and exit when it becomes signaled.
-
-The second parameter indicates the initial state of
-.B exit\-event
-and normally defaults to 0.
-
-Multiple OpenVPN processes can be simultaneously executed with the same
-.B exit\-event
-parameter. In any case, the controlling process can signal
-.B exit\-event,
-causing all such OpenVPN processes to exit.
-
-When executing an OpenVPN process using the
-.B \-\-service
-directive, OpenVPN will probably not have a console
-window to output status/error
-messages, therefore it is useful to use
-.B \-\-log
-or
-.B \-\-log\-append
-to write these messages to a file.
-.\"*********************************************************
+.B \-\-route\-noexec
+Don\(aqt add or remove routes automatically. Instead pass routes to
+\fB\-\-route\-up\fP script using environmental variables.
.TP
-.B \-\-show\-adapters
-(Standalone)
-Show available TAP\-Win32 adapters which can be selected using the
-.B \-\-dev\-node
-option. On non\-Windows systems, the
-.BR ifconfig (8)
-command provides similar functionality.
-.\"*********************************************************
-.TP
-.B \-\-allow\-nonadmin [TAP\-adapter]
-(Standalone)
-Set
-.B TAP\-adapter
-to allow access from non\-administrative accounts. If
-.B TAP\-adapter
-is omitted, all TAP adapters on the system will be configured to allow
-non\-admin access.
-The non\-admin access setting will only persist for the length of time that
-the TAP\-Win32 device object and driver remain loaded, and will need
-to be re\-enabled after a reboot, or if the driver is unloaded
-and reloaded.
-This directive can only be used by an administrator.
-.\"*********************************************************
+.B \-\-route\-nopull
+When used with \fB\-\-client\fP or \fB\-\-pull\fP, accept options pushed by
+server EXCEPT for routes, block\-outside\-dns and dhcp options like DNS
+servers.
+.sp
+When used on the client, this option effectively bars the server from
+adding routes to the client\(aqs routing table, however note that this
+option still allows the server to set the TCP/IP properties of the
+client\(aqs TUN/TAP interface.
+.TP
+.BI \-\-topology \ mode
+Configure virtual addressing topology when running in \fB\-\-dev tun\fP
+mode. This directive has no meaning in \fB\-\-dev tap\fP mode, which always
+uses a \fBsubnet\fP topology.
+.sp
+If you set this directive on the server, the \fB\-\-server\fP and
+\fB\-\-server\-bridge\fP directives will automatically push your chosen
+topology setting to clients as well. This directive can also be manually
+pushed to clients. Like the \fB\-\-dev\fP directive, this directive must
+always be compatible between client and server.
+.sp
+\fBmode\fP can be one of:
+.INDENT 7.0
+.TP
+.B \fBnet30\fP
+Use a point\-to\-point topology, by allocating one /30 subnet
+per client. This is designed to allow point\-to\-point semantics when some
+or all of the connecting clients might be Windows systems. This is the
+default on OpenVPN 2.0.
.TP
-.B \-\-show\-valid\-subnets
-(Standalone)
-Show valid subnets for
-.B \-\-dev tun
-emulation. Since the TAP\-Win32 driver
-exports an ethernet interface to Windows, and since TUN devices are
-point\-to\-point in nature, it is necessary for the TAP\-Win32 driver
-to impose certain constraints on TUN endpoint address selection.
-
-Namely, the point\-to\-point endpoints used in TUN device emulation
-must be the middle two addresses of a /30 subnet (netmask 255.255.255.252).
-.\"*********************************************************
+.B \fBp2p\fP
+Use a point\-to\-point topology where the remote endpoint of
+the client\(aqs tun interface always points to the local endpoint of the
+server\(aqs tun interface. This mode allocates a single IP address per
+connecting client. Only use when none of the connecting clients are
+Windows systems.
.TP
-.B \-\-show\-net
-(Standalone)
-Show OpenVPN's view of the system routing table and network
-adapter list.
-.\"*********************************************************
-.SS PKCS#11 Standalone Options:
-.\"*********************************************************
-.TP
-.B \-\-show\-pkcs11\-ids [provider] [cert_private]
-(Standalone)
-Show PKCS#11 token object list. Specify cert_private as 1
-if certificates are stored as private objects.
-
-If p11\-kit is present on the system, the
-.B provider
-argument is optional; if omitted the default
-.B p11\-kit\-proxy.so
-module will be queried.
-
-.B \-\-verb
-option can be used BEFORE this option to produce debugging information.
-.\"*********************************************************
-.SS Standalone Debug Options:
-.\"*********************************************************
-.TP
-.B \-\-show\-gateway [v6target]
-(Standalone)
-Show current IPv4 and IPv6 default gateway and interface towards the
-gateway (if the protocol in question is enabled). If an IPv6 address
-is passed as argument, the IPv6 route for this host is reported.
-.\"*********************************************************
-.SS IPv6 Related Options
-.\"*********************************************************
-The following options exist to support IPv6 tunneling in peer\-to\-peer
-and client\-server mode. All options are modeled after their IPv4
-counterparts, so more detailed explanations given there apply here
-as well (except for
-.B \-\-topology
-, which has no effect on IPv6).
-.TP
-.B \-\-ifconfig\-ipv6 ipv6addr/bits ipv6remote
-configure IPv6 address
-.B ipv6addr/bits
-on the ``tun'' device. The second parameter is used as route target for
-.B \-\-route\-ipv6
-if no gateway is specified.
-.TP
-.B \-\-route\-ipv6 ipv6addr/bits [gateway] [metric]
-setup IPv6 routing in the system to send the specified IPv6 network
-into OpenVPN's ``tun''. The gateway parameter is only used for
-IPv6 routes across ``tap'' devices, and if missing, the ``ipv6remote''
-field from
-.B \-\-ifconfig\-ipv6
-is used.
-.TP
-.B \-\-server\-ipv6 ipv6addr/bits
-convenience\-function to enable a number of IPv6 related options at
-once, namely
-.B \-\-ifconfig\-ipv6, \-\-ifconfig\-ipv6\-pool
-and
-.B \-\-push tun\-ipv6
-Is only accepted if ``\-\-mode server'' or ``\-\-server'' is set. Pushing of the
-.B \-\-tun\-ipv6
-directive is done for older clients which require an explicit
-``\-\-tun\-ipv6'' in their configuration.
-.TP
-.B \-\-ifconfig\-ipv6\-pool ipv6addr/bits
-Specify an IPv6 address pool for dynamic assignment to clients. The
-pool starts at
-.B ipv6addr
-and matches the offset determined from the start of the IPv4 pool.
-.TP
-.B \-\-ifconfig\-ipv6\-push ipv6addr/bits ipv6remote
-for ccd/ per\-client static IPv6 interface configuration, see
-.B \-\-client\-config\-dir
-and
-.B \-\-ifconfig\-push
-for more details.
-.TP
-.B \-\-iroute\-ipv6 ipv6addr/bits
-for ccd/ per\-client static IPv6 route configuration, see
-.B \-\-iroute
-for more details how to setup and use this, and how
-.B \-\-iroute
-and
-.B \-\-route
-interact.
-
-.\"*********************************************************
-.SH SCRIPTING AND ENVIRONMENTAL VARIABLES
-OpenVPN exports a series
-of environmental variables for use by user\-defined scripts.
-.\"*********************************************************
-.SS Script Order of Execution
-.\"*********************************************************
+.B \fBsubnet\fP
+Use a subnet rather than a point\-to\-point topology by
+configuring the tun interface with a local IP address and subnet mask,
+similar to the topology used in \fB\-\-dev tap\fP and ethernet bridging
+mode. This mode allocates a single IP address per connecting client and
+works on Windows as well. Only available when server and clients are
+OpenVPN 2.1 or higher, or OpenVPN 2.0.x which has been manually patched
+with the \fB\-\-topology\fP directive code. When used on Windows, requires
+version 8.2 or higher of the TAP\-Win32 driver. When used on *nix,
+requires that the tun driver supports an \fBifconfig\fP(8) command which
+sets a subnet instead of a remote endpoint IP address.
+.UNINDENT
+.sp
+\fINote:\fP Using \fB\-\-topology subnet\fP changes the interpretation of the
+arguments of \fB\-\-ifconfig\fP to mean "address netmask", no longer "local
+remote".
+.TP
+.BI \-\-tun\-mtu \ n
+Take the TUN device MTU to be \fBn\fP and derive the link MTU from it
+(default \fB1500\fP). In most cases, you will probably want to leave
+this parameter set to its default value.
+.sp
+The MTU (Maximum Transmission Units) is the maximum datagram size in
+bytes that can be sent unfragmented over a particular network path.
+OpenVPN requires that packets on the control and data channels be sent
+unfragmented.
+.sp
+MTU problems often manifest themselves as connections which hang during
+periods of active usage.
+.sp
+It\(aqs best to use the \fB\-\-fragment\fP and/or \fB\-\-mssfix\fP options to deal
+with MTU sizing issues.
+.TP
+.BI \-\-tun\-mtu\-extra \ n
+Assume that the TUN/TAP device might return as many as \fBn\fP bytes more
+than the \fB\-\-tun\-mtu\fP size on read. This parameter defaults to 0, which
+is sufficient for most TUN devices. TAP devices may introduce additional
+overhead in excess of the MTU size, and a setting of 32 is the default
+when TAP devices are used. This parameter only controls internal OpenVPN
+buffer sizing, so there is no transmission overhead associated with
+using a larger value.
+.UNINDENT
+.SS TUN/TAP standalone operations
+.sp
+These two standalone operations will require \fB\-\-dev\fP and optionally
+\fB\-\-user\fP and/or \fB\-\-group\fP\&.
+.INDENT 0.0
.TP
-.B \-\-up
-Executed after TCP/UDP socket bind and TUN/TAP open.
-.\"*********************************************************
+.B \-\-mktun
+(Standalone) Create a persistent tunnel on platforms which support them
+such as Linux. Normally TUN/TAP tunnels exist only for the period of
+time that an application has them open. This option takes advantage of
+the TUN/TAP driver\(aqs ability to build persistent tunnels that live
+through multiple instantiations of OpenVPN and die only when they are
+deleted or the machine is rebooted.
+.sp
+One of the advantages of persistent tunnels is that they eliminate the
+need for separate \fB\-\-up\fP and \fB\-\-down\fP scripts to run the appropriate
+\fBifconfig\fP(8) and \fBroute\fP(8) commands. These commands can be
+placed in the the same shell script which starts or terminates an
+OpenVPN session.
+.sp
+Another advantage is that open connections through the TUN/TAP\-based
+tunnel will not be reset if the OpenVPN peer restarts. This can be
+useful to provide uninterrupted connectivity through the tunnel in the
+event of a DHCP reset of the peer\(aqs public IP address (see the
+\fB\-\-ipchange\fP option above).
+.sp
+One disadvantage of persistent tunnels is that it is harder to
+automatically configure their MTU value (see \fB\-\-link\-mtu\fP and
+\fB\-\-tun\-mtu\fP above).
+.sp
+On some platforms such as Windows, TAP\-Win32 tunnels are persistent by
+default.
.TP
-.B \-\-tls\-verify
+.B \-\-rmtun
+(Standalone) Remove a persistent tunnel.
+.UNINDENT
+.SS Virtual Routing and Forwarding
+.sp
+Options in this section relates to configuration of virtual routing and
+forwarding in combination with the underlying operating system.
+.sp
+As of today this is only supported on Linux, a kernel >= 4.9 is
+recommended.
+.sp
+This could come in handy when for example the external network should be
+only used as a means to connect to some VPN endpoints and all regular
+traffic should only be routed through any tunnel(s). This could be
+achieved by setting up a VRF and configuring the interface connected to
+the external network to be part of the VRF. The examples below will cover
+this setup.
+.sp
+Another option would be to put the tun/tap interface into a VRF. This could
+be done by an up\-script which uses the \fBip link set\fP command shown
+below.
+.SS VRF setup with iproute2
+.sp
+Create VRF \fBvrf_external\fP and map it to routing table \fB1023\fP
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ip link add vrf_external type vrf table 1023
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Move \fBeth0\fP into \fBvrf_external\fP
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+ip link set master vrf_external dev eth0
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Any prefixes configured on \fBeth0\fP will be moved from the :code\(gamain\(ga
+routing table into routing table \fI1023\fP
+.SS VRF setup with ifupdown
+.sp
+For Debian based Distributions \fBifupdown2\fP provides an almost drop\-in
+replacement for \fBifupdown\fP including VRFs and other features.
+A configuration for an interface \fBeth0\fP being part of VRF
+code:\fIvrf_external\fP could look like this:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+auto eth0
+iface eth0
+ address 192.0.2.42/24
+ address 2001:db8:08:15::42/64
+ gateway 192.0.2.1
+ gateway 2001:db8:08:15::1
+ vrf vrf_external
+
+auto vrf_external
+iface vrf_external
+ vrf\-table 1023
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS OpenVPN configuration
+.sp
+The OpenVPN configuration needs to contain this line:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+bind\-dev vrf_external
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Further reading
+.sp
+Wikipedia has nice page one VRFs: \fI\%https://en.wikipedia.org/wiki/Virtual_routing_and_forwarding\fP
+.sp
+This talk from the Network Track of FrOSCon 2018 provides an overview about
+advanced layer 2 and layer 3 features of Linux
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.IP \(bu 2
+Slides: \fI\%https://www.slideshare.net/BarbarossaTM/l2l3\-fr\-fortgeschrittene\-helle\-und\-dunkle\-magie\-im\-linuxnetzwerkstack\fP
+.IP \(bu 2
+Video (german): \fI\%https://media.ccc.de/v/froscon2018\-2247\-l2_l3_fur_fortgeschrittene_\-_helle_und_dunkle_magie_im_linux\-netzwerkstack\fP
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH SCRIPTING INTEGRATION
+.sp
+OpenVPN can execute external scripts in various phases of the lifetime of
+the OpenVPN process.
+.SS Script Order of Execution
+.INDENT 0.0
+.IP 1. 4
+\fB\-\-up\fP
+.sp
+Executed after TCP/UDP socket bind and TUN/TAP open.
+.IP 2. 4
+\fB\-\-tls\-verify\fP
+.sp
Executed when we have a still untrusted remote peer.
-.\"*********************************************************
-.TP
-.B \-\-ipchange
+.IP 3. 4
+\fB\-\-ipchange\fP
+.sp
Executed after connection authentication, or remote IP address change.
-.\"*********************************************************
-.TP
-.B \-\-client\-connect
-Executed in
-.B \-\-mode server
-mode immediately after client authentication.
-.\"*********************************************************
-.TP
-.B \-\-route\-up
-Executed after connection authentication, either
-immediately after, or some number of seconds after
-as defined by the
-.B \-\-route\-delay
-option.
-.\"*********************************************************
-.TP
-.B \-\-route\-pre\-down
+.IP 4. 4
+\fB\-\-client\-connect\fP
+.sp
+Executed in \fB\-\-mode server\fP mode immediately after client
+authentication.
+.IP 5. 4
+\fB\-\-route\-up\fP
+.sp
+Executed after connection authentication, either immediately after, or
+some number of seconds after as defined by the \fB\-\-route\-delay\fP option.
+.IP 6. 4
+\fB\-\-route\-pre\-down\fP
+.sp
Executed right before the routes are removed.
-.\"*********************************************************
+.IP 7. 4
+\fB\-\-client\-disconnect\fP
+.sp
+Executed in \fB\-\-mode server\fP mode on client instance shutdown.
+.IP 8. 4
+\fB\-\-down\fP
+.sp
+Executed after TCP/UDP and TUN/TAP close.
+.IP 9. 4
+\fB\-\-learn\-address\fP
+.sp
+Executed in \fB\-\-mode server\fP mode whenever an IPv4 address/route or MAC
+address is added to OpenVPN\(aqs internal routing table.
+.IP 10. 4
+\fB\-\-auth\-user\-pass\-verify\fP
+.sp
+Executed in \fB\-\-mode server\fP mode on new client connections, when the
+client is still untrusted.
+.UNINDENT
+.SS SCRIPT HOOKS
+.INDENT 0.0
+.TP
+.BI \-\-auth\-user\-pass\-verify \ args
+Require the client to provide a username/password (possibly in addition
+to a client certificate) for authentication.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+auth\-user\-pass\-verify cmd method
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+OpenVPN will run command \fBcmd\fP to validate the username/password
+provided by the client.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+If \fBmethod\fP is set to \fBvia\-env\fP, OpenVPN will call \fBscript\fP
+with the environmental variables \fBusername\fP and \fBpassword\fP
+set to the username/password strings provided by the client. \fIBeware\fP
+that this method is insecure on some platforms which make the environment
+of a process publicly visible to other unprivileged processes.
+.sp
+If \fBmethod\fP is set to \fBvia\-file\fP, OpenVPN will write the username
+and password to the first two lines of a temporary file. The filename
+will be passed as an argument to \fBscript\fP, and the file will be
+automatically deleted by OpenVPN after the script returns. The location
+of the temporary file is controlled by the \fB\-\-tmp\-dir\fP option, and
+will default to the current directory if unspecified. For security,
+consider setting \fB\-\-tmp\-dir\fP to a volatile storage medium such as
+\fB/dev/shm\fP (if available) to prevent the username/password file
+from touching the hard drive.
+.sp
+The script should examine the username and password, returning a success
+exit code (\fB0\fP) if the client\(aqs authentication request is to be
+accepted, or a failure code (\fB1\fP) to reject the client.
+.sp
+This directive is designed to enable a plugin\-style interface for
+extending OpenVPN\(aqs authentication capabilities.
+.sp
+To protect against a client passing a maliciously formed username or
+password string, the username string must consist only of these
+characters: alphanumeric, underbar (\(aq\fB_\fP\(aq), dash (\(aq\fB\-\fP\(aq),
+dot (\(aq\fB\&.\fP\(aq), or at (\(aq\fB@\fP\(aq). The password string can consist
+of any printable characters except for CR or LF. Any illegal characters
+in either the username or password string will be converted to
+underbar (\(aq\fB_\fP\(aq).
+.sp
+Care must be taken by any user\-defined scripts to avoid creating a
+security vulnerability in the way that these strings are handled. Never
+use these strings in such a way that they might be escaped or evaluated
+by a shell interpreter.
+.sp
+For a sample script that performs PAM authentication, see
+\fBsample\-scripts/auth\-pam.pl\fP in the OpenVPN source distribution.
+.TP
+.BI \-\-client\-connect \ cmd
+Run command \fBcmd\fP on client connection.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+The command is passed the common name and IP address of the
+just\-authenticated client as environmental variables (see environmental
+variable section below). The command is also passed the pathname of a
+freshly created temporary file as the last argument (after any arguments
+specified in \fBcmd\fP ), to be used by the command to pass dynamically
+generated config file directives back to OpenVPN.
+.sp
+If the script wants to generate a dynamic config file to be applied on
+the server when the client connects, it should write it to the file
+named by the last argument.
+.sp
+See the \fB\-\-client\-config\-dir\fP option below for options which can be
+legally used in a dynamically generated config file.
+.sp
+Note that the return value of \fBscript\fP is significant. If \fBscript\fP
+returns a non\-zero error status, it will cause the client to be
+disconnected.
+.sp
+If a \fB\-\-client\-connect\fP wants to defer the generating of the
+configuration then the script needs to use the
+\fBclient_connect_deferred_file\fP and
+\fBclient_connect_config_file\fP environment variables, and write
+status accordingly into these files. See the \fI\%Environmental Variables\fP
+section for more details.
+.TP
+.BI \-\-client\-disconnect \ cmd
+Like \fB\-\-client\-connect\fP but called on client instance shutdown. Will
+not be called unless the \fB\-\-client\-connect\fP script and plugins (if
+defined) were previously called on this instance with successful (0)
+status returns.
+.sp
+The exception to this rule is if the \fB\-\-client\-disconnect\fP command or
+plugins are cascaded, and at least one client\-connect function
+succeeded, then ALL of the client\-disconnect functions for scripts and
+plugins will be called on client instance object deletion, even in cases
+where some of the related client\-connect functions returned an error
+status.
+.sp
+The \fB\-\-client\-disconnect\fP command is not passed any extra arguments
+(only those arguments specified in cmd, if any).
+.TP
+.BI \-\-down \ cmd
+Run command \fBcmd\fP after TUN/TAP device close (post \fB\-\-user\fP UID
+change and/or \fB\-\-chroot\fP ). \fBcmd\fP consists of a path to script (or
+executable program), optionally followed by arguments. The path and
+arguments may be single\- or double\-quoted and/or escaped using a
+backslash, and should be separated by one or more spaces.
+.sp
+Called with the same parameters and environmental variables as the
+\fB\-\-up\fP option above.
+.sp
+Note that if you reduce privileges by using \fB\-\-user\fP and/or
+\fB\-\-group\fP, your \fB\-\-down\fP script will also run at reduced privilege.
.TP
-.B \-\-client\-disconnect
-Executed in
-.B \-\-mode server
-mode on client instance shutdown.
-.\"*********************************************************
+.B \-\-down\-pre
+Call \fB\-\-down\fP cmd/script before, rather than after, TUN/TAP close.
+.TP
+.BI \-\-ipchange \ cmd
+Run command \fBcmd\fP when our remote ip\-address is initially
+authenticated or changes.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+When \fBcmd\fP is executed two arguments are appended after any arguments
+specified in \fBcmd\fP , as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cmd ip address port number
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Don\(aqt use \fB\-\-ipchange\fP in \fB\-\-mode server\fP mode. Use a
+\fB\-\-client\-connect\fP script instead.
+.sp
+See the \fI\%Environmental Variables\fP section below for additional
+parameters passed as environmental variables.
+.sp
+If you are running in a dynamic IP address environment where the IP
+addresses of either peer could change without notice, you can use this
+script, for example, to edit the \fB/etc/hosts\fP file with the current
+address of the peer. The script will be run every time the remote peer
+changes its IP address.
+.sp
+Similarly if \fIour\fP IP address changes due to DHCP, we should configure
+our IP address change script (see man page for \fBdhcpcd\fP(8)) to
+deliver a \fBSIGHUP\fP or \fBSIGUSR1\fP signal to OpenVPN. OpenVPN will
+then re\-establish a connection with its most recently authenticated
+peer on its new IP address.
.TP
-.B \-\-down
-Executed after TCP/UDP and TUN/TAP close.
-.\"*********************************************************
-.TP
-.B \-\-learn\-address
-Executed in
-.B \-\-mode server
-mode whenever an IPv4 address/route or MAC address is added to OpenVPN's
-internal routing table.
-.\"*********************************************************
-.TP
-.B \-\-auth\-user\-pass\-verify
-Executed in
-.B \-\-mode server
-mode on new client connections, when the client is
-still untrusted.
-.\"*********************************************************
+.BI \-\-learn\-address \ cmd
+Run command \fBcmd\fP to validate client virtual addresses or routes.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+Three arguments will be appended to any arguments in \fBcmd\fP as follows:
+.INDENT 7.0
+.TP
+.B \fB$1\fP \- [operation]
+\fB"add"\fP, \fB"update"\fP, or \fB"delete"\fP based on whether
+or not the address is being added to, modified, or deleted from
+OpenVPN\(aqs internal routing table.
+.TP
+.B \fB$2\fP \- [address]
+The address being learned or unlearned. This can be an IPv4 address
+such as \fB"198.162.10.14"\fP, an IPv4 subnet such as
+\fB"198.162.10.0/24"\fP, or an ethernet MAC address (when
+\fB\-\-dev tap\fP is being used) such as \fB"00:FF:01:02:03:04"\fP\&.
+.TP
+.B \fB$3\fP \- [common name]
+The common name on the certificate associated with the client linked
+to this address. Only present for \fB"add"\fP or \fB"update"\fP
+operations, not \fB"delete"\fP\&.
+.UNINDENT
+.sp
+On \fB"add"\fP or \fB"update"\fP methods, if the script returns
+a failure code (non\-zero), OpenVPN will reject the address and will not
+modify its internal routing table.
+.sp
+Normally, the \fBcmd\fP script will use the information provided above to
+set appropriate firewall entries on the VPN TUN/TAP interface. Since
+OpenVPN provides the association between virtual IP or MAC address and
+the client\(aqs authenticated common name, it allows a user\-defined script
+to configure firewall access policies with regard to the client\(aqs
+high\-level common name, rather than the low level client virtual
+addresses.
+.TP
+.BI \-\-route\-up \ cmd
+Run command \fBcmd\fP after routes are added, subject to \fB\-\-route\-delay\fP\&.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+See the \fI\%Environmental Variables\fP section below for additional
+parameters passed as environmental variables.
+.TP
+.BI \-\-route\-pre\-down \ cmd
+Run command \fBcmd\fP before routes are removed upon disconnection.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+See the \fI\%Environmental Variables\fP section below for additional
+parameters passed as environmental variables.
+.TP
+.BI \-\-setenv \ args
+Set a custom environmental variable \fBname=value\fP to pass to script.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+setenv name value
+setenv FORWARD_COMPATIBLE 1
+setenv opt config_option
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+By setting \fBFORWARD_COMPATIBLE\fP to \fB1\fP, the config file
+syntax checking is relaxed so that unknown directives will trigger a
+warning but not a fatal error, on the assumption that a given unknown
+directive might be valid in future OpenVPN versions.
+.sp
+This option should be used with caution, as there are good security
+reasons for having OpenVPN fail if it detects problems in a config file.
+Having said that, there are valid reasons for wanting new software
+features to gracefully degrade when encountered by older software
+versions.
+.sp
+It is also possible to tag a single directive so as not to trigger a
+fatal error if the directive isn\(aqt recognized. To do this, prepend the
+following before the directive: \fBsetenv opt\fP
+.sp
+Versions prior to OpenVPN 2.3.3 will always ignore options set with the
+\fBsetenv opt\fP directive.
+.sp
+See also \fB\-\-ignore\-unknown\-option\fP
+.TP
+.BI \-\-setenv\-safe \ args
+Set a custom environmental variable \fBOPENVPN_name\fP to \fBvalue\fP
+to pass to scripts.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+setenv\-safe name value
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This directive is designed to be pushed by the server to clients, and
+the prepending of \fBOPENVPN_\fP to the environmental variable is a
+safety precaution to prevent a \fBLD_PRELOAD\fP style attack from a
+malicious or compromised server.
+.TP
+.BI \-\-tls\-verify \ cmd
+Run command \fBcmd\fP to verify the X509 name of a pending TLS connection
+that has otherwise passed all other tests of certification (except for
+revocation via \fB\-\-crl\-verify\fP directive; the revocation test occurs
+after the \fB\-\-tls\-verify\fP test).
+.sp
+\fBcmd\fP should return \fB0\fP to allow the TLS handshake to proceed,
+or \fB1\fP to fail.
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+When \fBcmd\fP is executed two arguments are appended after any arguments
+specified in \fBcmd\fP, as follows:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cmd certificate_depth subject
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+These arguments are, respectively, the current certificate depth and the
+X509 subject distinguished name (dn) of the peer.
+.sp
+This feature is useful if the peer you want to trust has a certificate
+which was signed by a certificate authority who also signed many other
+certificates, where you don\(aqt necessarily want to trust all of them, but
+rather be selective about which peer certificate you will accept. This
+feature allows you to write a script which will test the X509 name on a
+certificate and decide whether or not it should be accepted. For a
+simple perl script which will test the common name field on the
+certificate, see the file \fBverify\-cn\fP in the OpenVPN distribution.
+.sp
+See the \fI\%Environmental Variables\fP section below for additional
+parameters passed as environmental variables.
+.TP
+.BI \-\-up \ cmd
+Run command \fBcmd\fP after successful TUN/TAP device open (pre \fB\-\-user\fP
+UID change).
+.sp
+\fBcmd\fP consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single\- or
+double\-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.
+.sp
+The up command is useful for specifying route commands which route IP
+traffic destined for private subnets which exist at the other end of the
+VPN connection into the tunnel.
+.sp
+For \fB\-\-dev tun\fP execute as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cmd tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_remote_ip [init | restart]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+For \fB\-\-dev tap\fP execute as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cmd tap_dev tap_mtu link_mtu ifconfig_local_ip ifconfig_netmask [init | restart]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+See the \fI\%Environmental Variables\fP section below for additional
+parameters passed as environmental variables.
+.sp
+Note that if \fBcmd\fP includes arguments, all OpenVPN\-generated arguments
+will be appended to them to build an argument list with which the
+executable will be called.
+.sp
+Typically, \fBcmd\fP will run a script to add routes to the tunnel.
+.sp
+Normally the up script is called after the TUN/TAP device is opened. In
+this context, the last command line parameter passed to the script will
+be \fIinit.\fP If the \fB\-\-up\-restart\fP option is also used, the up script
+will be called for restarts as well. A restart is considered to be a
+partial reinitialization of OpenVPN where the TUN/TAP instance is
+preserved (the \fB\-\-persist\-tun\fP option will enable such preservation).
+A restart can be generated by a SIGUSR1 signal, a \fB\-\-ping\-restart\fP
+timeout, or a connection reset when the TCP protocol is enabled with the
+\fB\-\-proto\fP option. If a restart occurs, and \fB\-\-up\-restart\fP has been
+specified, the up script will be called with \fIrestart\fP as the last
+parameter.
+.INDENT 7.0
+.TP
+.B \fINOTE:\fP
+On restart, OpenVPN will not pass the full set of environment
+variables to the script. Namely, everything related to routing and
+gateways will not be passed, as nothing needs to be done anyway \- all
+the routing setup is already in place. Additionally, the up\-restart
+script will run with the downgraded UID/GID settings (if configured).
+.UNINDENT
+.sp
+The following standalone example shows how the \fB\-\-up\fP script can be
+called in both an initialization and restart context. (\fINOTE:\fP for
+security reasons, don\(aqt run the following example unless UDP port 9999
+is blocked by your firewall. Also, the example will run indefinitely, so
+you should abort with control\-c).
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+openvpn \-\-dev tun \-\-port 9999 \-\-verb 4 \-\-ping\-restart 10 \e
+ \-\-up \(aqecho up\(aq \-\-down \(aqecho down\(aq \-\-persist\-tun \e
+ \-\-up\-restart
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Note that OpenVPN also provides the \fB\-\-ifconfig\fP option to
+automatically ifconfig the TUN device, eliminating the need to define an
+\fB\-\-up\fP script, unless you also want to configure routes in the
+\fB\-\-up\fP script.
+.sp
+If \fB\-\-ifconfig\fP is also specified, OpenVPN will pass the ifconfig
+local and remote endpoints on the command line to the \fB\-\-up\fP script so
+that they can be used to configure routes such as:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+route add \-net 10.0.0.0 netmask 255.255.255.0 gw $5
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.TP
+.B \-\-up\-delay
+Delay TUN/TAP open and possible \fB\-\-up\fP script execution until after
+TCP/UDP connection establishment with peer.
+.sp
+In \fB\-\-proto udp\fP mode, this option normally requires the use of
+\fB\-\-ping\fP to allow connection initiation to be sensed in the absence of
+tunnel data, since UDP is a "connectionless" protocol.
+.sp
+On Windows, this option will delay the TAP\-Win32 media state
+transitioning to "connected" until connection establishment, i.e. the
+receipt of the first authenticated packet from the peer.
+.TP
+.B \-\-up\-restart
+Enable the \fB\-\-up\fP and \fB\-\-down\fP scripts to be called for restarts as
+well as initial program start. This option is described more fully above
+in the \fB\-\-up\fP option documentation.
+.UNINDENT
.SS String Types and Remapping
-In certain cases, OpenVPN will perform remapping of characters
-in strings. Essentially, any characters outside the set of
-permitted characters for each string type will be converted
-to underbar ('_').
-
-.B Q:
-Why is string remapping necessary?
-
-.B A:
-It's an important security feature to prevent the malicious coding of
-strings from untrusted sources to be passed as parameters to scripts,
-saved in the environment, used as a common name, translated to a filename,
-etc.
-
-.B Q:
-Can string remapping be disabled?
-
-.B A:
-Yes, by using the
-.B \-\-no\-name\-remapping
-option, however this should be considered an advanced option.
-
-Here is a brief rundown of OpenVPN's current string types and the
+.sp
+In certain cases, OpenVPN will perform remapping of characters in
+strings. Essentially, any characters outside the set of permitted
+characters for each string type will be converted to underbar (\(aq_\(aq).
+.INDENT 0.0
+.TP
+.B \fIQ: Why is string remapping necessary?\fP
+It\(aqs an important security feature to prevent the malicious
+coding of strings from untrusted sources to be passed as parameters to
+scripts, saved in the environment, used as a common name, translated to
+a filename, etc.
+.TP
+.B \fIQ: Can string remapping be disabled?\fP
+Yes, by using the \fB\-\-no\-name\-remapping\fP option, however this
+should be considered an advanced option.
+.UNINDENT
+.sp
+Here is a brief rundown of OpenVPN\(aqs current string types and the
permitted character class for each string:
-
-.B X509 Names:
-Alphanumeric, underbar ('_'), dash ('\-'), dot ('.'), at
-('@'), colon (':'), slash ('/'), and equal ('='). Alphanumeric is defined
-as a character which will cause the C library isalnum() function to return
-true.
-
-.B Common Names:
-Alphanumeric, underbar ('_'), dash ('\-'), dot ('.'), and at
-('@').
-
-.B \-\-auth\-user\-pass username:
-Same as Common Name, with one exception: starting with OpenVPN 2.0.1,
-the username is passed to the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY plugin in its raw form,
+.INDENT 0.0
+.TP
+.B \fIX509 Names\fP
+Alphanumeric, underbar (\(aq_\(aq), dash (\(aq\-\(aq), dot (\(aq.\(aq), at
+(\(aq@\(aq), colon (\(aq:\(aq), slash (\(aq/\(aq), and equal (\(aq=\(aq). Alphanumeric is
+defined as a character which will cause the C library isalnum() function
+to return true.
+.TP
+.B \fICommon Names\fP
+Alphanumeric, underbar (\(aq_\(aq), dash (\(aq\-\(aq), dot (\(aq.\(aq), and at (\(aq@\(aq).
+.TP
+.B \fI\-\-auth\-user\-pass username\fP
+Same as Common Name, with one exception:
+starting with OpenVPN 2.0.1, the username is passed to the
+\fBOPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\fP plugin in its raw form,
without string remapping.
-
-.B \-\-auth\-user\-pass password:
-Any "printable" character except CR or LF.
-Printable is defined to be a character which will cause the C library
-isprint() function to return true.
-
-.B \-\-client\-config\-dir filename as derived from common name or username:
-Alphanumeric, underbar ('_'), dash ('\-'), and dot ('.') except for "." or
-".." as standalone strings. As of v2.0.1\-rc6, the at ('@') character has
-been added as well for compatibility with the common name character class.
-
-.B Environmental variable names:
-Alphanumeric or underbar ('_').
-
-.B Environmental variable values:
-Any printable character.
-
-For all cases, characters in a string which are not members of the legal
-character class for that string type will be remapped to underbar ('_').
-.\"*********************************************************
-.SS Environmental Variables
-Once set, a variable is persisted
-indefinitely until it is reset by a new value or a restart,
-
-As of OpenVPN 2.0\-beta12, in server mode, environmental
-variables set by OpenVPN
-are scoped according to the client objects
-they are
-associated with, so there should not be any issues with
-scripts having access to stale, previously set variables
-which refer to different client instances.
-.\"*********************************************************
-.TP
-.B bytes_received
-Total number of bytes received from client during VPN session.
-Set prior to execution of the
-.B \-\-client\-disconnect
-script.
-.\"*********************************************************
.TP
-.B bytes_sent
-Total number of bytes sent to client during VPN session.
-Set prior to execution of the
-.B \-\-client\-disconnect
-script.
-.\"*********************************************************
-.TP
-.B common_name
-The X509 common name of an authenticated client.
-Set prior to execution of
-.B \-\-client\-connect, \-\-client\-disconnect,
-and
-.B \-\-auth\-user\-pass\-verify
-scripts.
-.\"*********************************************************
+.B \fI\-\-auth\-user\-pass password\fP
+Any "printable" character except CR or LF. Printable is defined to be
+a character which will cause the C library isprint() function to
+return true.
.TP
-.B config
-Name of first
-.B \-\-config
-file.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
+.B \fI\-\-client\-config\-dir filename as derived from common name or\(gausername\fP
+Alphanumeric, underbar (\(aq_\(aq), dash (\(aq\-\(aq), and dot (\(aq.\(aq) except for "."
+or ".." as standalone strings. As of v2.0.1\-rc6, the at (\(aq@\(aq) character
+has been added as well for compatibility with the common name character
+class.
.TP
-.B daemon
-Set to "1" if the
-.B \-\-daemon
-directive is specified, or "0" otherwise.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
+.B \fIEnvironmental variable names\fP
+Alphanumeric or underbar (\(aq_\(aq).
.TP
-.B daemon_log_redirect
-Set to "1" if the
-.B \-\-log
-or
-.B \-\-log\-append
-directives are specified, or "0" otherwise.
+.B \fIEnvironmental variable values\fP
+Any printable character.
+.UNINDENT
+.sp
+For all cases, characters in a string which are not members of the legal
+character class for that string type will be remapped to underbar
+(\(aq_\(aq).
+.SS Environmental Variables
+.sp
+Once set, a variable is persisted indefinitely until it is reset by a
+new value or a restart,
+.sp
+As of OpenVPN 2.0\-beta12, in server mode, environmental variables set by
+OpenVPN are scoped according to the client objects they are associated
+with, so there should not be any issues with scripts having access to
+stale, previously set variables which refer to different client
+instances.
+.INDENT 0.0
+.TP
+.B \fBbytes_received\fP
+Total number of bytes received from client during VPN session. Set prior
+to execution of the \fB\-\-client\-disconnect\fP script.
+.TP
+.B \fBbytes_sent\fP
+Total number of bytes sent to client during VPN session. Set prior to
+execution of the \fB\-\-client\-disconnect\fP script.
+.TP
+.B \fBclient_connect_config_file\fP
+The path to the configuration file that should be written to by the
+\fB\-\-client\-connect\fP script (optional, if per\-session configuration
+is desired). This is the same file name as passed via command line
+argument on the call to the \fB\-\-client\-connect\fP script.
+.TP
+.B \fBclient_connect_deferred_file\fP
+This file can be optionally written to in order to to communicate a
+status code of the \fB\-\-client\-connect\fP script or plgin. Only the
+first character in the file is relevant. It must be either \fB1\fP
+to indicate normal script execution, \fB0\fP indicates an error (in
+the same way that a non zero exit status does) or \fB2\fP to indicate
+that the script deferred returning the config file.
+.sp
+For deferred (background) handling, the script or plugin MUST write
+\fB2\fP to the file to indicate the deferral and then return with
+exit code \fB0\fP to signal \fBdeferred handler started OK\fP\&.
+.sp
+A background process or similar must then take care of writing the
+configuration to the file indicated by the
+\fBclient_connect_config_file\fP environment variable and when
+finished, write the a \fB1\fP to this file (or \fB0\fP in case of
+an error).
+.sp
+The absence of any character in the file when the script finishes
+executing is interpreted the same as \fB1\fP\&. This allows scripts
+that are not written to support the defer mechanism to be used
+unmodified.
+.TP
+.B \fBcommon_name\fP
+The X509 common name of an authenticated client. Set prior to execution
+of \fB\-\-client\-connect\fP, \fB\-\-client\-disconnect\fP and
+\fB\-\-auth\-user\-pass\-verify\fP scripts.
+.TP
+.B \fBconfig\fP
+Name of first \fB\-\-config\fP file. Set on program initiation and reset on
+SIGHUP.
+.TP
+.B \fBdaemon\fP
+Set to "1" if the \fB\-\-daemon\fP directive is specified, or "0" otherwise.
Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
.TP
-.B dev
-The actual name of the TUN/TAP device, including
-a unit number if it exists.
-Set prior to
-.B \-\-up
-or
-.B \-\-down
-script execution.
-.\"*********************************************************
-.TP
-.B dev_idx
-On Windows, the device index of the TUN/TAP adapter (to
-be used in netsh.exe calls which sometimes just do not work
-right with interface names).
-Set prior to
-.B \-\-up
-or
-.B \-\-down
-script execution.
-.\"*********************************************************
-.TP
-.B foreign_option_{n}
-An option pushed via
-.B \-\-push
-to a client which does not natively support it,
-such as
-.B \-\-dhcp\-option
-on a non\-Windows system, will be recorded to this
-environmental variable sequence prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_broadcast
-The broadcast address for the virtual
-ethernet segment which is derived from the
-.B \-\-ifconfig
-option when
-.B \-\-dev tap
-is used.
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
+.B \fBdaemon_log_redirect\fP
+Set to "1" if the \fB\-\-log\fP or \fB\-\-log\-append\fP directives are
+specified, or "0" otherwise. Set on program initiation and reset on
+SIGHUP.
.TP
-.B ifconfig_ipv6_local
-The local VPN endpoint IPv6 address specified in the
-.B \-\-ifconfig\-ipv6
-option (first parameter).
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_ipv6_netbits
-The prefix length of the IPv6 network on the VPN interface. Derived from
-the /nnn parameter of the IPv6 address in the
-.B \-\-ifconfig\-ipv6
-option (first parameter).
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
+.B \fBdev\fP
+The actual name of the TUN/TAP device, including a unit number if it
+exists. Set prior to \fB\-\-up\fP or \fB\-\-down\fP script execution.
.TP
-.B ifconfig_ipv6_remote
-The remote VPN endpoint IPv6 address specified in the
-.B \-\-ifconfig\-ipv6
-option (second parameter).
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_local
-The local VPN endpoint IP address specified in the
-.B \-\-ifconfig
-option (first parameter).
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_remote
-The remote VPN endpoint IP address specified in the
-.B \-\-ifconfig
-option (second parameter) when
-.B \-\-dev tun
-is used.
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_netmask
-The subnet mask of the virtual ethernet segment
-that is specified as the second parameter to
-.B \-\-ifconfig
-when
-.B \-\-dev tap
-is being used.
-Set prior to OpenVPN calling the
-.I ifconfig
-or
-.I netsh
-(windows version of ifconfig) commands which
-normally occurs prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B ifconfig_pool_local_ip
-The local
-virtual IP address for the TUN/TAP tunnel taken from an
-.B \-\-ifconfig\-push
-directive if specified, or otherwise from
-the ifconfig pool (controlled by the
-.B \-\-ifconfig\-pool
-config file directive).
-Only set for
-.B \-\-dev tun
-tunnels.
-This option is set on the server prior to execution
-of the
-.B \-\-client\-connect
-and
-.B \-\-client\-disconnect
-scripts.
-.\"*********************************************************
-.TP
-.B ifconfig_pool_netmask
-The
-virtual IP netmask for the TUN/TAP tunnel taken from an
-.B \-\-ifconfig\-push
-directive if specified, or otherwise from
-the ifconfig pool (controlled by the
-.B \-\-ifconfig\-pool
-config file directive).
-Only set for
-.B \-\-dev tap
-tunnels.
-This option is set on the server prior to execution
-of the
-.B \-\-client\-connect
-and
-.B \-\-client\-disconnect
-scripts.
-.\"*********************************************************
-.TP
-.B ifconfig_pool_remote_ip
-The remote
-virtual IP address for the TUN/TAP tunnel taken from an
-.B \-\-ifconfig\-push
-directive if specified, or otherwise from
-the ifconfig pool (controlled by the
-.B \-\-ifconfig\-pool
-config file directive).
-This option is set on the server prior to execution
-of the
-.B \-\-client\-connect
-and
-.B \-\-client\-disconnect
-scripts.
-.\"*********************************************************
+.B \fBdev_idx\fP
+On Windows, the device index of the TUN/TAP adapter (to be used in
+netsh.exe calls which sometimes just do not work right with interface
+names). Set prior to \fB\-\-up\fP or \fB\-\-down\fP script execution.
.TP
-.B link_mtu
-The maximum packet size (not including the IP header)
-of tunnel data in UDP tunnel transport mode.
-Set prior to
-.B \-\-up
-or
-.B \-\-down
+.B \fBforeign_option_{n}\fP
+An option pushed via \fB\-\-push\fP to a client which does not natively
+support it, such as \fB\-\-dhcp\-option\fP on a non\-Windows system, will be
+recorded to this environmental variable sequence prior to \fB\-\-up\fP
script execution.
-.\"*********************************************************
.TP
-.B local
-The
-.B \-\-local
-parameter.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
+.B \fBifconfig_broadcast\fP
+The broadcast address for the virtual ethernet segment which is derived
+from the \fB\-\-ifconfig\fP option when \fB\-\-dev tap\fP is used. Set prior to
+OpenVPN calling the \fBifconfig\fP or \fBnetsh\fP (windows version
+of ifconfig) commands which normally occurs prior to \fB\-\-up\fP script
+execution.
.TP
-.B local_port
-The local port number or name, specified by
-.B \-\-port
-or
-.B \-\-lport.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
-.TP
-.B password
-The password provided by a connecting client.
-Set prior to
-.B \-\-auth\-user\-pass\-verify
-script execution only when the
-.B via\-env
-modifier is specified, and deleted from the environment
-after the script returns.
-.\"*********************************************************
-.TP
-.B proto
-The
-.B \-\-proto
-parameter.
+.B \fBifconfig_ipv6_local\fP
+The local VPN endpoint IPv6 address specified in the
+\fB\-\-ifconfig\-ipv6\fP option (first parameter). Set prior to OpenVPN
+calling the \fBifconfig\fP or code:\fInetsh\fP (windows version of
+ifconfig) commands which normally occurs prior to \fB\-\-up\fP script
+execution.
+.TP
+.B \fBifconfig_ipv6_netbits\fP
+The prefix length of the IPv6 network on the VPN interface. Derived
+from the /nnn parameter of the IPv6 address in the \fB\-\-ifconfig\-ipv6\fP
+option (first parameter). Set prior to OpenVPN calling the
+\fBifconfig\fP or \fBnetsh\fP (windows version of ifconfig)
+commands which normally occurs prior to \fB\-\-up\fP script execution.
+.TP
+.B \fBifconfig_ipv6_remote\fP
+The remote VPN endpoint IPv6 address specified in the
+\fB\-\-ifconfig\-ipv6\fP option (second parameter). Set prior to OpenVPN
+calling the \fBifconfig\fP or \fBnetsh\fP (windows version of
+ifconfig) commands which normally occurs prior to \fB\-\-up\fP script
+execution.
+.TP
+.B \fBifconfig_local\fP
+The local VPN endpoint IP address specified in the \fB\-\-ifconfig\fP
+option (first parameter). Set prior to OpenVPN calling the
+\fBifconfig\fP or \fBnetsh\fP (windows version of ifconfig)
+commands which normally occurs prior to \fB\-\-up\fP script execution.
+.TP
+.B \fBifconfig_remote\fP
+The remote VPN endpoint IP address specified in the \fB\-\-ifconfig\fP
+option (second parameter) when \fB\-\-dev tun\fP is used. Set prior to
+OpenVPN calling the \fBifconfig\fP or \fBnetsh\fP (windows version
+of ifconfig) commands which normally occurs prior to \fB\-\-up\fP script
+execution.
+.TP
+.B \fBifconfig_netmask\fP
+The subnet mask of the virtual ethernet segment that is specified as
+the second parameter to \fB\-\-ifconfig\fP when \fB\-\-dev tap\fP is being
+used. Set prior to OpenVPN calling the \fBifconfig\fP or
+\fBnetsh\fP (windows version of ifconfig) commands which normally
+occurs prior to \fB\-\-up\fP script execution.
+.TP
+.B \fBifconfig_pool_local_ip\fP
+The local virtual IP address for the TUN/TAP tunnel taken from an
+\fB\-\-ifconfig\-push\fP directive if specified, or otherwise from the
+ifconfig pool (controlled by the \fB\-\-ifconfig\-pool\fP config file
+directive). Only set for \fB\-\-dev tun\fP tunnels. This option is set on
+the server prior to execution of the \fB\-\-client\-connect\fP and
+\fB\-\-client\-disconnect\fP scripts.
+.TP
+.B \fBifconfig_pool_netmask\fP
+The virtual IP netmask for the TUN/TAP tunnel taken from an
+\fB\-\-ifconfig\-push\fP directive if specified, or otherwise from the
+ifconfig pool (controlled by the \fB\-\-ifconfig\-pool\fP config file
+directive). Only set for \fB\-\-dev tap\fP tunnels. This option is set on
+the server prior to execution of the \fB\-\-client\-connect\fP and
+\fB\-\-client\-disconnect\fP scripts.
+.TP
+.B \fBifconfig_pool_remote_ip\fP
+The remote virtual IP address for the TUN/TAP tunnel taken from an
+\fB\-\-ifconfig\-push\fP directive if specified, or otherwise from the
+ifconfig pool (controlled by the \fB\-\-ifconfig\-pool\fP config file
+directive). This option is set on the server prior to execution of the
+\fB\-\-client\-connect\fP and \fB\-\-client\-disconnect\fP scripts.
+.TP
+.B \fBlink_mtu\fP
+The maximum packet size (not including the IP header) of tunnel data in
+UDP tunnel transport mode. Set prior to \fB\-\-up\fP or \fB\-\-down\fP script
+execution.
+.TP
+.B \fBlocal\fP
+The \fB\-\-local\fP parameter. Set on program initiation and reset on
+SIGHUP.
+.TP
+.B \fBlocal_port\fP
+The local port number or name, specified by \fB\-\-port\fP or \fB\-\-lport\fP\&.
Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
.TP
-.B remote_{n}
-The
-.B \-\-remote
-parameter.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
+.B \fBpassword\fP
+The password provided by a connecting client. Set prior to
+\fB\-\-auth\-user\-pass\-verify\fP script execution only when the \fBvia\-env\fP
+modifier is specified, and deleted from the environment after the script
+returns.
.TP
-.B remote_port_{n}
-The remote port number, specified by
-.B \-\-port
-or
-.B \-\-rport.
-Set on program initiation and reset on SIGHUP.
-.\"*********************************************************
+.B \fBproto\fP
+The \fB\-\-proto\fP parameter. Set on program initiation and reset on
+SIGHUP.
.TP
-.B route_net_gateway
-The pre\-existing default IP gateway in the system routing
-table.
-Set prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
-.TP
-.B route_vpn_gateway
-The default gateway used by
-.B \-\-route
-options, as specified in either the
-.B \-\-route\-gateway
-option or the second parameter to
-.B \-\-ifconfig
-when
-.B \-\-dev tun
-is specified.
-Set prior to
-.B \-\-up
-script execution.
-.\"*********************************************************
+.B \fBremote_{n}\fP
+The \fB\-\-remote\fP parameter. Set on program initiation and reset on
+SIGHUP.
.TP
-.B route_{parm}_{n}
-A set of variables which define each route to be added, and
-are set prior to
-.B \-\-up
-script execution.
-
-.B parm
-will be one of "network", "netmask", "gateway", or "metric".
-
-.B n
-is the OpenVPN route number, starting from 1.
-
-If the network or gateway are resolvable DNS names,
-their IP address translations will be recorded rather
-than their names as denoted on the command line
-or configuration file.
-.\"*********************************************************
-.TP
-.B route_ipv6_{parm}_{n}
-A set of variables which define each IPv6 route to be added, and
-are set prior to
-.B \-\-up
+.B \fBremote_port_{n}\fP
+The remote port number, specified by \fB\-\-port\fP or \fB\-\-rport\fP\&. Set on
+program initiation and reset on SIGHUP.
+.TP
+.B \fBroute_net_gateway\fP
+The pre\-existing default IP gateway in the system routing table. Set
+prior to \fB\-\-up\fP script execution.
+.TP
+.B \fBroute_vpn_gateway\fP
+The default gateway used by \fB\-\-route\fP options, as specified in either
+the \fB\-\-route\-gateway\fP option or the second parameter to
+\fB\-\-ifconfig\fP when \fB\-\-dev tun\fP is specified. Set prior to \fB\-\-up\fP
script execution.
-
-.B parm
-will be one of "network" or "gateway" ("netmask" is contained as "/nnn"
-in the route_ipv6_network_{n}, unlike IPv4 where it is passed in a separate
-environment variable).
-
-.B n
-is the OpenVPN route number, starting from 1.
-
-If the network or gateway are resolvable DNS names,
-their IP address translations will be recorded rather
-than their names as denoted on the command line
-or configuration file.
-.\"*********************************************************
-.TP
-.B peer_cert
-Temporary file name containing the client certificate upon
-connection. Useful in conjunction with \-\-tls\-verify
-.\"*********************************************************
-.TP
-.B script_context
-Set to "init" or "restart" prior to up/down script execution.
-For more information, see
-documentation for
-.B \-\-up.
-.\"*********************************************************
-.TP
-.B script_type
-Prior to execution of any script, this variable is set to the type of
-script being run. It can be one of the following:
-.B up, down, ipchange, route\-up, tls\-verify, auth\-user\-pass\-verify,
-.B client\-connect, client\-disconnect,
-or
-.B learn\-address.
-Set prior to execution of any script.
-.\"*********************************************************
-.TP
-.B signal
-The reason for exit or restart. Can be one of
-.B sigusr1, sighup, sigterm, sigint, inactive
-(controlled by
-.B \-\-inactive
-option),
-.B ping\-exit
-(controlled by
-.B \-\-ping\-exit
-option),
-.B ping\-restart
-(controlled by
-.B \-\-ping\-restart
-option),
-.B connection\-reset
-(triggered on TCP connection reset),
-.B error,
-or
-.B unknown
-(unknown signal). This variable is set just prior to down script execution.
-.\"*********************************************************
-.TP
-.B time_ascii
-Client connection timestamp, formatted as a human\-readable
-time string.
-Set prior to execution of the
-.B \-\-client\-connect
-script.
-.\"*********************************************************
.TP
-.B time_duration
+.B \fBroute_{parm}_{n}\fP
+A set of variables which define each route to be added, and are set
+prior to \fB\-\-up\fP script execution.
+.sp
+\fBparm\fP will be one of \fBnetwork\fP, \fBnetmask"\fP,
+\fBgateway\fP, or \fBmetric\fP\&.
+.sp
+\fBn\fP is the OpenVPN route number, starting from 1.
+.sp
+If the network or gateway are resolvable DNS names, their IP address
+translations will be recorded rather than their names as denoted on the
+command line or configuration file.
+.TP
+.B \fBroute_ipv6_{parm}_{n}\fP
+A set of variables which define each IPv6 route to be added, and are
+set prior to \fB\-\-up\fP script execution.
+.sp
+\fBparm\fP will be one of \fBnetwork\fP, \fBgateway\fP or
+\fBmetric\fP\&. \fBroute_ipv6_network_{n}\fP contains \fBnetmask\fP
+as \fB/nnn\fP, unlike IPv4 where it is passed in a separate environment
+variable.
+.sp
+\fBn\fP is the OpenVPN route number, starting from 1.
+.sp
+If the network or gateway are resolvable DNS names, their IP address
+translations will be recorded rather than their names as denoted on the
+command line or configuration file.
+.TP
+.B \fBpeer_cert\fP
+Temporary file name containing the client certificate upon connection.
+Useful in conjunction with \fB\-\-tls\-verify\fP\&.
+.TP
+.B \fBscript_context\fP
+Set to "init" or "restart" prior to up/down script execution. For more
+information, see documentation for \fB\-\-up\fP\&.
+.TP
+.B \fBscript_type\fP
+Prior to execution of any script, this variable is set to the type of
+script being run. It can be one of the following: \fBup\fP,
+\fBdown\fP, \fBipchange\fP, \fBroute\-up\fP, \fBtls\-verify\fP,
+\fBauth\-user\-pass\-verify\fP, \fBclient\-connect\fP,
+\fBclient\-disconnect\fP or \fBlearn\-address\fP\&. Set prior to
+execution of any script.
+.TP
+.B \fBsignal\fP
+The reason for exit or restart. Can be one of \fBsigusr1\fP,
+\fBsighup\fP, \fBsigterm\fP, \fBsigint\fP, \fBinactive\fP
+(controlled by \fB\-\-inactive\fP option), \fBping\-exit\fP (controlled
+by \fB\-\-ping\-exit\fP option), \fBping\-restart\fP (controlled by
+\fB\-\-ping\-restart\fP option), \fBconnection\-reset\fP (triggered on TCP
+connection reset), \fBerror\fP or \fBunknown\fP (unknown signal).
+This variable is set just prior to down script execution.
+.TP
+.B \fBtime_ascii\fP
+Client connection timestamp, formatted as a human\-readable time string.
+Set prior to execution of the \fB\-\-client\-connect\fP script.
+.TP
+.B \fBtime_duration\fP
The duration (in seconds) of the client session which is now
-disconnecting.
-Set prior to execution of the
-.B \-\-client\-disconnect
+disconnecting. Set prior to execution of the \fB\-\-client\-disconnect\fP
script.
-.\"*********************************************************
.TP
-.B time_unix
-Client connection timestamp, formatted as a unix integer
-date/time value.
-Set prior to execution of the
-.B \-\-client\-connect
-script.
-.\"*********************************************************
-.TP
-.B tls_digest_{n} / tls_digest_sha256_{n}
-Contains the certificate SHA1 / SHA256 fingerprint, where
-.B n
-is the verification level. Only set for TLS connections. Set prior
-to execution of
-.B \-\-tls\-verify
-script.
-.\"*********************************************************
-.TP
-.B tls_id_{n}
-A series of certificate fields from the remote peer,
-where
-.B n
-is the verification level. Only set for TLS connections. Set prior
-to execution of
-.B \-\-tls\-verify
-script.
-.\"*********************************************************
-.TP
-.B tls_serial_{n}
-The serial number of the certificate from the remote peer,
-where
-.B n
-is the verification level. Only set for TLS connections. Set prior
-to execution of
-.B \-\-tls\-verify
-script. This is in the form of a decimal string like "933971680", which is
-suitable for doing serial\-based OCSP queries (with OpenSSL, do not
-prepend "0x" to the string) If something goes wrong while reading
-the value from the certificate it will be an empty string, so your
-code should check that.
-See the contrib/OCSP_check/OCSP_check.sh script for an example.
-.\"*********************************************************
-.TP
-.B tls_serial_hex_{n}
-Like
-.B tls_serial_{n}\fR,
-but in hex form (e.g. "12:34:56:78:9A").
-.\"*********************************************************
-.TP
-.B tun_mtu
-The MTU of the TUN/TAP device.
-Set prior to
-.B \-\-up
-or
-.B \-\-down
+.B \fBtime_unix\fP
+Client connection timestamp, formatted as a unix integer date/time
+value. Set prior to execution of the \fB\-\-client\-connect\fP script.
+.TP
+.B \fBtls_digest_{n}\fP / \fBtls_digest_sha256_{n}\fP
+Contains the certificate SHA1 / SHA256 fingerprint, where \fBn\fP is the
+verification level. Only set for TLS connections. Set prior to execution
+of \fB\-\-tls\-verify\fP script.
+.TP
+.B \fBtls_id_{n}\fP
+A series of certificate fields from the remote peer, where \fBn\fP is the
+verification level. Only set for TLS connections. Set prior to execution
+of \fB\-\-tls\-verify\fP script.
+.TP
+.B \fBtls_serial_{n}\fP
+The serial number of the certificate from the remote peer, where \fBn\fP
+is the verification level. Only set for TLS connections. Set prior to
+execution of \fB\-\-tls\-verify\fP script. This is in the form of a decimal
+string like "933971680", which is suitable for doing serial\-based OCSP
+queries (with OpenSSL, do not prepend "0x" to the string) If something
+goes wrong while reading the value from the certificate it will be an
+empty string, so your code should check that. See the
+\fBcontrib/OCSP_check/OCSP_check.sh\fP script for an example.
+.TP
+.B \fBtls_serial_hex_{n}\fP
+Like \fBtls_serial_{n}\fP, but in hex form (e.g.
+\fB12:34:56:78:9A\fP).
+.TP
+.B \fBtun_mtu\fP
+The MTU of the TUN/TAP device. Set prior to \fB\-\-up\fP or \fB\-\-down\fP
script execution.
-.\"*********************************************************
-.TP
-.B trusted_ip (or trusted_ip6)
-Actual IP address of connecting client or peer which has been authenticated.
-Set prior to execution of
-.B \-\-ipchange, \-\-client\-connect,
-and
-.B \-\-client\-disconnect
-scripts.
-If using ipv6 endpoints (udp6, tcp6),
-.B trusted_ip6
-will be set instead.
-.\"*********************************************************
-.TP
-.B trusted_port
-Actual port number of connecting client or peer which has been authenticated.
-Set prior to execution of
-.B \-\-ipchange, \-\-client\-connect,
-and
-.B \-\-client\-disconnect
-scripts.
-.\"*********************************************************
-.TP
-.B untrusted_ip (or untrusted_ip6)
-Actual IP address of connecting client or peer which has not been authenticated
-yet. Sometimes used to
-.B nmap
-the connecting host in a
-.B \-\-tls\-verify
-script to ensure it is firewalled properly.
-Set prior to execution of
-.B \-\-tls\-verify
-and
-.B \-\-auth\-user\-pass\-verify
-scripts.
-If using ipv6 endpoints (udp6, tcp6),
-.B untrusted_ip6
+.TP
+.B \fBtrusted_ip\fP / \fBtrusted_ip6\fP)
+Actual IP address of connecting client or peer which has been
+authenticated. Set prior to execution of \fB\-\-ipchange\fP,
+\fB\-\-client\-connect\fP and \fB\-\-client\-disconnect\fP scripts. If using ipv6
+endpoints (udp6, tcp6), \fBtrusted_ip6\fP will be set instead.
+.TP
+.B \fBtrusted_port\fP
+Actual port number of connecting client or peer which has been
+authenticated. Set prior to execution of \fB\-\-ipchange\fP,
+\fB\-\-client\-connect\fP and \fB\-\-client\-disconnect\fP scripts.
+.TP
+.B \fBuntrusted_ip\fP / \fBuntrusted_ip6\fP
+Actual IP address of connecting client or peer which has not been
+authenticated yet. Sometimes used to \fInmap\fP the connecting host in a
+\fB\-\-tls\-verify\fP script to ensure it is firewalled properly. Set prior
+to execution of \fB\-\-tls\-verify\fP and \fB\-\-auth\-user\-pass\-verify\fP
+scripts. If using ipv6 endpoints (udp6, tcp6), \fBuntrusted_ip6\fP
will be set instead.
-.\"*********************************************************
-.TP
-.B untrusted_port
-Actual port number of connecting client or peer which has not been authenticated
-yet.
-Set prior to execution of
-.B \-\-tls\-verify
-and
-.B \-\-auth\-user\-pass\-verify
-scripts.
-.\"*********************************************************
-.TP
-.B username
-The username provided by a connecting client.
-Set prior to
-.B \-\-auth\-user\-pass\-verify
-script execution only when the
-.B via\-env
-modifier is specified.
-.\"*********************************************************
-.TP
-.B X509_{n}_{subject_field}
-An X509 subject field from the remote peer certificate,
-where
-.B n
-is the verification level. Only set for TLS connections. Set prior
-to execution of
-.B \-\-tls\-verify
-script. This variable is similar to
-.B tls_id_{n}
-except the component X509 subject fields are broken out, and
-no string remapping occurs on these field values (except for remapping
-of control characters to "_").
-For example, the following variables would be set on the
-OpenVPN server using the sample client certificate
-in sample\-keys (client.crt).
-Note that the verification level is 0 for the client certificate
-and 1 for the CA certificate.
-
+.TP
+.B \fBuntrusted_port\fP
+Actual port number of connecting client or peer which has not been
+authenticated yet. Set prior to execution of \fB\-\-tls\-verify\fP and
+\fB\-\-auth\-user\-pass\-verify\fP scripts.
+.TP
+.B \fBusername\fP
+The username provided by a connecting client. Set prior to
+\fB\-\-auth\-user\-pass\-verify\fP script execution only when the
+\fBvia\-env\fP modifier is specified.
+.TP
+.B \fBX509_{n}_{subject_field}\fP
+An X509 subject field from the remote peer certificate, where \fBn\fP is
+the verification level. Only set for TLS connections. Set prior to
+execution of \fB\-\-tls\-verify\fP script. This variable is similar to
+\fBtls_id_{n}\fP except the component X509 subject fields are broken
+out, and no string remapping occurs on these field values (except for
+remapping of control characters to "\fB_\fP"). For example, the
+following variables would be set on the OpenVPN server using the sample
+client certificate in sample\-keys (client.crt). Note that the
+verification level is 0 for the client certificate and 1 for the CA
+certificate.
+.INDENT 7.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
+.ft C
X509_0_emailAddress=me@myhost.mydomain
X509_0_CN=Test\-Client
X509_0_O=OpenVPN\-TEST
@@ -6889,455 +5851,858 @@ X509_1_O=OpenVPN\-TEST
X509_1_L=BISHKEK
X509_1_ST=NA
X509_1_C=KG
-.in -4
-.ft
+.ft P
.fi
-.\"*********************************************************
-.SH INLINE FILE SUPPORT
-OpenVPN allows including files in the main configuration for the
-.B \-\-ca, \-\-cert, \-\-dh, \-\-extra\-certs, \-\-key, \-\-pkcs12, \-\-secret,
-.B \-\-crl\-verify, \-\-http\-proxy\-user\-pass, \-\-tls\-auth
-and
-.B \-\-tls\-crypt
-options.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SS Management Interface Options
+.sp
+OpenVPN provides a feature rich socket based management interface for both
+server and client mode operations.
+.INDENT 0.0
+.TP
+.BI \-\-management \ args
+Enable a management server on a \fBsocket\-name\fP Unix socket on those
+platforms supporting it, or on a designated TCP port.
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+management socket\-name unix #
+management socket\-name unix pw\-file # (recommended)
+management IP port # (INSECURE)
+management IP port pw\-file #
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBpw\-file\fP, if specified, is a password file where the password must
+be on first line. Instead of a filename it can use the keyword stdin
+which will prompt the user for a password to use when OpenVPN is
+starting.
+.sp
+For unix sockets, the default behaviour is to create a unix domain
+socket that may be connected to by any process. Use the
+\fB\-\-management\-client\-user\fP and \fB\-\-management\-client\-group\fP
+directives to restrict access.
+.sp
+The management interface provides a special mode where the TCP
+management link can operate over the tunnel itself. To enable this mode,
+set IP to \fBtunnel\fP\&. Tunnel mode will cause the management interface to
+listen for a TCP connection on the local VPN address of the TUN/TAP
+interface.
+.sp
+\fB*BEWARE*\fP of enabling the management interface over TCP. In these cases
+you should \fIALWAYS\fP make use of \fBpw\-file\fP to password protect the
+management interface. Any user who can connect to this TCP \fBIP:port\fP
+will be able to manage and control (and interfere with) the OpenVPN
+process. It is also strongly recommended to set IP to 127.0.0.1
+(localhost) to restrict accessibility of the management server to local
+clients.
+.sp
+While the management port is designed for programmatic control of
+OpenVPN by other applications, it is possible to telnet to the port,
+using a telnet client in "raw" mode. Once connected, type \fBhelp\fP
+for a list of commands.
+.sp
+For detailed documentation on the management interface, see the
+\fImanagement\-notes.txt\fP file in the management folder of the OpenVPN
+source distribution.
+.TP
+.B \-\-management\-client
+Management interface will connect as a TCP/unix domain client to
+\fBIP:port\fP specified by \fB\-\-management\fP rather than listen as a TCP
+server or on a unix domain socket.
+.sp
+If the client connection fails to connect or is disconnected, a SIGTERM
+signal will be generated causing OpenVPN to quit.
+.TP
+.B \-\-management\-client\-auth
+Gives management interface client the responsibility to authenticate
+clients after their client certificate has been verified. See
+\fBmanagement\-notes.txt\fP in OpenVPN distribution for detailed notes.
+.TP
+.BI \-\-management\-client\-group \ g
+When the management interface is listening on a unix domain socket, only
+allow connections from group \fBg\fP\&.
+.TP
+.B \-\-management\-client\-pf
+Management interface clients must specify a packet filter file for each
+connecting client. See \fBmanagement\-notes.txt\fP in OpenVPN
+distribution for detailed notes.
+.TP
+.BI \-\-management\-client\-user \ u
+When the management interface is listening on a unix domain socket, only
+allow connections from user \fBu\fP\&.
+.TP
+.BI \-\-management\-external\-cert \ certificate\-hint
+Allows usage for external certificate instead of \fB\-\-cert\fP option
+(client\-only). \fBcertificate\-hint\fP is an arbitrary string which is
+passed to a management interface client as an argument of
+\fINEED\-CERTIFICATE\fP notification. Requires \fB\-\-management\-external\-key\fP\&.
+.TP
+.BI \-\-management\-external\-key \ args
+Allows usage for external private key file instead of \fB\-\-key\fP option
+(client\-only).
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+management\-external\-key
+management\-external\-key nopadding
+management\-external\-key pkcs1
+management\-external\-key nopadding pkcs1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The optional parameters \fBnopadding\fP and \fBpkcs1\fP signal
+support for different padding algorithms. See
+\fBdoc/mangement\-notes.txt\fP for a complete description of this
+feature.
+.TP
+.B \-\-management\-forget\-disconnect
+Make OpenVPN forget passwords when management session disconnects.
+.sp
+This directive does not affect the \fB\-\-http\-proxy\fP username/password.
+It is always cached.
+.TP
+.B \-\-management\-hold
+Start OpenVPN in a hibernating state, until a client of the management
+interface explicitly starts it with the \fBhold release\fP command.
+.TP
+.BI \-\-management\-log\-cache \ n
+Cache the most recent \fBn\fP lines of log file history for usage by the
+management channel.
+.TP
+.B \-\-management\-query\-passwords
+Query management channel for private key password and
+\fB\-\-auth\-user\-pass\fP username/password. Only query the management
+channel for inputs which ordinarily would have been queried from the
+console.
+.TP
+.B \-\-management\-query\-proxy
+Query management channel for proxy server information for a specific
+\fB\-\-remote\fP (client\-only).
+.TP
+.B \-\-management\-query\-remote
+Allow management interface to override \fB\-\-remote\fP directives
+(client\-only).
+.TP
+.B \-\-management\-signal
+Send SIGUSR1 signal to OpenVPN if management session disconnects. This
+is useful when you wish to disconnect an OpenVPN session on user logoff.
+For \fB\-\-management\-client\fP this option is not needed since a disconnect
+will always generate a \fBSIGTERM\fP\&.
+.TP
+.B \-\-management\-up\-down
+Report tunnel up/down events to management interface.
+.UNINDENT
+.SS Plug\-in Interface Options
+.sp
+OpenVPN can be extended by loading external plug\-in modules at runtime. These
+plug\-ins must be prebuilt and adhere to the OpenVPN Plug\-In API.
+.INDENT 0.0
+.TP
+.BI \-\-plugin \ args
+Loads an OpenVPN plug\-in module.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+plugin module\-name
+plugin module\-name "arguments"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The \fBmodule\-name\fP needs to be the first
+argument, indicating the plug\-in to load. The second argument is an
+optional init string which will be passed directly to the plug\-in.
+If the init consists of multiple arguments it must be enclosed in
+double\-quotes ("). Multiple plugin modules may be loaded into one
+OpenVPN process.
+.sp
+The \fBmodule\-name\fP argument can be just a filename or a filename
+with a relative or absolute path. The format of the filename and path
+defines if the plug\-in will be loaded from a default plug\-in directory
+or outside this directory.
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-plugin path Effective directory used
+===================== =============================
+ myplug.so DEFAULT_DIR/myplug.so
+ subdir/myplug.so DEFAULT_DIR/subdir/myplug.so
+ ./subdir/myplug.so CWD/subdir/myplug.so
+ /usr/lib/my/plug.so /usr/lib/my/plug.so
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBDEFAULT_DIR\fP is replaced by the default plug\-in directory, which is
+configured at the build time of OpenVPN. \fBCWD\fP is the current directory
+where OpenVPN was started or the directory OpenVPN have switched into
+via the \fB\-\-cd\fP option before the \fB\-\-plugin\fP option.
+.sp
+For more information and examples on how to build OpenVPN plug\-in
+modules, see the README file in the \fBplugin\fP folder of the OpenVPN
+source distribution.
+.sp
+If you are using an RPM install of OpenVPN, see
+\fB/usr/share/openvpn/plugin\fP\&. The documentation is in \fBdoc\fP and
+the actual plugin modules are in \fBlib\fP\&.
+.sp
+Multiple plugin modules can be cascaded, and modules can be used in
+tandem with scripts. The modules will be called by OpenVPN in the order
+that they are declared in the config file. If both a plugin and script
+are configured for the same callback, the script will be called last. If
+the return code of the module/script controls an authentication function
+(such as tls\-verify, auth\-user\-pass\-verify, or client\-connect), then
+every module and script must return success (\fB0\fP) in order for the
+connection to be authenticated.
+.UNINDENT
+.SS Windows\-Specific Options
+.INDENT 0.0
+.TP
+.BI \-\-allow\-nonadmin \ TAP\-adapter
+(Standalone) Set \fBTAP\-adapter\fP to allow access from non\-administrative
+accounts. If \fBTAP\-adapter\fP is omitted, all TAP adapters on the system
+will be configured to allow non\-admin access. The non\-admin access
+setting will only persist for the length of time that the TAP\-Win32
+device object and driver remain loaded, and will need to be re\-enabled
+after a reboot, or if the driver is unloaded and reloaded. This
+directive can only be used by an administrator.
+.TP
+.B \-\-block\-outside\-dns
+Block DNS servers on other network adapters to prevent DNS leaks. This
+option prevents any application from accessing TCP or UDP port 53 except
+one inside the tunnel. It uses Windows Filtering Platform (WFP) and
+works on Windows Vista or later.
+.sp
+This option is considered unknown on non\-Windows platforms and
+unsupported on Windows XP, resulting in fatal error. You may want to use
+\fB\-\-setenv opt\fP or \fB\-\-ignore\-unknown\-option\fP (not suitable for
+Windows XP) to ignore said error. Note that pushing unknown options from
+server does not trigger fatal errors.
+.TP
+.BI \-\-cryptoapicert \ select\-string
+\fI(Windows/OpenSSL Only)\fP Load the certificate and private key from the
+Windows Certificate System Store.
+.sp
+Use this option instead of \fB\-\-cert\fP and \fB\-\-key\fP\&.
+.sp
+This makes it possible to use any smart card, supported by Windows, but
+also any kind of certificate, residing in the Cert Store, where you have
+access to the private key. This option has been tested with a couple of
+different smart cards (GemSAFE, Cryptoflex, and Swedish Post Office eID)
+on the client side, and also an imported PKCS12 software certificate on
+the server side.
+.sp
+To select a certificate, based on a substring search in the
+certificate\(aqs subject:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cryptoapicert "SUBJ:Peter Runestig"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+To select a certificate, based on certificate\(aqs thumbprint:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cryptoapicert "THUMB:f6 49 24 41 01 b4 ..."
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The thumbprint hex string can easily be copy\-and\-pasted from the Windows
+Certificate Store GUI.
+.TP
+.B \-\-dhcp\-release
+Ask Windows to release the TAP adapter lease on shutdown. This option
+has no effect now, as it is enabled by default starting with
+OpenVPN 2.4.1.
+.TP
+.B \-\-dhcp\-renew
+Ask Windows to renew the TAP adapter lease on startup. This option is
+normally unnecessary, as Windows automatically triggers a DHCP
+renegotiation on the TAP adapter when it comes up, however if you set
+the TAP\-Win32 adapter Media Status property to "Always Connected", you
+may need this flag.
+.TP
+.BI \-\-ip\-win32 \ method
+When using \fB\-\-ifconfig\fP on Windows, set the TAP\-Win32 adapter IP
+address and netmask using \fBmethod\fP\&. Don\(aqt use this option unless you
+are also using \fB\-\-ifconfig\fP\&.
+.INDENT 7.0
+.TP
+.B \fBmanual\fP
+Don\(aqt set the IP address or netmask automatically. Instead
+output a message to the console telling the user to configure the
+adapter manually and indicating the IP/netmask which OpenVPN
+expects the adapter to be set to.
+.TP
+.B \fBdynamic [offset] [lease\-time]\fP
+Automatically set the IP address and netmask by replying to DHCP
+query messages generated by the kernel. This mode is probably the
+"cleanest" solution for setting the TCP/IP properties since it
+uses the well\-known DHCP protocol. There are, however, two
+prerequisites for using this mode:
+.INDENT 7.0
+.IP 1. 3
+The TCP/IP properties for the TAP\-Win32 adapter must be set
+to "Obtain an IP address automatically", and
+.IP 2. 3
+OpenVPN needs to claim an IP address in the subnet for use
+as the virtual DHCP server address.
+.UNINDENT
+.sp
+By default in \fB\-\-dev tap\fP mode, OpenVPN will take the normally
+unused first address in the subnet. For example, if your subnet is
+\fB192.168.4.0 netmask 255.255.255.0\fP, then OpenVPN will take
+the IP address \fB192.168.4.0\fP to use as the virtual DHCP
+server address. In \fB\-\-dev tun\fP mode, OpenVPN will cause the DHCP
+server to masquerade as if it were coming from the remote endpoint.
+.sp
+The optional offset parameter is an integer which is > \fB\-256\fP
+and < \fB256\fP and which defaults to \-1. If offset is positive,
+the DHCP server will masquerade as the IP address at network
+address + offset. If offset is negative, the DHCP server will
+masquerade as the IP address at broadcast address + offset.
+.sp
+The Windows \fBipconfig /all\fP command can be used to show what
+Windows thinks the DHCP server address is. OpenVPN will "claim"
+this address, so make sure to use a free address. Having said that,
+different OpenVPN instantiations, including different ends of
+the same connection, can share the same virtual DHCP server
+address.
+.sp
+The \fBlease\-time\fP parameter controls the lease time of the DHCP
+assignment given to the TAP\-Win32 adapter, and is denoted in
+seconds. Normally a very long lease time is preferred because it
+prevents routes involving the TAP\-Win32 adapter from being lost
+when the system goes to sleep. The default lease time is one year.
+.TP
+.B \fBnetsh\fP
+Automatically set the IP address and netmask using the Windows
+command\-line "netsh" command. This method appears to work correctly
+on Windows XP but not Windows 2000.
+.TP
+.B \fBipapi\fP
+Automatically set the IP address and netmask using the Windows IP
+Helper API. This approach does not have ideal semantics, though
+testing has indicated that it works okay in practice. If you use
+this option, it is best to leave the TCP/IP properties for the
+TAP\-Win32 adapter in their default state, i.e. "Obtain an IP
+address automatically."
+.TP
+.B \fBadaptive\fP (Default)
+Try \fBdynamic\fP method initially and fail over to \fBnetsh\fP
+if the DHCP negotiation with the TAP\-Win32 adapter does not succeed
+in 20 seconds. Such failures have been known to occur when certain
+third\-party firewall packages installed on the client machine block
+the DHCP negotiation used by the TAP\-Win32 adapter. Note that if
+the \fBnetsh\fP failover occurs, the TAP\-Win32 adapter TCP/IP
+properties will be reset from DHCP to static, and this will cause
+future OpenVPN startups using the \fBadaptive\fP mode to use
+\fBnetsh\fP immediately, rather than trying \fBdynamic\fP first.
+.sp
+To "unstick" the \fBadaptive\fP mode from using \fBnetsh\fP,
+run OpenVPN at least once using the \fBdynamic\fP mode to restore
+the TAP\-Win32 adapter TCP/IP properties to a DHCP configuration.
+.UNINDENT
+.TP
+.B \-\-pause\-exit
+Put up a "press any key to continue" message on the console prior to
+OpenVPN program exit. This option is automatically used by the Windows
+explorer when OpenVPN is run on a configuration file using the
+right\-click explorer menu.
+.TP
+.B \-\-register\-dns
+Run \fBipconfig /flushdns\fP and \fBipconfig /registerdns\fP on
+connection initiation. This is known to kick Windows into recognizing
+pushed DNS servers.
+.TP
+.BI \-\-route\-method \ m
+Which method \fBm\fP to use for adding routes on Windows?
+.INDENT 7.0
+.TP
+.B \fBadaptive\fP (default)
+Try IP helper API first. If that fails, fall back to the route.exe
+shell command.
+.TP
+.B \fBipapi\fP
+Use IP helper API.
+.TP
+.B \fBexe\fP
+Call the route.exe shell command.
+.UNINDENT
+.TP
+.BI \-\-service \ args
+Should be used when OpenVPN is being automatically executed by another
+program in such a context that no interaction with the user via display
+or keyboard is possible.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+service exit\-event [0|1]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+In general, end\-users should never need to explicitly use this option,
+as it is automatically added by the OpenVPN service wrapper when a given
+OpenVPN configuration is being run as a service.
+.sp
+\fBexit\-event\fP is the name of a Windows global event object, and OpenVPN
+will continuously monitor the state of this event object and exit when
+it becomes signaled.
+.sp
+The second parameter indicates the initial state of \fBexit\-event\fP and
+normally defaults to 0.
+.sp
+Multiple OpenVPN processes can be simultaneously executed with the same
+\fBexit\-event\fP parameter. In any case, the controlling process can
+signal \fBexit\-event\fP, causing all such OpenVPN processes to exit.
+.sp
+When executing an OpenVPN process using the \fB\-\-service\fP directive,
+OpenVPN will probably not have a console window to output status/error
+messages, therefore it is useful to use \fB\-\-log\fP or \fB\-\-log\-append\fP to
+write these messages to a file.
+.TP
+.B \-\-show\-adapters
+(Standalone) Show available TAP\-Win32 adapters which can be selected
+using the \fB\-\-dev\-node\fP option. On non\-Windows systems, the
+\fBifconfig\fP(8) command provides similar functionality.
+.TP
+.B \-\-show\-net
+(Standalone) Show OpenVPN\(aqs view of the system routing table and network
+adapter list.
+.TP
+.B \-\-show\-net\-up
+Output OpenVPN\(aqs view of the system routing table and network adapter
+list to the syslog or log file after the TUN/TAP adapter has been
+brought up and any routes have been added.
+.TP
+.B \-\-show\-valid\-subnets
+(Standalone) Show valid subnets for \fB\-\-dev tun\fP emulation. Since the
+TAP\-Win32 driver exports an ethernet interface to Windows, and since TUN
+devices are point\-to\-point in nature, it is necessary for the TAP\-Win32
+driver to impose certain constraints on TUN endpoint address selection.
+.sp
+Namely, the point\-to\-point endpoints used in TUN device emulation must
+be the middle two addresses of a /30 subnet (netmask 255.255.255.252).
+.TP
+.BI \-\-tap\-sleep \ n
+Cause OpenVPN to sleep for \fBn\fP seconds immediately after the TAP\-Win32
+adapter state is set to "connected".
+.sp
+This option is intended to be used to troubleshoot problems with the
+\fB\-\-ifconfig\fP and \fB\-\-ip\-win32\fP options, and is used to give the
+TAP\-Win32 adapter time to come up before Windows IP Helper API
+operations are applied to it.
+.TP
+.BI \-\-win\-sys \ path
+Set the Windows system directory pathname to use when looking for system
+executables such as \fBroute.exe\fP and \fBnetsh.exe\fP\&. By default, if this
+directive is not specified, OpenVPN will use the SystemRoot environment
+variable.
+.sp
+This option has changed behaviour since OpenVPN 2.3. Earlier you had to
+define \fB\-\-win\-sys env\fP to use the SystemRoot environment variable,
+otherwise it defaulted to \fBC:\e\eWINDOWS\fP\&. It is not needed to use
+the \fBenv\fP keyword any more, and it will just be ignored. A warning is
+logged when this is found in the configuration file.
+.TP
+.BI \-\-windows\-driver \ drv
+Specifies which tun driver to use. Values are \fBtap\-windows6\fP
+(default) and \fBwintun\fP\&. This is a Windows\-only option.
+\fBwintun\fP" requires \fB\-\-dev tun\fP and the OpenVPN process to run
+elevated, or be invoked using the Interactive Service.
+.UNINDENT
+.SS Standalone Debug Options
+.INDENT 0.0
+.TP
+.BI \-\-show\-gateway \ args
+(Standalone) Show current IPv4 and IPv6 default gateway and interface
+towards the gateway (if the protocol in question is enabled).
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+\-\-show\-gateway
+\-\-show\-gateway IPv6\-target
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+For IPv6 this queries the route towards ::/128, or the specified IPv6
+target address if passed as argument.
+For IPv4 on Linux, Windows, MacOS and BSD it looks for a 0.0.0.0/0 route.
+If there are more specific routes, the result will not always be matching
+the route of the IPv4 packets to the VPN gateway.
+.UNINDENT
+.SS Advanced Expert Options
+.sp
+These are options only required when special tweaking is needed, often
+used when debugging or testing out special usage scenarios.
+.INDENT 0.0
+.TP
+.BI \-\-hash\-size \ args
+Set the size of the real address hash table to \fBr\fP and the virtual
+address table to \fBv\fP\&.
+.sp
+Valid syntax:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+hash\-size r v
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+By default, both tables are sized at 256 buckets.
+.TP
+.BI \-\-bcast\-buffers \ n
+Allocate \fBn\fP buffers for broadcast datagrams (default \fB256\fP).
+.TP
+.B \-\-persist\-local\-ip
+Preserve initially resolved local IP address and port number across
+\fBSIGUSR1\fP or \fB\-\-ping\-restart\fP restarts.
+.TP
+.B \-\-persist\-remote\-ip
+Preserve most recently authenticated remote IP address and port number
+across \fBSIGUSR1\fP or \fB\-\-ping\-restart\fP restarts.
+.TP
+.BI \-\-prng \ args
+\fI(Advanced)\fP Change the PRNG (Pseudo\-random number generator) parameters
+.sp
+Valid syntaxes:
+.INDENT 7.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+prng alg
+prng alg nsl
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Changes the PRNG to use digest algorithm \fBalg\fP (default \fBsha1\fP),
+and set \fBnsl\fP (default \fB16\fP) to the size in bytes of the nonce
+secret length (between 16 and 64).
+.sp
+Set \fBalg\fP to \fBnone\fP to disable the PRNG and use the OpenSSL
+RAND_bytes function instead for all of OpenVPN\(aqs pseudo\-random number
+needs.
+.TP
+.BI \-\-rcvbuf \ size
+Set the TCP/UDP socket receive buffer size. Defaults to operating system
+default.
+.TP
+.BI \-\-shaper \ n
+Limit bandwidth of outgoing tunnel data to \fBn\fP bytes per second on the
+TCP/UDP port. Note that this will only work if mode is set to
+\fBp2p\fP\&. If you want to limit the bandwidth in both directions, use
+this option on both peers.
+.sp
+OpenVPN uses the following algorithm to implement traffic shaping: Given
+a shaper rate of \fBn\fP bytes per second, after a datagram write of \fBb\fP
+bytes is queued on the TCP/UDP port, wait a minimum of \fB(b / n)\fP
+seconds before queuing the next write.
+.sp
+It should be noted that OpenVPN supports multiple tunnels between the
+same two peers, allowing you to construct full\-speed and reduced
+bandwidth tunnels at the same time, routing low\-priority data such as
+off\-site backups over the reduced bandwidth tunnel, and other data over
+the full\-speed tunnel.
+.sp
+Also note that for low bandwidth tunnels (under 1000 bytes per second),
+you should probably use lower MTU values as well (see above), otherwise
+the packet latency will grow so large as to trigger timeouts in the TLS
+layer and TCP connections running over the tunnel.
+.sp
+OpenVPN allows \fBn\fP to be between 100 bytes/sec and 100 Mbytes/sec.
+.TP
+.BI \-\-sndbuf \ size
+Set the TCP/UDP socket send buffer size. Defaults to operating system
+default.
+.TP
+.BI \-\-tcp\-queue\-limit \ n
+Maximum number of output packets queued before TCP (default \fB64\fP).
+.sp
+When OpenVPN is tunneling data from a TUN/TAP device to a remote client
+over a TCP connection, it is possible that the TUN/TAP device might
+produce data at a faster rate than the TCP connection can support. When
+the number of output packets queued before sending to the TCP socket
+reaches this limit for a given client connection, OpenVPN will start to
+drop outgoing packets directed at this client.
+.TP
+.BI \-\-txqueuelen \ n
+\fI(Linux only)\fP Set the TX queue length on the TUN/TAP interface.
+Currently defaults to operating system default.
+.UNINDENT
+.SH UNSUPPORTED OPTIONS
+.sp
+Options listed in this section have been removed from OpenVPN and are no
+longer supported
+.INDENT 0.0
+.TP
+.B \-\-client\-cert\-not\-required
+Removed in OpenVPN 2.5. This should be replaxed with
+\fB\-\-verify\-client\-cert none\fP\&.
+.TP
+.B \-\-ifconfig\-pool\-linear
+Removed in OpenVPN 2.5. This should be replaced with \fB\-\-topology p2p\fP\&.
+.TP
+.B \-\-key\-method
+Removed in OpenVPN 2.5. This option should not be used, as using the old
+\fBkey\-method\fP weakens the VPN tunnel security. The old \fBkey\-method\fP
+was also only needed when the remote side was older than OpenVPN 2.0.
+.TP
+.B \-\-no\-iv
+Removed in OpenVPN 2.5. This option should not be used as it weakens the
+VPN tunnel security. This has been a NOOP option since OpenVPN 2.4.
+.TP
+.B \-\-no\-replay
+Removed in OpenVPN 2.5. This option should not be used as it weakens the
+VPN tunnel security.
+.TP
+.B \-\-ns\-cert\-type
+Removed in OpenVPN 2.5. The \fBnsCertType\fP field is no longer supported
+in recent SSL/TLS libraries. If your certificates does not include \fIkey
+usage\fP and \fIextended key usage\fP fields, they must be upgraded and the
+\fB\-\-remote\-cert\-tls\fP option should be used instead.
+.UNINDENT
+.SH CONNECTION PROFILES
+.sp
+Client configuration files may contain multiple remote servers which
+it will attempt to connect against. But there are some configuration
+options which are related to specific \fB\-\-remote\fP options. For these
+use cases, connection profiles are the solution.
+.sp
+By enacpulating the \fB\-\-remote\fP option and related options within
+\fB<connection>\fP and \fB</connection>\fP, these options are handled as a
+group.
+.sp
+An OpenVPN client will try each connection profile sequentially until it
+achieves a successful connection.
+.sp
+\fB\-\-remote\-random\fP can be used to initially "scramble" the connection
+list.
+.sp
+Here is an example of connection profile usage:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+client
+dev tun
-Each inline file started by the line
-.B <option>
-and ended by the line
-.B </option>
+<connection>
+remote 198.19.34.56 1194 udp
+</connection>
-Here is an example of an inline file usage
+<connection>
+remote 198.19.34.56 443 tcp
+</connection>
+<connection>
+remote 198.19.34.56 443 tcp
+http\-proxy 192.168.0.8 8080
+</connection>
+
+<connection>
+remote 198.19.36.99 443 tcp
+http\-proxy 192.168.0.8 8080
+</connection>
+
+persist\-key
+persist\-tun
+pkcs12 client.p12
+remote\-cert\-tls server
+verb 3
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+First we try to connect to a server at 198.19.34.56:1194 using UDP. If
+that fails, we then try to connect to 198.19.34.56:443 using TCP. If
+that also fails, then try connecting through an HTTP proxy at
+192.168.0.8:8080 to 198.19.34.56:443 using TCP. Finally, try to connect
+through the same proxy to a server at 198.19.36.99:443 using TCP.
+.sp
+The following OpenVPN options may be used inside of a \fB<connection>\fP
+block:
+.sp
+\fBbind\fP, \fBconnect\-retry\fP, \fBconnect\-retry\-max\fP, \fBconnect\-timeout\fP,
+\fBexplicit\-exit\-notify\fP, \fBfloat\fP, \fBfragment\fP, \fBhttp\-proxy\fP,
+\fBhttp\-proxy\-option\fP, \fBkey\-direction\fP, \fBlink\-mtu\fP, \fBlocal\fP,
+\fBlport\fP, \fBmssfix\fP, \fBmtu\-disc\fP, \fBnobind\fP, \fBport\fP, \fBproto\fP,
+\fBremote\fP, \fBrport\fP, \fBsocks\-proxy\fP, \fBtls\-auth\fP, \fBtls\-crypt\fP,
+\fBtun\-mtu and\fP, \fBtun\-mtu\-extra\fP\&.
+.sp
+A defaulting mechanism exists for specifying options to apply to all
+\fB<connection>\fP profiles. If any of the above options (with the
+exception of \fBremote\fP ) appear outside of a \fB<connection>\fP block,
+but in a configuration file which has one or more \fB<connection>\fP
+blocks, the option setting will be used as a default for
+\fB<connection>\fP blocks which follow it in the configuration file.
+.sp
+For example, suppose the \fBnobind\fP option were placed in the sample
+configuration file above, near the top of the file, before the first
+\fB<connection>\fP block. The effect would be as if \fBnobind\fP were
+declared in all \fB<connection>\fP blocks below it.
+.SH INLINE FILE SUPPORT
+.sp
+OpenVPN allows including files in the main configuration for the \fB\-\-ca\fP,
+\fB\-\-cert\fP, \fB\-\-dh\fP, \fB\-\-extra\-certs\fP, \fB\-\-key\fP, \fB\-\-pkcs12\fP,
+\fB\-\-secret\fP, \fB\-\-crl\-verify\fP, \fB\-\-http\-proxy\-user\-pass\fP, \fB\-\-tls\-auth\fP,
+\fB\-\-auth\-gen\-token\-secret\fP, \fB\-\-tls\-crypt\fP and \fB\-\-tls\-crypt\-v2\fP
+options.
+.sp
+Each inline file started by the line \fB<option>\fP and ended by the line
+\fB</option>\fP
+.sp
+Here is an example of an inline file usage
+.INDENT 0.0
+.INDENT 3.5
+.sp
.nf
-.ft 3
-.in +4
+.ft C
<cert>
\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-
[...]
\-\-\-\-\-END CERTIFICATE\-\-\-\-\-
</cert>
-.in -4
-.ft
+.ft P
.fi
-
-When using the inline file feature with
-.B \-\-pkcs12
-the inline file has to be base64 encoded. Encoding of a .p12 file into base64 can be done for example with OpenSSL by running
-.B openssl base64 \-in input.p12
-
+.UNINDENT
+.UNINDENT
+.sp
+When using the inline file feature with \fB\-\-pkcs12\fP the inline file has
+to be base64 encoded. Encoding of a .p12 file into base64 can be done
+for example with OpenSSL by running \fBopenssl base64 \-in input.p12\fP
.SH SIGNALS
-.TP
-.B SIGHUP
-Cause OpenVPN to close all TUN/TAP and
-network connections,
-restart, re\-read the configuration file (if any),
-and reopen TUN/TAP and network connections.
-.\"*********************************************************
-.TP
-.B SIGUSR1
-Like
-.B SIGHUP,
-except don't re\-read configuration file, and possibly don't close and reopen TUN/TAP
-device, re\-read key files, preserve local IP address/port, or preserve most recently authenticated
-remote IP address/port based on
-.B \-\-persist\-tun, \-\-persist\-key, \-\-persist\-local\-ip,
-and
-.B \-\-persist\-remote\-ip
-options respectively (see above).
-
-This signal may also be internally generated by a timeout condition, governed
-by the
-.B \-\-ping\-restart
-option.
-
-This signal, when combined with
-.B \-\-persist\-remote\-ip,
-may be
-sent when the underlying parameters of the host's network interface change
+.INDENT 0.0
+.TP
+.B \fBSIGHUP\fP
+Cause OpenVPN to close all TUN/TAP and network connections, restart,
+re\-read the configuration file (if any), and reopen TUN/TAP and network
+connections.
+.TP
+.B \fBSIGUSR1\fP
+Like \fBSIGHUP\(ga\fP, except don\(aqt re\-read configuration file, and
+possibly don\(aqt close and reopen TUN/TAP device, re\-read key files,
+preserve local IP address/port, or preserve most recently authenticated
+remote IP address/port based on \fB\-\-persist\-tun\fP, \fB\-\-persist\-key\fP,
+\fB\-\-persist\-local\-ip\fP and \fB\-\-persist\-remote\-ip\fP options respectively
+(see above).
+.sp
+This signal may also be internally generated by a timeout condition,
+governed by the \fB\-\-ping\-restart\fP option.
+.sp
+This signal, when combined with \fB\-\-persist\-remote\-ip\fP, may be sent
+when the underlying parameters of the host\(aqs network interface change
such as when the host is a DHCP client and is assigned a new IP address.
-See
-.B \-\-ipchange
-above for more information.
-.\"*********************************************************
-.TP
-.B SIGUSR2
-Causes OpenVPN to display its current statistics (to the syslog
-file if
-.B \-\-daemon
-is used, or stdout otherwise).
-.\"*********************************************************
-.TP
-.B SIGINT, SIGTERM
+See \fB\-\-ipchange\fP for more information.
+.TP
+.B \fBSIGUSR2\fP
+Causes OpenVPN to display its current statistics (to the syslog file if
+\fB\-\-daemon\fP is used, or stdout otherwise).
+.TP
+.B \fBSIGINT\fP, \fBSIGTERM\fP
Causes OpenVPN to exit gracefully.
-.\"*********************************************************
-.SH TUN/TAP DRIVER SETUP
-If you are running Linux 2.4.7 or higher, you probably have the TUN/TAP driver
-already installed. If so, there are still a few things you need to do:
-
-Make device:
-.B mknod /dev/net/tun c 10 200
-
-Load driver:
-.B modprobe tun
-.\"*********************************************************
-.SH EXAMPLES
-Prior to running these examples, you should have OpenVPN installed on two
-machines with network connectivity between them. If you have not
-yet installed OpenVPN, consult the INSTALL file included in the OpenVPN
-distribution.
-.\"*********************************************************
-.SS TUN/TAP Setup:
-If you are using Linux 2.4 or higher,
-make the tun device node and load the tun module:
-.IP
-.B mknod /dev/net/tun c 10 200
-.LP
-.IP
-.B modprobe tun
-.LP
-If you installed from RPM, the
-.B mknod
-step may be omitted, because the RPM install does that for you.
-
-Only Linux 2.4 and newer are supported.
-
-For other platforms, consult the INSTALL file at
-.I http://openvpn.net/install.html
-for more information.
-.\"*********************************************************
-.SS Firewall Setup:
-If firewalls exist between
-the two machines, they should be set to forward UDP port 1194
-in both directions. If you do not have control over the firewalls
-between the two machines, you may still be able to use OpenVPN by adding
-.B \-\-ping 15
-to each of the
-.B openvpn
-commands used below in the examples (this will cause each peer to send out
-a UDP ping to its remote peer once every 15 seconds which will cause many
-stateful firewalls to forward packets in both directions
-without an explicit firewall rule).
-
-If you are using a Linux iptables\-based firewall, you may need to enter
-the following command to allow incoming packets on the TUN device:
-.IP
-.B iptables \-A INPUT \-i tun+ \-j ACCEPT
-.LP
-See the firewalls section below for more information on configuring firewalls
-for use with OpenVPN.
-.\"*********************************************************
-.SS VPN Address Setup:
-For purposes
-of our example, our two machines will be called
-.B bob.example.com
-and
-.B alice.example.com.
-If you are constructing a VPN over the internet, then replace
-.B bob.example.com
-and
-.B alice.example.com
-with the internet hostname or IP address that each machine will use
-to contact the other over the internet.
-
-Now we will choose the tunnel endpoints. Tunnel endpoints are
-private IP addresses that only have meaning in the context of
-the VPN. Each machine will use the tunnel endpoint of the other
-machine to access it over the VPN. In our example,
-the tunnel endpoint for bob.example.com
-will be 10.4.0.1 and for alice.example.com, 10.4.0.2.
-
-Once the VPN is established, you have essentially
-created a secure alternate path between the two hosts
-which is addressed by using the tunnel endpoints. You can
-control which network
-traffic passes between the hosts
-(a) over the VPN or (b) independently of the VPN, by choosing whether to use
-(a) the VPN endpoint address or (b) the public internet address,
-to access the remote host. For example if you are on bob.example.com and you wish to connect to alice.example.com
-via
-.B ssh
-without using the VPN (since
-.B ssh
-has its own built\-in security) you would use the command
-.B ssh alice.example.com.
-However in the same scenario, you could also use the command
-.B telnet 10.4.0.2
-to create a telnet session with alice.example.com over the VPN, that would
-use the VPN to secure the session rather than
-.B ssh.
-
-You can use any address you wish for the
-tunnel endpoints
-but make sure that they are private addresses
-(such as those that begin with 10 or 192.168) and that they are
-not part of any existing subnet on the networks of
-either peer, unless you are bridging. If you use an address that is part of
-your local subnet for either of the tunnel endpoints,
-you will get a weird feedback loop.
-.\"*********************************************************
-.SS Example 1: A simple tunnel without security
-.LP
-On bob:
-.IP
-.B openvpn \-\-remote alice.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 9
-.LP
-On alice:
-.IP
-.B openvpn \-\-remote bob.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 9
-.LP
-Now verify the tunnel is working by pinging across the tunnel.
-.LP
-On bob:
-.IP
-.B ping 10.4.0.2
-.LP
-On alice:
-.IP
-.B ping 10.4.0.1
-.LP
-The
-.B \-\-verb 9
-option will produce verbose output, similar to the
-.BR tcpdump (8)
-program. Omit the
-.B \-\-verb 9
-option to have OpenVPN run quietly.
-.\"*********************************************************
-.SS Example 2: A tunnel with static\-key security (i.e. using a pre\-shared secret)
-First build a static key on bob.
-.IP
-.B openvpn \-\-genkey \-\-secret key
-.LP
-This command will build a random key file called
-.B key
-(in ascii format).
-Now copy
-.B key
-to alice over a secure medium such as by
-using the
-.BR scp (1)
-program.
-.LP
-On bob:
-.IP
-.B openvpn \-\-remote alice.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 5 \-\-secret key
-.LP
-On alice:
-.IP
-.B openvpn \-\-remote bob.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 5 \-\-secret key
-.LP
-Now verify the tunnel is working by pinging across the tunnel.
-.LP
-On bob:
-.IP
-.B ping 10.4.0.2
-.LP
-On alice:
-.IP
-.B ping 10.4.0.1
-.\"*********************************************************
-.SS Example 3: A tunnel with full TLS\-based security
-For this test, we will designate
-.B bob
-as the TLS client and
-.B alice
-as the TLS server.
-.I Note that client or server designation only has meaning for the TLS subsystem. It has no bearing on OpenVPN's peer\-to\-peer, UDP\-based communication model.
-
-First, build a separate certificate/key pair
-for both bob and alice (see above where
-.B \-\-cert
-is discussed for more info). Then construct
-Diffie Hellman parameters (see above where
-.B \-\-dh
-is discussed for more info). You can also use the
-included test files client.crt, client.key,
-server.crt, server.key and ca.crt.
-The .crt files are certificates/public\-keys, the .key
-files are private keys, and ca.crt is a certification
-authority who has signed both
-client.crt and server.crt. For Diffie Hellman
-parameters you can use the included file dh1024.pem.
-.I Note that all client, server, and certificate authority certificates and keys included in the OpenVPN distribution are totally insecure and should be used for testing only.
-.LP
-On bob:
-.IP
-.B openvpn \-\-remote alice.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-tls\-client \-\-ca ca.crt \-\-cert client.crt \-\-key client.key \-\-reneg\-sec 60 \-\-verb 5
-.LP
-On alice:
-.IP
-.B openvpn \-\-remote bob.example.com \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-tls\-server \-\-dh dh1024.pem \-\-ca ca.crt \-\-cert server.crt \-\-key server.key \-\-reneg\-sec 60 \-\-verb 5
-.LP
-Now verify the tunnel is working by pinging across the tunnel.
-.LP
-On bob:
-.IP
-.B ping 10.4.0.2
-.LP
-On alice:
-.IP
-.B ping 10.4.0.1
-.LP
-Notice the
-.B \-\-reneg\-sec 60
-option we used above. That tells OpenVPN to renegotiate
-the data channel keys every minute.
-Since we used
-.B \-\-verb 5
-above, you will see status information on each new key negotiation.
-
-For production operations, a key renegotiation interval of 60 seconds
-is probably too frequent. Omit the
-.B \-\-reneg\-sec 60
-option to use OpenVPN's default key renegotiation interval of one hour.
-.\"*********************************************************
-.SS Routing:
-Assuming you can ping across the tunnel,
-the next step is to route a real subnet over
-the secure tunnel. Suppose that bob and alice have two network
-interfaces each, one connected
-to the internet, and the other to a private
-network. Our goal is to securely connect
-both private networks. We will assume that bob's private subnet
-is 10.0.0.0/24 and alice's is 10.0.1.0/24.
-.LP
-First, ensure that IP forwarding is enabled on both peers.
-On Linux, enable routing:
-.IP
-.B echo 1 > /proc/sys/net/ipv4/ip_forward
-.LP
-and enable TUN packet forwarding through the firewall:
-.IP
-.B iptables \-A FORWARD \-i tun+ \-j ACCEPT
-.LP
-On bob:
-.IP
-.B route add \-net 10.0.1.0 netmask 255.255.255.0 gw 10.4.0.2
-.LP
-On alice:
-.IP
-.B route add \-net 10.0.0.0 netmask 255.255.255.0 gw 10.4.0.1
-.LP
-Now any machine on the 10.0.0.0/24 subnet can
-access any machine on the 10.0.1.0/24 subnet
-over the secure tunnel (or vice versa).
-
-In a production environment, you could put the route command(s)
-in a script and execute with the
-.B \-\-up
-option.
-.\"*********************************************************
-.SH FIREWALLS
-OpenVPN's usage of a single UDP port makes it fairly firewall\-friendly.
-You should add an entry to your firewall rules to allow incoming OpenVPN
-packets. On Linux 2.4+:
-.IP
-.B iptables \-A INPUT \-p udp \-s 1.2.3.4 \-\-dport 1194 \-j ACCEPT
-.LP
-This will allow incoming packets on UDP port 1194 (OpenVPN's default UDP port)
-from an OpenVPN peer at 1.2.3.4.
-
-If you are using HMAC\-based packet authentication (the default in any of
-OpenVPN's secure modes), having the firewall filter on source
-address can be considered optional, since HMAC packet authentication
-is a much more secure method of verifying the authenticity of
-a packet source. In that case:
-.IP
-.B iptables \-A INPUT \-p udp \-\-dport 1194 \-j ACCEPT
-.LP
-would be adequate and would not render the host inflexible with
-respect to its peer having a dynamic IP address.
-
-OpenVPN also works well on stateful firewalls. In some cases, you may
-not need to add any static rules to the firewall list if you are
-using a stateful firewall that knows how to track UDP connections.
-If you specify
-.B \-\-ping n,
-OpenVPN will be guaranteed
-to send a packet to its peer at least once every
-.B n
-seconds. If
-.B n
-is less than the stateful firewall connection timeout, you can
-maintain an OpenVPN connection indefinitely without explicit
-firewall rules.
-
-You should also add firewall rules to allow incoming IP traffic on
-TUN or TAP devices such as:
-.IP
-.B iptables \-A INPUT \-i tun+ \-j ACCEPT
-.LP
-to allow input packets from tun devices,
-.IP
-.B iptables \-A FORWARD \-i tun+ \-j ACCEPT
-.LP
-to allow input packets from tun devices to be forwarded to
-other hosts on the local network,
-.IP
-.B iptables \-A INPUT \-i tap+ \-j ACCEPT
-.LP
-to allow input packets from tap devices, and
-.IP
-.B iptables \-A FORWARD \-i tap+ \-j ACCEPT
-.LP
-to allow input packets from tap devices to be forwarded to
-other hosts on the local network.
-
-These rules are secure if you use packet authentication,
-since no incoming packets will arrive on a TUN or TAP
-virtual device
-unless they first pass an HMAC authentication test.
-.\"*********************************************************
+.UNINDENT
.SH FAQ
-.I http://openvpn.net/faq.html
-.\"*********************************************************
+.sp
+\fI\%https://community.openvpn.net/openvpn/wiki/FAQ\fP
.SH HOWTO
-For a more comprehensive guide to setting up OpenVPN
-in a production setting, see the OpenVPN HOWTO at
-.I http://openvpn.net/howto.html
-.\"*********************************************************
+.sp
+For a more comprehensive guide to setting up OpenVPN in a production
+setting, see the OpenVPN HOWTO at
+\fI\%https://openvpn.net/community\-resources/how\-to/\fP
.SH PROTOCOL
-For a description of OpenVPN's underlying protocol,
-see
-.I http://openvpn.net/security.html
-.\"*********************************************************
+.sp
+For a description of OpenVPN\(aqs underlying protocol, see
+\fI\%https://openvpn.net/community\-resources/openvpn\-protocol/\fP
.SH WEB
-OpenVPN's web site is at
-.I http://openvpn.net/
-
-Go here to download the latest version of OpenVPN, subscribe
-to the mailing lists, read the mailing list
-archives, or browse the SVN repository.
-.\"*********************************************************
+.sp
+OpenVPN\(aqs web site is at \fI\%https://openvpn.net/\fP
+.sp
+Go here to download the latest version of OpenVPN, subscribe to the
+mailing lists, read the mailing list archives, or browse the SVN
+repository.
.SH BUGS
-Report all bugs to the OpenVPN team <info@openvpn.net>.
-.\"*********************************************************
-.SH "SEE ALSO"
-.BR dhcpcd (8),
-.BR ifconfig (8),
-.BR openssl (1),
-.BR route (8),
-.BR scp (1)
-.BR ssh (1)
-.\"*********************************************************
-.SH NOTES
-.LP
-This product includes software developed by the
-OpenSSL Project (
-.I http://www.openssl.org/
-)
-
+.sp
+Report all bugs to the OpenVPN team \fI\%info@openvpn.net\fP
+.SH SEE ALSO
+.sp
+\fBopenvpn\-examples\fP(5),
+\fBdhcpcd\fP(8),
+\fBifconfig\fP(8),
+\fBopenssl\fP(1),
+\fBroute\fP(8),
+\fBscp\fP(1)
+\fBssh\fP(1)
+.SH NOTES
+.sp
+This product includes software developed by the OpenSSL Project
+(\fI\%https://www.openssl.org/\fP)
+.sp
For more information on the TLS protocol, see
-.I http://www.ietf.org/rfc/rfc2246.txt
-
+\fI\%http://www.ietf.org/rfc/rfc2246.txt\fP
+.sp
For more information on the LZO real\-time compression library see
-.I http://www.oberhumer.com/opensource/lzo/
-.\"*********************************************************
+\fI\%https://www.oberhumer.com/opensource/lzo/\fP
.SH COPYRIGHT
-Copyright (C) 2002\-2018 OpenVPN Inc This program is free software;
-you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
-.\"*********************************************************
+.sp
+Copyright (C) 2002\-2020 OpenVPN Inc This program is free software; you
+can redistribute it and/or modify it under the terms of the GNU General
+Public License version 2 as published by the Free Software Foundation.
.SH AUTHORS
-James Yonan <jim@yonan.net>
+.sp
+James Yonan \fI\%james@openvpn.net\fP
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/openvpn.8.html b/doc/openvpn.8.html
new file mode 100644
index 0000000..1c0c65e
--- /dev/null
+++ b/doc/openvpn.8.html
@@ -0,0 +1,5899 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.16: http://docutils.sourceforge.net/" />
+<title>openvpn</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.subscript {
+ vertical-align: sub;
+ font-size: smaller }
+
+.superscript {
+ vertical-align: super;
+ font-size: smaller }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+ overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+ clear: left ;
+ float: left ;
+ margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+ clear: right ;
+ float: right ;
+ margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.align-center {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left }
+
+.align-center {
+ clear: both ;
+ text-align: center }
+
+.align-right {
+ text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+ text-align: inherit }
+
+/* div.align-center * { */
+/* text-align: left } */
+
+.align-top {
+ vertical-align: top }
+
+.align-middle {
+ vertical-align: middle }
+
+.align-bottom {
+ vertical-align: bottom }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+pre.code .ln { color: grey; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+ border: 0px;
+ border-top: 2px solid;
+ border-bottom: 2px solid;
+ border-collapse: collapse;
+}
+table.docutils.booktabs * {
+ border: 0px;
+}
+table.docutils.booktabs th {
+ border-bottom: thin solid;
+ text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="openvpn">
+<h1 class="title">openvpn</h1>
+<h2 class="subtitle" id="secure-ip-tunnel-daemon">Secure IP tunnel daemon</h2>
+<table class="docinfo" frame="void" rules="none">
+<col class="docinfo-name" />
+<col class="docinfo-content" />
+<tbody valign="top">
+<tr class="manual-section field"><th class="docinfo-name">Manual section:</th><td class="field-body">8</td>
+</tr>
+<tr class="manual-group field"><th class="docinfo-name">Manual group:</th><td class="field-body">System Manager's Manual</td>
+</tr>
+</tbody>
+</table>
+<div class="section" id="synopsis">
+<h1>SYNOPSIS</h1>
+<div class="line-block">
+<div class="line"><tt class="docutils literal">openvpn</tt> [ options ... ]</div>
+<div class="line"><tt class="docutils literal">openvpn</tt> <tt class="docutils literal"><span class="pre">--help</span></tt></div>
+</div>
+</div>
+<div class="section" id="introduction">
+<h1>INTRODUCTION</h1>
+<p>OpenVPN is an open source VPN daemon by James Yonan. Because OpenVPN
+tries to be a universal VPN tool offering a great deal of flexibility,
+there are a lot of options on this manual page. If you're new to
+OpenVPN, you might want to skip ahead to the examples section where you
+will see how to construct simple VPNs on the command line without even
+needing a configuration file.</p>
+<p>Also note that there's more documentation and examples on the OpenVPN
+web site: <a class="reference external" href="https://openvpn.net/">https://openvpn.net/</a></p>
+<p>And if you would like to see a shorter version of this manual, see the
+openvpn usage message which can be obtained by running <strong>openvpn</strong>
+without any parameters.</p>
+</div>
+<div class="section" id="description">
+<h1>DESCRIPTION</h1>
+<p>OpenVPN is a robust and highly flexible VPN daemon. OpenVPN supports
+SSL/TLS security, ethernet bridging, TCP or UDP tunnel transport through
+proxies or NAT, support for dynamic IP addresses and DHCP, scalability
+to hundreds or thousands of users, and portability to most major OS
+platforms.</p>
+<p>OpenVPN is tightly bound to the OpenSSL library, and derives much of its
+crypto capabilities from it.</p>
+<p>OpenVPN supports conventional encryption using a pre-shared secret key
+<strong>(Static Key mode)</strong> or public key security <strong>(SSL/TLS mode)</strong> using
+client &amp; server certificates. OpenVPN also supports non-encrypted
+TCP/UDP tunnels.</p>
+<p>OpenVPN is designed to work with the <strong>TUN/TAP</strong> virtual networking
+interface that exists on most platforms.</p>
+<p>Overall, OpenVPN aims to offer many of the key features of IPSec but
+with a relatively lightweight footprint.</p>
+</div>
+<div class="section" id="options">
+<h1>OPTIONS</h1>
+<p>OpenVPN allows any option to be placed either on the command line or in
+a configuration file. Though all command line options are preceded by a
+double-leading-dash (&quot;--&quot;), this prefix can be removed when an option is
+placed in a configuration file.</p>
+<div class="section" id="generic-options">
+<h2>Generic Options</h2>
+<p>This section covers generic options which are accessible regardless of
+which mode OpenVPN is configured as.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--help</span></kbd></td>
+<td>Show options.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--auth-nocache</span></kbd></td>
+<td><p class="first">Don't cache <tt class="docutils literal"><span class="pre">--askpass</span></tt> or <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> username/passwords in
+virtual memory.</p>
+<p>If specified, this directive will cause OpenVPN to immediately forget
+username/password inputs after they are used. As a result, when OpenVPN
+needs a username/password, it will prompt for input from stdin, which
+may be multiple times during the duration of an OpenVPN session.</p>
+<p>When using <tt class="docutils literal"><span class="pre">--auth-nocache</span></tt> in combination with a user/password file
+and <tt class="docutils literal"><span class="pre">--chroot</span></tt> or <tt class="docutils literal"><span class="pre">--daemon</span></tt>, make sure to use an absolute path.</p>
+<p class="last">This directive does not affect the <tt class="docutils literal"><span class="pre">--http-proxy</span></tt> username/password.
+It is always cached.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--cd <var>dir</var></span></kbd></td>
+<td><p class="first">Change directory to <tt class="docutils literal">dir</tt> prior to reading any files such as
+configuration files, key files, scripts, etc. <tt class="docutils literal">dir</tt> should be an
+absolute path, with a leading &quot;/&quot;, and without any references to the
+current directory such as <code>.</code> or <code>..</code>.</p>
+<p class="last">This option is useful when you are running OpenVPN in <tt class="docutils literal"><span class="pre">--daemon</span></tt> mode,
+and you want to consolidate all of your OpenVPN control files in one
+location.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--chroot <var>dir</var></span></kbd></td>
+<td><p class="first">Chroot to <tt class="docutils literal">dir</tt> after initialization. <tt class="docutils literal"><span class="pre">--chroot</span></tt> essentially
+redefines <tt class="docutils literal">dir</tt> as being the top level directory tree (/). OpenVPN
+will therefore be unable to access any files outside this tree. This can
+be desirable from a security standpoint.</p>
+<p>Since the chroot operation is delayed until after initialization, most
+OpenVPN options that reference files will operate in a pre-chroot
+context.</p>
+<p>In many cases, the <tt class="docutils literal">dir</tt> parameter can point to an empty directory,
+however complications can result when scripts or restarts are executed
+after the chroot operation.</p>
+<p class="last">Note: The SSL library will probably need /dev/urandom to be available
+inside the chroot directory <tt class="docutils literal">dir</tt>. This is because SSL libraries
+occasionally need to collect fresh random. Newer linux kernels and some
+BSDs implement a getrandom() or getentropy() syscall that removes the
+need for /dev/urandom to be available.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--config <var>file</var></span></kbd></td>
+<td><p class="first">Load additional config options from <tt class="docutils literal">file</tt> where each line corresponds
+to one command line option, but with the leading '--' removed.</p>
+<p>If <tt class="docutils literal"><span class="pre">--config</span> file</tt> is the only option to the openvpn command, the
+<tt class="docutils literal"><span class="pre">--config</span></tt> can be removed, and the command can be given as <tt class="docutils literal">openvpn
+file</tt></p>
+<p>Note that configuration files can be nested to a reasonable depth.</p>
+<p>Double quotation or single quotation characters (&quot;&quot;, '') can be used to
+enclose single parameters containing whitespace, and &quot;#&quot; or &quot;;&quot;
+characters in the first column can be used to denote comments.</p>
+<p>Note that OpenVPN 2.0 and higher performs backslash-based shell escaping
+for characters not in single quotations, so the following mappings
+should be observed:</p>
+<pre class="literal-block">
+\\ Maps to a single backslash character (\).
+\&quot; Pass a literal doublequote character (&quot;), don't
+ interpret it as enclosing a parameter.
+\[SPACE] Pass a literal space or tab character, don't
+ interpret it as a parameter delimiter.
+</pre>
+<p>For example on Windows, use double backslashes to represent pathnames:</p>
+<pre class="literal-block">
+secret &quot;c:\\OpenVPN\\secret.key&quot;
+</pre>
+<p>For examples of configuration files, see
+<a class="reference external" href="https://openvpn.net/community-resources/how-to/">https://openvpn.net/community-resources/how-to/</a></p>
+<p>Here is an example configuration file:</p>
+<pre class="last literal-block">
+#
+# Sample OpenVPN configuration file for
+# using a pre-shared static key.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+dev tun
+
+# Our remote peer
+remote mypeer.mydomain
+
+# 10.1.0.1 is our local VPN endpoint
+# 10.1.0.2 is our remote VPN endpoint
+ifconfig 10.1.0.1 10.1.0.2
+
+# Our pre-shared static key
+secret static.key
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--daemon <var>progname</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Become a daemon after all initialization functions are completed. This
+option will cause all message and error output to be sent to the syslog
+file (such as <code>/var/log/messages</code>), except for the output of
+scripts and ifconfig commands, which will go to <code>/dev/null</code> unless
+otherwise redirected. The syslog redirection occurs immediately at the
+point that <tt class="docutils literal"><span class="pre">--daemon</span></tt> is parsed on the command line even though the
+daemonization point occurs later. If one of the <tt class="docutils literal"><span class="pre">--log</span></tt> options is
+present, it will supersede syslog redirection.</p>
+<p>The optional <tt class="docutils literal">progname</tt> parameter will cause OpenVPN to report its
+program name to the system logger as <tt class="docutils literal">progname</tt>. This can be useful in
+linking OpenVPN messages in the syslog file with specific tunnels. When
+unspecified, <tt class="docutils literal">progname</tt> defaults to &quot;openvpn&quot;.</p>
+<p>When OpenVPN is run with the <tt class="docutils literal"><span class="pre">--daemon</span></tt> option, it will try to delay
+daemonization until the majority of initialization functions which are
+capable of generating fatal errors are complete. This means that
+initialization scripts can test the return status of the openvpn command
+for a fairly reliable indication of whether the command has correctly
+initialized and entered the packet forwarding event loop.</p>
+<p>In OpenVPN, the vast majority of errors which occur after initialization
+are non-fatal.</p>
+<p>Note: as soon as OpenVPN has daemonized, it can not ask for usernames,
+passwords, or key pass phrases anymore. This has certain consequences,
+namely that using a password-protected private key will fail unless the
+<tt class="docutils literal"><span class="pre">--askpass</span></tt> option is used to tell OpenVPN to ask for the pass phrase
+(this requirement is new in v2.3.7, and is a consequence of calling
+daemon() before initializing the crypto layer).</p>
+<p class="last">Further, using <tt class="docutils literal"><span class="pre">--daemon</span></tt> together with <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> (entered
+on console) and <tt class="docutils literal"><span class="pre">--auth-nocache</span></tt> will fail as soon as key
+renegotiation (and reauthentication) occurs.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--disable-occ</span></kbd></td>
+<td><p class="first">Don't output a warning message if option inconsistencies are detected
+between peers. An example of an option inconsistency would be where one
+peer uses <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> while the other peer uses <tt class="docutils literal"><span class="pre">--dev</span> tap</tt>.</p>
+<p class="last">Use of this option is discouraged, but is provided as a temporary fix in
+situations where a recent version of OpenVPN must connect to an old
+version.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--engine <var>engine-name</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Enable OpenSSL hardware-based crypto engine functionality.</p>
+<p class="last">If <tt class="docutils literal"><span class="pre">engine-name</span></tt> is specified, use a specific crypto engine. Use the
+<tt class="docutils literal"><span class="pre">--show-engines</span></tt> standalone option to list the crypto engines which
+are supported by OpenSSL.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--fast-io</span></kbd></td>
+<td><p class="first">(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding a call to
+poll/epoll/select prior to the write operation. The purpose of such a
+call would normally be to block until the device or socket is ready to
+accept the write. Such blocking is unnecessary on some platforms which
+don't support write blocking on UDP sockets or TUN/TAP devices. In such
+cases, one can optimize the event loop by avoiding the poll/epoll/select
+call, improving CPU efficiency by 5% to 10%.</p>
+<p class="last">This option can only be used on non-Windows systems, when <tt class="docutils literal"><span class="pre">--proto</span>
+udp</tt> is specified, and when <tt class="docutils literal"><span class="pre">--shaper</span></tt> is NOT specified.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--group <var>group</var></span></kbd></td>
+<td>Similar to the <tt class="docutils literal"><span class="pre">--user</span></tt> option, this option changes the group ID of
+the OpenVPN process to <tt class="docutils literal">group</tt> after initialization.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ignore-unknown-option <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Valid syntax:</p>
+<pre class="literal-block">
+ignore-unknown-options opt1 opt2 opt3 ... optN
+</pre>
+<p>When one of options <tt class="docutils literal">opt1 ... optN</tt> is encountered in the configuration
+file the configuration file parsing does not fail if this OpenVPN version
+does not support the option. Multiple <tt class="docutils literal"><span class="pre">--ignore-unknown-option</span></tt> options
+can be given to support a larger number of options to ignore.</p>
+<p>This option should be used with caution, as there are good security
+reasons for having OpenVPN fail if it detects problems in a config file.
+Having said that, there are valid reasons for wanting new software
+features to gracefully degrade when encountered by older software
+versions.</p>
+<p class="last"><tt class="docutils literal"><span class="pre">--ignore-unknown-option</span></tt> is available since OpenVPN 2.3.3.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--iproute <var>cmd</var></span></kbd></td>
+<td>Set alternate command to execute instead of default <tt class="docutils literal">iproute2</tt> command.
+May be used in order to execute OpenVPN in unprivileged environment.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--keying-material-exporter <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Save Exported Keying Material [RFC5705] of len bytes (must be between 16
+and 4095 bytes) using <tt class="docutils literal">label</tt> in environment
+(<code>exported_keying_material</code>) for use by plugins in
+<code>OPENVPN_PLUGIN_TLS_FINAL</code> callback.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+keying-material-exporter label len
+</pre>
+<p class="last">Note that exporter <tt class="docutils literal">labels</tt> have the potential to collide with existing
+PRF labels. In order to prevent this, labels <em>MUST</em> begin with
+<code>EXPORTER</code>.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mlock</span></kbd></td>
+<td><p class="first">Disable paging by calling the POSIX mlockall function. Requires that
+OpenVPN be initially run as root (though OpenVPN can subsequently
+downgrade its UID using the <tt class="docutils literal"><span class="pre">--user</span></tt> option).</p>
+<p>Using this option ensures that key material and tunnel data are never
+written to disk due to virtual memory paging operations which occur
+under most modern operating systems. It ensures that even if an attacker
+was able to crack the box running OpenVPN, he would not be able to scan
+the system swap file to recover previously used ephemeral keys, which
+are used for a period of time governed by the <tt class="docutils literal"><span class="pre">--reneg</span></tt> options (see
+below), then are discarded.</p>
+<p>The downside of using <tt class="docutils literal"><span class="pre">--mlock</span></tt> is that it will reduce the amount of
+physical memory available to other applications.</p>
+<p class="last">The limit on how much memory can be locked and how that limit
+is enforced are OS-dependent. On Linux the default limit that an
+unprivileged process may lock (RLIMIT_MEMLOCK) is low, and if
+privileges are dropped later, future memory allocations will very
+likely fail. The limit can be increased using ulimit or systemd
+directives depending on how OpenVPN is started.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--nice <var>n</var></span></kbd></td>
+<td>Change process priority after initialization (<tt class="docutils literal">n</tt> greater than 0 is
+lower priority, <tt class="docutils literal">n</tt> less than zero is higher priority).</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--persist-key</span></kbd></td>
+<td><p class="first">Don't re-read key files across <code>SIGUSR1</code> or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>.</p>
+<p>This option can be combined with <tt class="docutils literal"><span class="pre">--user</span> nobody</tt> to allow restarts
+triggered by the <code>SIGUSR1</code> signal. Normally if you drop root
+privileges in OpenVPN, the daemon cannot be restarted since it will now
+be unable to re-read protected key files.</p>
+<p class="last">This option solves the problem by persisting keys across <code>SIGUSR1</code>
+resets, so they don't need to be re-read.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remap-usr1 <var>signal</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Control whether internally or externally generated <code>SIGUSR1</code> signals
+are remapped to <code>SIGHUP</code> (restart without persisting state) or
+SIGTERM (exit).</p>
+<p class="last"><tt class="docutils literal">signal</tt> can be set to <code>SIGHUP</code> or <code>SIGTERM</code>. By default,
+no remapping occurs.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--script-security <var>level</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">This directive offers policy-level control over OpenVPN's usage of
+external programs and scripts. Lower <tt class="docutils literal">level</tt> values are more
+restrictive, higher values are more permissive. Settings for <tt class="docutils literal">level</tt>:</p>
+<dl class="docutils">
+<dt><code>0</code></dt>
+<dd>Strictly no calling of external programs.</dd>
+<dt><code>1</code></dt>
+<dd>(Default) Only call built-in executables such as ifconfig,
+ip, route, or netsh.</dd>
+<dt><code>2</code></dt>
+<dd>Allow calling of built-in executables and user-defined
+scripts.</dd>
+<dt><code>3</code></dt>
+<dd>Allow passwords to be passed to scripts via environmental
+variables (potentially unsafe).</dd>
+</dl>
+<p>OpenVPN releases before v2.3 also supported a <tt class="docutils literal">method</tt> flag which
+indicated how OpenVPN should call external commands and scripts. This
+could be either <code>execve</code> or <code>system</code>. As of OpenVPN 2.3, this
+flag is no longer accepted. In most *nix environments the execve()
+approach has been used without any issues.</p>
+<p>Some directives such as <tt class="docutils literal"><span class="pre">--up</span></tt> allow options to be passed to the
+external script. In these cases make sure the script name does not
+contain any spaces or the configuration parser will choke because it
+can't determine where the script name ends and script options start.</p>
+<p>To run scripts in Windows in earlier OpenVPN versions you needed to
+either add a full path to the script interpreter which can parse the
+script or use the <tt class="docutils literal">system</tt> flag to run these scripts. As of OpenVPN
+2.3 it is now a strict requirement to have full path to the script
+interpreter when running non-executables files. This is not needed for
+executable files, such as .exe, .com, .bat or .cmd files. For example,
+if you have a Visual Basic script, you must use this syntax now:</p>
+<pre class="literal-block">
+--up 'C:\\Windows\\System32\\wscript.exe C:\\Program\ Files\\OpenVPN\\config\\my-up-script.vbs'
+</pre>
+<p>Please note the single quote marks and the escaping of the backslashes
+(\) and the space character.</p>
+<p class="last">The reason the support for the <code>system</code> flag was removed is due to
+the security implications with shell expansions when executing scripts
+via the <code>system()</code> call.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--setcon <var>context</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Apply SELinux <tt class="docutils literal">context</tt> after initialization. This essentially
+provides the ability to restrict OpenVPN's rights to only network I/O
+operations, thanks to SELinux. This goes further than <tt class="docutils literal"><span class="pre">--user</span></tt> and
+<tt class="docutils literal"><span class="pre">--chroot</span></tt> in that those two, while being great security features,
+unfortunately do not protect against privilege escalation by
+exploitation of a vulnerable system call. You can of course combine all
+three, but please note that since setcon requires access to /proc you
+will have to provide it inside the chroot directory (e.g. with mount
+--bind).</p>
+<p>Since the setcon operation is delayed until after initialization,
+OpenVPN can be restricted to just network-related system calls, whereas
+by applying the context before startup (such as the OpenVPN one provided
+in the SELinux Reference Policies) you will have to allow many things
+required only during initialization.</p>
+<p class="last">Like with chroot, complications can result when scripts or restarts are
+executed after the setcon operation, which is why you should really
+consider using the <tt class="docutils literal"><span class="pre">--persist-key</span></tt> and <tt class="docutils literal"><span class="pre">--persist-tun</span></tt> options.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--status <var>args</var></span></kbd></td>
+<td><p class="first">Write operational status to <tt class="docutils literal">file</tt> every <tt class="docutils literal">n</tt> seconds.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+status file
+status file n
+</pre>
+<p>Status can also be written to the syslog by sending a <code>SIGUSR2</code>
+signal.</p>
+<p>With multi-client capability enabled on a server, the status file
+includes a list of clients and a routing table. The output format can be
+controlled by the <tt class="docutils literal"><span class="pre">--status-version</span></tt> option in that case.</p>
+<p class="last">For clients or instances running in point-to-point mode, it will contain
+the traffic statistics.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--status-version <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set the status file format version number to <tt class="docutils literal">n</tt>.</p>
+<p>This only affects the status file on servers with multi-client
+capability enabled. Valid status version values:</p>
+<dl class="last docutils">
+<dt><code>1</code></dt>
+<dd>Traditional format (default). The client list contains the
+following fields comma-separated: Common Name, Real Address, Bytes
+Received, Bytes Sent, Connected Since.</dd>
+<dt><code>2</code></dt>
+<dd>A more reliable format for external processing. Compared to
+version <code>1</code>, the client list contains some additional fields:
+Virtual Address, Virtual IPv6 Address, Username, Client ID, Peer ID,
+Data Channel Cipher. Future versions may extend the number of fields.</dd>
+<dt><code>3</code></dt>
+<dd>Identical to <code>2</code>, but fields are tab-separated.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--test-crypto</span></kbd></td>
+<td><p class="first">Do a self-test of OpenVPN's crypto options by encrypting and decrypting
+test packets using the data channel encryption options specified above.
+This option does not require a peer to function, and therefore can be
+specified without <tt class="docutils literal"><span class="pre">--dev</span></tt> or <tt class="docutils literal"><span class="pre">--remote</span></tt>.</p>
+<p>The typical usage of <tt class="docutils literal"><span class="pre">--test-crypto</span></tt> would be something like this:</p>
+<pre class="literal-block">
+openvpn --test-crypto --secret key
+</pre>
+<p>or</p>
+<pre class="literal-block">
+openvpn --test-crypto --secret key --verb 9
+</pre>
+<p class="last">This option is very useful to test OpenVPN after it has been ported to a
+new platform, or to isolate problems in the compiler, OpenSSL crypto
+library, or OpenVPN's crypto code. Since it is a self-test mode,
+problems with encryption and authentication can be debugged
+independently of network and tunnel issues.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tmp-dir <var>dir</var></span></kbd></td>
+<td><p class="first">Specify a directory <tt class="docutils literal">dir</tt> for temporary files. This directory will be
+used by openvpn processes and script to communicate temporary data with
+openvpn main process. Note that the directory must be writable by the
+OpenVPN process after it has dropped it's root privileges.</p>
+<p>This directory will be used by in the following cases:</p>
+<ul class="last simple">
+<li><tt class="docutils literal"><span class="pre">--client-connect</span></tt> scripts and <code>OPENVPN_PLUGIN_CLIENT_CONNECT</code>
+plug-in hook to dynamically generate client-specific configuration
+<code>client_connect_config_file</code> and return success/failure via
+<code>client_connect_deferred_file</code> when using deferred client connect
+method</li>
+<li><code>OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY</code> plug-in hooks returns
+success/failure via <code>auth_control_file</code> when using deferred auth
+method</li>
+<li><code>OPENVPN_PLUGIN_ENABLE_PF</code> plugin hook to pass filtering rules
+via <tt class="docutils literal">pf_file</tt></li>
+</ul>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--use-prediction-resistance</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Enable prediction resistance on mbed TLS's RNG.</p>
+<p>Enabling prediction resistance causes the RNG to reseed in each call for
+random. Reseeding this often can quickly deplete the kernel entropy
+pool.</p>
+<p class="last">If you need this option, please consider running a daemon that adds
+entropy to the kernel pool.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--user <var>user</var></span></kbd></td>
+<td><p class="first">Change the user ID of the OpenVPN process to <tt class="docutils literal">user</tt> after
+initialization, dropping privileges in the process. This option is
+useful to protect the system in the event that some hostile party was
+able to gain control of an OpenVPN session. Though OpenVPN's security
+features make this unlikely, it is provided as a second line of defense.</p>
+<p class="last">By setting <tt class="docutils literal">user</tt> to <code>nobody</code> or somebody similarly unprivileged,
+the hostile party would be limited in what damage they could cause. Of
+course once you take away privileges, you cannot return them to an
+OpenVPN session. This means, for example, that if you want to reset an
+OpenVPN daemon with a <code>SIGUSR1</code> signal (for example in response to
+a DHCP reset), you should make use of one or more of the <tt class="docutils literal"><span class="pre">--persist</span></tt>
+options to ensure that OpenVPN doesn't need to execute any privileged
+operations in order to restart (such as re-reading key files or running
+<tt class="docutils literal">ifconfig</tt> on the TUN device).</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--writepid <var>file</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Write OpenVPN's main process ID to <tt class="docutils literal">file</tt>.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="log-options">
+<h2>Log options</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--echo <var>parms</var></span></kbd></td>
+<td><p class="first">Echo <tt class="docutils literal">parms</tt> to log output.</p>
+<p class="last">Designed to be used to send messages to a controlling application which
+is receiving the OpenVPN log output.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--errors-to-stderr</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Output errors to stderr instead of stdout unless log output is
+redirected by one of the <tt class="docutils literal"><span class="pre">--log</span></tt> options.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--log <var>file</var></span></kbd></td>
+<td><p class="first">Output logging messages to <tt class="docutils literal">file</tt>, including output to stdout/stderr
+which is generated by called scripts. If <tt class="docutils literal">file</tt> already exists it will
+be truncated. This option takes effect immediately when it is parsed in
+the command line and will supersede syslog output if <tt class="docutils literal"><span class="pre">--daemon</span></tt> or
+<tt class="docutils literal"><span class="pre">--inetd</span></tt> is also specified. This option is persistent over the entire
+course of an OpenVPN instantiation and will not be reset by
+<code>SIGHUP</code>, <code>SIGUSR1</code>, or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>.</p>
+<p class="last">Note that on Windows, when OpenVPN is started as a service, logging
+occurs by default without the need to specify this option.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--log-append <var>file</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Append logging messages to <tt class="docutils literal">file</tt>. If <tt class="docutils literal">file</tt> does not exist, it will
+be created. This option behaves exactly like <tt class="docutils literal"><span class="pre">--log</span></tt> except that it
+appends to rather than truncating the log file.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--machine-readable-output</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Always write timestamps and message flags to log messages, even when
+they otherwise would not be prefixed. In particular, this applies to log
+messages sent to stdout.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mute <var>n</var></span></kbd></td>
+<td>Log at most <tt class="docutils literal">n</tt> consecutive messages in the same category. This is
+useful to limit repetitive logging of similar message types.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--mute-replay-warnings</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Silence the output of replay warnings, which are a common false alarm on
+WiFi networks. This option preserves the security of the replay
+protection code without the verbosity associated with warnings about
+duplicate packets.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--suppress-timestamps</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Avoid writing timestamps to log messages, even when they otherwise would
+be prepended. In particular, this applies to log messages sent to
+stdout.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--syslog <var>progname</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Direct log output to system logger, but do not become a daemon. See
+<tt class="docutils literal"><span class="pre">--daemon</span></tt> directive above for description of <tt class="docutils literal">progname</tt> parameter.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--verb <var>n</var></span></kbd></td>
+<td><p class="first">Set output verbosity to <tt class="docutils literal">n</tt> (default <code>1</code>). Each level shows all
+info from the previous levels. Level <code>3</code> is recommended if you want
+a good summary of what's happening without being swamped by output.</p>
+<dl class="last docutils">
+<dt><code>0</code></dt>
+<dd>No output except fatal errors.</dd>
+<dt><code>1</code> to <code>4</code></dt>
+<dd>Normal usage range.</dd>
+<dt><code>5</code></dt>
+<dd>Outputs <code>R</code> and <code>W</code> characters to the console for
+each packet read and write, uppercase is used for TCP/UDP
+packets and lowercase is used for TUN/TAP packets.</dd>
+<dt><code>6</code> to <code>11</code></dt>
+<dd>Debug info range (see <code>errlevel.h</code> in the source code for
+additional information on debug levels).</dd>
+</dl>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="protocol-options">
+<h2>Protocol options</h2>
+<p>Options in this section affect features available in the OpenVPN wire
+protocol. Many of these options also define the encryption options
+of the data channel in the OpenVPN wire protocol. These options must be
+configured in a compatible way between both the local and remote side.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--allow-compression <var>mode</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">As described in the <tt class="docutils literal"><span class="pre">--compress</span></tt> option, compression is a potentially
+dangerous option. This option allows controlling the behaviour of
+OpenVPN when compression is used and allowed.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+allow-compression
+allow-compression mode
+</pre>
+<p>The <tt class="docutils literal">mode</tt> argument can be one of the following values:</p>
+<dl class="last docutils">
+<dt><code>asym</code> (default)</dt>
+<dd>OpenVPN will only <em>decompress downlink packets</em> but <em>not compress
+uplink packets</em>. This also allows migrating to disable compression
+when changing both server and client configurations to remove
+compression at the same time is not a feasible option.</dd>
+<dt><code>no</code></dt>
+<dd>OpenVPN will refuse any non-stub compression.</dd>
+<dt><code>yes</code></dt>
+<dd>OpenVPN will send and receive compressed packets.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--auth <var>alg</var></span></kbd></td>
+<td><p class="first">Authenticate data channel packets and (if enabled) <tt class="docutils literal"><span class="pre">tls-auth</span></tt> control
+channel packets with HMAC using message digest algorithm <tt class="docutils literal">alg</tt>. (The
+default is <tt class="docutils literal">SHA1</tt> ). HMAC is a commonly used message authentication
+algorithm (MAC) that uses a data string, a secure hash algorithm and a
+key to produce a digital signature.</p>
+<p>The OpenVPN data channel protocol uses encrypt-then-mac (i.e. first
+encrypt a packet then HMAC the resulting ciphertext), which prevents
+padding oracle attacks.</p>
+<p>If an AEAD cipher mode (e.g. GCM) is chosen then the specified <tt class="docutils literal"><span class="pre">--auth</span></tt>
+algorithm is ignored for the data channel and the authentication method
+of the AEAD cipher is used instead. Note that <tt class="docutils literal">alg</tt> still specifies
+the digest used for <tt class="docutils literal"><span class="pre">tls-auth</span></tt>.</p>
+<p>In static-key encryption mode, the HMAC key is included in the key file
+generated by <tt class="docutils literal"><span class="pre">--genkey</span></tt>. In TLS mode, the HMAC key is dynamically
+generated and shared between peers via the TLS control channel. If
+OpenVPN receives a packet with a bad HMAC it will drop the packet. HMAC
+usually adds 16 or 20 bytes per packet. Set <tt class="docutils literal">alg=none</tt> to disable
+authentication.</p>
+<p class="last">For more information on HMAC see
+<a class="reference external" href="http://www.cs.ucsd.edu/users/mihir/papers/hmac.html">http://www.cs.ucsd.edu/users/mihir/papers/hmac.html</a></p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--cipher <var>alg</var></span></kbd></td>
+<td><p class="first">This option is deprecated for server-client mode. <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt>
+or possibly <cite>--data-ciphers-fallback`</cite> should be used instead.</p>
+<p>Encrypt data channel packets with cipher algorithm <tt class="docutils literal">alg</tt>.</p>
+<p>The default is <code>BF-CBC</code>, an abbreviation for Blowfish in Cipher
+Block Chaining mode. When cipher negotiation (NCP) is allowed,
+OpenVPN 2.4 and newer on both client and server side will automatically
+upgrade to <code>AES-256-GCM</code>. See <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> and
+<tt class="docutils literal"><span class="pre">--ncp-disable</span></tt> for more details on NCP.</p>
+<p>Using <code>BF-CBC</code> is no longer recommended, because of its 64-bit
+block size. This small block size allows attacks based on collisions, as
+demonstrated by SWEET32. See
+<a class="reference external" href="https://community.openvpn.net/openvpn/wiki/SWEET32">https://community.openvpn.net/openvpn/wiki/SWEET32</a>
+for details. Due to this, support for <code>BF-CBC</code>, <code>DES</code>,
+<code>CAST5</code>, <code>IDEA</code> and <code>RC2</code> ciphers will be removed in
+OpenVPN 2.6.</p>
+<p>To see other ciphers that are available with OpenVPN, use the
+<tt class="docutils literal"><span class="pre">--show-ciphers</span></tt> option.</p>
+<p class="last">Set <tt class="docutils literal">alg</tt> to <code>none</code> to disable encryption.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--compress <var>algorithm</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first"><strong>DEPRECATED</strong> Enable a compression algorithm. Compression is generally
+not recommended. VPN tunnels which use compression are susceptible to
+the VORALCE attack vector.</p>
+<p>The <tt class="docutils literal">algorithm</tt> parameter may be <code>lzo</code>, <code>lz4</code>,
+<code>lz4-v2</code>, <code>stub</code>, <code>stub-v2</code> or empty.
+LZO and LZ4 are different compression algorithms, with LZ4 generally
+offering the best performance with least CPU usage.</p>
+<p>The <code>lz4-v2</code> and <code>stub-v2</code> variants implement a better
+framing that does not add overhead when packets cannot be compressed. All
+other variants always add one extra framing byte compared to no
+compression framing.</p>
+<p>If the <tt class="docutils literal">algorithm</tt> parameter is <code>stub</code>, <code>stub-v2</code> or empty,
+compression will be turned off, but the packet framing for compression
+will still be enabled, allowing a different setting to be pushed later.
+Additionally, <code>stub</code> and <code>stub-v2</code> wil disable announcing
+<tt class="docutils literal">lzo</tt> and <tt class="docutils literal">lz4</tt> compression support via <em>IV_</em> variables to the
+server.</p>
+<p>Note: the <code>stub</code> (or empty) option is NOT compatible with the older
+option <tt class="docutils literal"><span class="pre">--comp-lzo</span> no</tt>.</p>
+<p><strong>*Security Considerations*</strong></p>
+<p class="last">Compression and encryption is a tricky combination. If an attacker knows
+or is able to control (parts of) the plain-text of packets that contain
+secrets, the attacker might be able to extract the secret if compression
+is enabled. See e.g. the <em>CRIME</em> and <em>BREACH</em> attacks on TLS and
+<em>VORACLE</em> on VPNs which also leverage to break encryption. If you are not
+entirely sure that the above does not apply to your traffic, you are
+advised to <em>not</em> enable compression.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--comp-lzo <var>mode</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first"><strong>DEPRECATED</strong> Enable LZO compression algorithm. Compression is
+generally not recommended. VPN tunnels which uses compression are
+suspectible to the VORALCE attack vector.</p>
+<p>Use LZO compression -- may add up to 1 byte per packet for incompressible
+data. <tt class="docutils literal">mode</tt> may be <code>yes</code>, <code>no</code>, or <code>adaptive</code>
+(default).</p>
+<p>In a server mode setup, it is possible to selectively turn compression
+on or off for individual clients.</p>
+<p>First, make sure the client-side config file enables selective
+compression by having at least one <tt class="docutils literal"><span class="pre">--comp-lzo</span></tt> directive, such as
+<tt class="docutils literal"><span class="pre">--comp-lzo</span> no</tt>. This will turn off compression by default, but allow
+a future directive push from the server to dynamically change the
+<code>on</code>/<code>off</code>/<code>adaptive</code> setting.</p>
+<p>Next in a <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> file, specify the compression setting
+for the client, for example:</p>
+<pre class="literal-block">
+comp-lzo yes
+push &quot;comp-lzo yes&quot;
+</pre>
+<p class="last">The first line sets the <tt class="docutils literal"><span class="pre">comp-lzo</span></tt> setting for the server side of the
+link, the second sets the client side.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--comp-noadapt</span></kbd></td>
+<td><p class="first"><strong>DEPRECATED</strong> When used in conjunction with <tt class="docutils literal"><span class="pre">--comp-lzo</span></tt>, this option
+will disable OpenVPN's adaptive compression algorithm. Normally, adaptive
+compression is enabled with <tt class="docutils literal"><span class="pre">--comp-lzo</span></tt>.</p>
+<p class="last">Adaptive compression tries to optimize the case where you have
+compression enabled, but you are sending predominantly incompressible
+(or pre-compressed) packets over the tunnel, such as an FTP or rsync
+transfer of a large, compressed file. With adaptive compression, OpenVPN
+will periodically sample the compression process to measure its
+efficiency. If the data being sent over the tunnel is already
+compressed, the compression efficiency will be very low, triggering
+openvpn to disable compression for a period of time until the next
+re-sample test.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--key-direction</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Alternative way of specifying the optional direction parameter for the
+<tt class="docutils literal"><span class="pre">--tls-auth</span></tt> and <tt class="docutils literal"><span class="pre">--secret</span></tt> options. Useful when using inline files
+(See section on inline files).</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--keysize <var>n</var></span></kbd></td>
+<td><p class="first"><strong>DEPRECATED</strong> This option will be removed in OpenVPN 2.6.</p>
+<p class="last">Size of cipher key in bits (optional). If unspecified, defaults to
+cipher-specific default. The <tt class="docutils literal"><span class="pre">--show-ciphers</span></tt> option (see below) shows
+all available OpenSSL ciphers, their default key sizes, and whether the
+key size can be changed. Use care in changing a cipher's default key
+size. Many ciphers have not been extensively cryptanalyzed with
+non-standard key lengths, and a larger key may offer no real guarantee
+of greater security, or may even reduce security.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--data-ciphers <var>cipher-list</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Restrict the allowed ciphers to be negotiated to the ciphers in
+<tt class="docutils literal"><span class="pre">cipher-list</span></tt>. <tt class="docutils literal"><span class="pre">cipher-list</span></tt> is a colon-separated list of ciphers,
+and defaults to <code>AES-256-GCM:AES-128-GCM</code>.</p>
+<p>For servers, the first cipher from <tt class="docutils literal"><span class="pre">cipher-list</span></tt> that is also
+supported by the client will be pushed to clients that support cipher
+negotiation.</p>
+<p>Cipher negotiation is enabled in client-server mode only. I.e. if
+<tt class="docutils literal"><span class="pre">--mode</span></tt> is set to 'server' (server-side, implied by setting
+<tt class="docutils literal"><span class="pre">--server</span></tt> ), or if <tt class="docutils literal"><span class="pre">--pull</span></tt> is specified (client-side, implied by
+setting --client).</p>
+<p>If no common cipher is found during cipher negotiation, the connection
+is terminated. To support old clients/old servers that do not provide any
+cipher negotiation support see <tt class="docutils literal"><span class="pre">--data-ciphers-fallback</span></tt>.</p>
+<p>Additionally, to allow for more smooth transition, if NCP is enabled,
+OpenVPN will inherit the cipher of the peer if that cipher is different
+from the local <tt class="docutils literal"><span class="pre">--cipher</span></tt> setting, but the peer cipher is one of the
+ciphers specified in <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt>. E.g. a non-NCP client (&lt;=v2.3,
+or with --ncp-disabled set) connecting to a NCP server (v2.4+) with
+<tt class="docutils literal"><span class="pre">--cipher</span> <span class="pre">BF-CBC</span></tt> and <tt class="docutils literal"><span class="pre">--data-ciphers</span> <span class="pre">AES-256-GCM:AES-256-CBC</span></tt> set can
+either specify <tt class="docutils literal"><span class="pre">--cipher</span> <span class="pre">BF-CBC</span></tt> or <tt class="docutils literal"><span class="pre">--cipher</span> <span class="pre">AES-256-CBC</span></tt> and both
+will work.</p>
+<p>Note for using NCP with an OpenVPN 2.4 peer: This list must include the
+<code>AES-256-GCM</code> and <code>AES-128-GCM</code> ciphers.</p>
+<p>This list is restricted to be 127 chars long after conversion to OpenVPN
+ciphers.</p>
+<p class="last">This option was called <tt class="docutils literal"><span class="pre">--ncp-ciphers</span></tt> in OpenVPN 2.4 but has been renamed
+to <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> in OpenVPN 2.5 to more accurately reflect its meaning.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--data-ciphers-fallback <var>alg</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Configure a cipher that is used to fall back to if we could not determine
+which cipher the peer is willing to use.</p>
+<p class="last">This option should only be needed to
+connect to peers that are running OpenVPN 2.3 and older version, and
+have been configured with <cite>--enable-small</cite>
+(typically used on routers or other embedded devices).</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ncp-disable</span></kbd></td>
+<td><strong>DEPRECATED</strong> Disable &quot;Negotiable Crypto Parameters&quot;. This completely
+disables cipher negotiation.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--secret <var>args</var></span></kbd></td>
+<td><p class="first">Enable Static Key encryption mode (non-TLS). Use pre-shared secret
+<tt class="docutils literal">file</tt> which was generated with <tt class="docutils literal"><span class="pre">--genkey</span></tt>.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+secret file
+secret file direction
+</pre>
+<p>The optional <tt class="docutils literal">direction</tt> parameter enables the use of 4 distinct keys
+(HMAC-send, cipher-encrypt, HMAC-receive, cipher-decrypt), so that each
+data flow direction has a different set of HMAC and cipher keys. This
+has a number of desirable security properties including eliminating
+certain kinds of DoS and message replay attacks.</p>
+<p>When the <tt class="docutils literal">direction</tt> parameter is omitted, 2 keys are used
+bidirectionally, one for HMAC and the other for encryption/decryption.</p>
+<p>The <tt class="docutils literal">direction</tt> parameter should always be complementary on either
+side of the connection, i.e. one side should use <code>0</code> and the other
+should use <code>1</code>, or both sides should omit it altogether.</p>
+<p>The <tt class="docutils literal">direction</tt> parameter requires that <tt class="docutils literal">file</tt> contains a 2048 bit
+key. While pre-1.5 versions of OpenVPN generate 1024 bit key files, any
+version of OpenVPN which supports the <tt class="docutils literal">direction</tt> parameter, will also
+support 2048 bit key file generation using the <tt class="docutils literal"><span class="pre">--genkey</span></tt> option.</p>
+<p>Static key encryption mode has certain advantages, the primary being
+ease of configuration.</p>
+<p>There are no certificates or certificate authorities or complicated
+negotiation handshakes and protocols. The only requirement is that you
+have a pre-existing secure channel with your peer (such as <tt class="docutils literal">ssh</tt>) to
+initially copy the key. This requirement, along with the fact that your
+key never changes unless you manually generate a new one, makes it
+somewhat less secure than TLS mode (see below). If an attacker manages
+to steal your key, everything that was ever encrypted with it is
+compromised. Contrast that to the perfect forward secrecy features of
+TLS mode (using Diffie Hellman key exchange), where even if an attacker
+was able to steal your private key, he would gain no information to help
+him decrypt past sessions.</p>
+<p class="last">Another advantageous aspect of Static Key encryption mode is that it is
+a handshake-free protocol without any distinguishing signature or
+feature (such as a header or protocol handshake sequence) that would
+mark the ciphertext packets as being generated by OpenVPN. Anyone
+eavesdropping on the wire would see nothing but random-looking data.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tran-window <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Transition window -- our old key can live this many seconds after a new
+a key renegotiation begins (default <code>3600</code> seconds). This feature
+allows for a graceful transition from old to new key, and removes the key
+renegotiation sequence from the critical path of tunnel data forwarding.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="client-options">
+<h2>Client Options</h2>
+<p>The client options are used when connecting to an OpenVPN server configured
+to use <tt class="docutils literal"><span class="pre">--server</span></tt>, <tt class="docutils literal"><span class="pre">--server-bridge</span></tt>, or <tt class="docutils literal"><span class="pre">--mode</span> server</tt> in its
+configuration.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--allow-pull-fqdn</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Allow client to pull DNS names from server (rather than being limited to
+IP address) for <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>, <tt class="docutils literal"><span class="pre">--route</span></tt>, and <tt class="docutils literal"><span class="pre">--route-gateway</span></tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--allow-recursive-routing</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When this option is set, OpenVPN will not drop incoming tun packets with
+same destination as host.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-token <var>token</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">This is not an option to be used directly in any configuration files,
+but rather push this option from a <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script or a
+<tt class="docutils literal"><span class="pre">--plugin</span></tt> which hooks into the <code>OPENVPN_PLUGIN_CLIENT_CONNECT</code>
+or <code>OPENVPN_PLUGIN_CLIENT_CONNECT_V2</code> calls. This option provides a
+possibility to replace the clients password with an authentication token
+during the lifetime of the OpenVPN client.</p>
+<p>Whenever the connection is renegotiated and the
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script or <tt class="docutils literal"><span class="pre">--plugin</span></tt> making use of the
+<code>OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY</code> hook is triggered, it will
+pass over this token as the password instead of the password the user
+provided. The authentication token can only be reset by a full reconnect
+where the server can push new options to the client. The password the
+user entered is never preserved once an authentication token has been
+set. If the OpenVPN server side rejects the authentication token then
+the client will receive an <code>AUTH_FAILED</code> and disconnect.</p>
+<p>The purpose of this is to enable two factor authentication methods, such
+as HOTP or TOTP, to be used without needing to retrieve a new OTP code
+each time the connection is renegotiated. Another use case is to cache
+authentication data on the client without needing to have the users
+password cached in memory during the life time of the session.</p>
+<p>To make use of this feature, the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script or
+<tt class="docutils literal"><span class="pre">--plugin</span></tt> needs to put</p>
+<pre class="literal-block">
+push &quot;auth-token UNIQUE_TOKEN_VALUE&quot;
+</pre>
+<p>into the file/buffer for dynamic configuration data. This will then make
+the OpenVPN server to push this value to the client, which replaces the
+local password with the <tt class="docutils literal">UNIQUE_TOKEN_VALUE</tt>.</p>
+<p class="last">Newer clients (2.4.7+) will fall back to the original password method
+after a failed auth. Older clients will keep using the token value and
+react according to <tt class="docutils literal"><span class="pre">--auth-retry</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-token-user <var>base64username</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Companion option to <tt class="docutils literal"><span class="pre">--auth-token</span></tt>. This options allows to override
+the username used by the client when reauthenticating with the <tt class="docutils literal"><span class="pre">auth-token</span></tt>.
+It also allows to use <tt class="docutils literal"><span class="pre">--auth-token</span></tt> in setups that normally do not use
+username and password.</p>
+<p class="last">The username has to be base64 encoded.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-user-pass</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Authenticate with server using username/password.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+auth-user-pass
+auth-user-pass up
+</pre>
+<p>If <tt class="docutils literal">up</tt> is present, it must be a file containing username/password on 2
+lines. If the password line is missing, OpenVPN will prompt for one.</p>
+<p>If <tt class="docutils literal">up</tt> is omitted, username/password will be prompted from the
+console.</p>
+<p class="last">The server configuration must specify an <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt>
+script to verify the username/password provided by the client.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-retry <var>type</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Controls how OpenVPN responds to username/password verification errors
+such as the client-side response to an <code>AUTH_FAILED</code> message from
+the server or verification failure of the private key password.</p>
+<p>Normally used to prevent auth errors from being fatal on the client
+side, and to permit username/password requeries in case of error.</p>
+<p>An <code>AUTH_FAILED</code> message is generated by the server if the client
+fails <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> authentication, or if the server-side
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script returns an error status when the client
+tries to connect.</p>
+<p><tt class="docutils literal">type</tt> can be one of:</p>
+<dl class="docutils">
+<dt><code>none</code></dt>
+<dd>Client will exit with a fatal error (this is the default).</dd>
+<dt><code>nointeract</code></dt>
+<dd>Client will retry the connection without requerying
+for an <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> username/password. Use this option for
+unattended clients.</dd>
+<dt><code>interact</code></dt>
+<dd>Client will requery for an <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt>
+username/password and/or private key password before attempting a
+reconnection.</dd>
+</dl>
+<p class="last">Note that while this option cannot be pushed, it can be controlled from
+the management interface.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--client</span></kbd></td>
+<td><p class="first">A helper directive designed to simplify the configuration of OpenVPN's
+client mode. This directive is equivalent to:</p>
+<pre class="last literal-block">
+pull
+tls-client
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-nat <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">This pushable client option sets up a stateless one-to-one NAT rule on
+packet addresses (not ports), and is useful in cases where routes or
+ifconfig settings pushed to the client would create an IP numbering
+conflict.</p>
+<p>Examples:</p>
+<pre class="literal-block">
+client-nat snat 192.168.0.0/255.255.0.0
+client-nat dnat 10.64.0.0/255.255.0.0
+</pre>
+<p><tt class="docutils literal">network/netmask</tt> (for example <code>192.168.0.0/255.255.0.0</code>) defines
+the local view of a resource from the client perspective, while
+<tt class="docutils literal">alias/netmask</tt> (for example <code>10.64.0.0/255.255.0.0</code>) defines the
+remote view from the server perspective.</p>
+<p>Use <code>snat</code> (source NAT) for resources owned by the client and
+<code>dnat</code> (destination NAT) for remote resources.</p>
+<p class="last">Set <tt class="docutils literal"><span class="pre">--verb</span> 6</tt> for debugging info showing the transformation of
+src/dest addresses in packets.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--connect-retry <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Wait <tt class="docutils literal">n</tt> seconds between connection attempts (default <code>5</code>).
+Repeated reconnection attempts are slowed down after 5 retries per
+remote by doubling the wait time after each unsuccessful attempt. An
+optional argument <tt class="docutils literal">max</tt> specifies the maximum value of wait time in
+seconds at which it gets capped (default <code>300</code>).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--connect-retry-max <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><tt class="docutils literal">n</tt> specifies the number of times each <tt class="docutils literal"><span class="pre">--remote</span></tt> or
+<tt class="docutils literal">&lt;connection&gt;</tt> entry is tried. Specifying <tt class="docutils literal">n</tt> as <code>1</code> would try
+each entry exactly once. A successful connection resets the counter.
+(default <em>unlimited</em>).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--connect-timeout <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>See <tt class="docutils literal"><span class="pre">--server-poll-timeout</span></tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--explicit-exit-notify <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">In UDP client mode or point-to-point mode, send server/peer an exit
+notification if tunnel is restarted or OpenVPN process is exited. In
+client mode, on exit/restart, this option will tell the server to
+immediately close its client instance object rather than waiting for a
+timeout.</p>
+<p>The <strong>n</strong> parameter (default <code>1</code> if not present) controls the
+maximum number of attempts that the client will try to resend the exit
+notification message.</p>
+<p>In UDP server mode, send <code>RESTART</code> control channel command to
+connected clients. The <tt class="docutils literal">n</tt> parameter (default <code>1</code> if not present)
+controls client behavior. With <tt class="docutils literal">n</tt> = <code>1</code> client will attempt to
+reconnect to the same server, with <tt class="docutils literal">n</tt> = <code>2</code> client will advance
+to the next server.</p>
+<p class="last">OpenVPN will not send any exit notifications unless this option is
+enabled.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--inactive <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Causes OpenVPN to exit after <tt class="docutils literal">n</tt> seconds of inactivity on the TUN/TAP
+device. The time length of inactivity is measured since the last
+incoming or outgoing tunnel packet. The default value is 0 seconds,
+which disables this feature.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+inactive n
+inactive n bytes
+</pre>
+<p>If the optional <tt class="docutils literal">bytes</tt> parameter is included, exit if less than
+<tt class="docutils literal">bytes</tt> of combined in/out traffic are produced on the tun/tap device
+in <tt class="docutils literal">n</tt> seconds.</p>
+<p class="last">In any case, OpenVPN's internal ping packets (which are just keepalives)
+and TLS control packets are not considered &quot;activity&quot;, nor are they
+counted as traffic, as they are used internally by OpenVPN and are not
+an indication of actual user activity.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--proto-force <var>p</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When iterating through connection profiles, only consider profiles using
+protocol <tt class="docutils literal">p</tt> (<code>tcp</code> | <code>udp</code>).</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--pull</span></kbd></td>
+<td><p class="first">This option must be used on a client which is connecting to a
+multi-client server. It indicates to OpenVPN that it should accept
+options pushed by the server, provided they are part of the legal set of
+pushable options (note that the <tt class="docutils literal"><span class="pre">--pull</span></tt> option is implied by
+<tt class="docutils literal"><span class="pre">--client</span></tt> ).</p>
+<p class="last">In particular, <tt class="docutils literal"><span class="pre">--pull</span></tt> allows the server to push routes to the
+client, so you should not use <tt class="docutils literal"><span class="pre">--pull</span></tt> or <tt class="docutils literal"><span class="pre">--client</span></tt> in situations
+where you don't trust the server to have control over the client's
+routing table.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pull-filter <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Filter options on the client pushed by the server to the client.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+pull-filter accept text
+pull-filter ignore text
+pull-filter reject text
+</pre>
+<p>Filter options received from the server if the option starts with
+<code>text</code>. The action flag <code>accept</code> allows the option,
+<code>ignore</code> removes it and <code>reject</code> flags an error and triggers
+a <code>SIGUSR1</code> restart. The filters may be specified multiple times,
+and each filter is applied in the order it is specified. The filtering of
+each option stops as soon as a match is found. Unmatched options are accepted
+by default.</p>
+<p>Prefix comparison is used to match <code>text</code> against the received option so
+that</p>
+<pre class="literal-block">
+pull-filter ignore &quot;route&quot;
+</pre>
+<p>would remove all pushed options starting with <tt class="docutils literal">route</tt> which would
+include, for example, <tt class="docutils literal"><span class="pre">route-gateway</span></tt>. Enclose <em>text</em> in quotes to
+embed spaces.</p>
+<pre class="literal-block">
+pull-filter accept &quot;route 192.168.1.&quot;
+pull-filter ignore &quot;route &quot;
+</pre>
+<p>would remove all routes that do not start with <tt class="docutils literal">192.168.1</tt>.</p>
+<p class="last"><em>Note</em> that <code>reject</code> may result in a repeated cycle of failure and
+reconnect, unless multiple remotes are specified and connection to the
+next remote succeeds. To silently ignore an option pushed by the server,
+use <code>ignore</code>.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--remote <var>args</var></span></kbd></td>
+<td><p class="first">Remote host name or IP address, port and protocol.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+remote host
+remote host port
+remote host port proto
+</pre>
+<p>The <tt class="docutils literal">port</tt> and <tt class="docutils literal">proto</tt> arguments are optional. The OpenVPN client
+will try to connect to a server at <tt class="docutils literal">host:port</tt>. The <tt class="docutils literal">proto</tt> argument
+indicates the protocol to use when connecting with the remote, and may be
+<code>tcp</code> or <code>udp</code>. To enforce IPv4 or IPv6 connections add a
+<code>4</code> or <code>6</code> suffix; like <code>udp4</code> / <code>udp6</code>
+/ <code>tcp4</code> / <code>tcp6</code>.</p>
+<p>On the client, multiple <tt class="docutils literal"><span class="pre">--remote</span></tt> options may be specified for
+redundancy, each referring to a different OpenVPN server, in the order
+specified by the list of <tt class="docutils literal"><span class="pre">--remote</span></tt> options. Specifying multiple
+<tt class="docutils literal"><span class="pre">--remote</span></tt> options for this purpose is a special case of the more
+general connection-profile feature. See the <tt class="docutils literal">&lt;connection&gt;</tt>
+documentation below.</p>
+<p>The client will move on to the next host in the list, in the event of
+connection failure. Note that at any given time, the OpenVPN client will
+at most be connected to one server.</p>
+<p>Examples:</p>
+<pre class="literal-block">
+remote server1.example.net
+remote server1.example.net 1194
+remote server2.example.net 1194 tcp
+</pre>
+<dl class="docutils">
+<dt><em>Note:</em></dt>
+<dd><p class="first">Since UDP is connectionless, connection failure is defined by
+the <tt class="docutils literal"><span class="pre">--ping</span></tt> and <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> options.</p>
+<p class="last">Also, if you use multiple <tt class="docutils literal"><span class="pre">--remote</span></tt> options, AND you are dropping
+root privileges on the client with <tt class="docutils literal"><span class="pre">--user</span></tt> and/or <tt class="docutils literal"><span class="pre">--group</span></tt> AND
+the client is running a non-Windows OS, if the client needs to switch
+to a different server, and that server pushes back different TUN/TAP
+or route settings, the client may lack the necessary privileges to
+close and reopen the TUN/TAP interface. This could cause the client
+to exit with a fatal error.</p>
+</dd>
+</dl>
+<p>If <tt class="docutils literal"><span class="pre">--remote</span></tt> is unspecified, OpenVPN will listen for packets from any
+IP address, but will not act on those packets unless they pass all
+authentication tests. This requirement for authentication is binding on
+all potential peers, even those from known and supposedly trusted IP
+addresses (it is very easy to forge a source IP address on a UDP
+packet).</p>
+<p>When used in TCP mode, <tt class="docutils literal"><span class="pre">--remote</span></tt> will act as a filter, rejecting
+connections from any host which does not match <tt class="docutils literal">host</tt>.</p>
+<p class="last">If <tt class="docutils literal">host</tt> is a DNS name which resolves to multiple IP addresses,
+OpenVPN will try them in the order that the system getaddrinfo()
+presents them, so priorization and DNS randomization is done by the
+system library. Unless an IP version is forced by the protocol
+specification (4/6 suffix), OpenVPN will try both IPv4 and IPv6
+addresses, in the order getaddrinfo() returns them.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remote-random</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When multiple <tt class="docutils literal"><span class="pre">--remote</span></tt> address/ports are specified, or if connection
+profiles are being used, initially randomize the order of the list as a
+kind of basic load-balancing measure.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remote-random-hostname</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Prepend a random string (6 bytes, 12 hex characters) to hostname to
+prevent DNS caching. For example, &quot;foo.bar.gov&quot; would be modified to
+&quot;&lt;random-chars&gt;.foo.bar.gov&quot;.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--resolv-retry <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">If hostname resolve fails for <tt class="docutils literal"><span class="pre">--remote</span></tt>, retry resolve for <tt class="docutils literal">n</tt>
+seconds before failing.</p>
+<p>Set <tt class="docutils literal">n</tt> to &quot;infinite&quot; to retry indefinitely.</p>
+<p class="last">By default, <tt class="docutils literal"><span class="pre">--resolv-retry</span> infinite</tt> is enabled. You can disable by
+setting n=0.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--single-session</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">After initially connecting to a remote peer, disallow any new
+connections. Using this option means that a remote peer cannot connect,
+disconnect, and then reconnect.</p>
+<p>If the daemon is reset by a signal or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>, it will allow
+one new connection.</p>
+<p class="last"><tt class="docutils literal"><span class="pre">--single-session</span></tt> can be used with <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> or <tt class="docutils literal"><span class="pre">--inactive</span></tt>
+to create a single dynamic session that will exit when finished.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--server-poll-timeout <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When connecting to a remote server do not wait for more than <tt class="docutils literal">n</tt>
+seconds for a response before trying the next server. The default value
+is 120s. This timeout includes proxy and TCP connect timeouts.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--static-challenge <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Enable static challenge/response protocol</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+static-challenge text echo
+</pre>
+<p>The <tt class="docutils literal">text</tt> challenge text is presented to the user which describes what
+information is requested. The <tt class="docutils literal">echo</tt> flag indicates if the user's
+input should be echoed on the screen. Valid <tt class="docutils literal">echo</tt> values are
+<code>0</code> or <code>1</code>.</p>
+<p class="last">See management-notes.txt in the OpenVPN distribution for a description of
+the OpenVPN challenge/response protocol.</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--show-proxy-settings</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Show sensed HTTP or SOCKS proxy settings. Currently, only Windows
+clients support this option.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--http-proxy <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Connect to remote host through an HTTP proxy. This requires at least an
+address <tt class="docutils literal">server</tt> and <tt class="docutils literal">port</tt> argument. If HTTP Proxy-Authenticate
+is required, a file name to an <tt class="docutils literal">authfile</tt> file containing a username
+and password on 2 lines can be given, or <code>stdin</code> to prompt from
+console. Its content can also be specified in the config file with the
+<tt class="docutils literal"><span class="pre">--http-proxy-user-pass</span></tt> option. (See section on inline files)</p>
+<p>The last optional argument is an <tt class="docutils literal"><span class="pre">auth-method</span></tt> which should be one
+of <code>none</code>, <code>basic</code>, or <code>ntlm</code>.</p>
+<p>HTTP Digest authentication is supported as well, but only via the
+<code>auto</code> or <code>auto-nct</code> flags (below). This must replace
+the <tt class="docutils literal">authfile</tt> argument.</p>
+<p>The <code>auto</code> flag causes OpenVPN to automatically determine the
+<tt class="docutils literal"><span class="pre">auth-method</span></tt> and query stdin or the management interface for
+username/password credentials, if required. This flag exists on OpenVPN
+2.1 or higher.</p>
+<p>The <tt class="docutils literal"><span class="pre">auto-nct</span></tt> flag (no clear-text auth) instructs OpenVPN to
+automatically determine the authentication method, but to reject weak
+authentication protocols such as HTTP Basic Authentication.</p>
+<p>Examples:</p>
+<pre class="last literal-block">
+http-proxy proxy.example.net 3128
+http-proxy proxy.example.net 3128 authfile.txt
+http-proxy proxy.example.net 3128 stdin
+http-proxy proxy.example.net 3128 auto basic
+http-proxy proxy.example.net 3128 auto-nct ntlm
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--http-proxy-option <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set extended HTTP proxy options. Requires an option <tt class="docutils literal">type</tt> as argument
+and an optional <tt class="docutils literal">parameter</tt> to the type. Repeat to set multiple
+options.</p>
+<dl class="docutils">
+<dt><code>VERSION</code> <tt class="docutils literal">version</tt></dt>
+<dd>Set HTTP version number to <tt class="docutils literal">version</tt> (default <code>1.0</code>).</dd>
+<dt><code>AGENT</code> <tt class="docutils literal"><span class="pre">user-agent</span></tt></dt>
+<dd>Set HTTP &quot;User-Agent&quot; string to <tt class="docutils literal"><span class="pre">user-agent</span></tt>.</dd>
+<dt><code>CUSTOM-HEADER</code> <tt class="docutils literal">name</tt> <tt class="docutils literal">content</tt></dt>
+<dd>Adds the custom Header with <tt class="docutils literal">name</tt> as name and <tt class="docutils literal">content</tt> as
+the content of the custom HTTP header.</dd>
+</dl>
+<p>Examples:</p>
+<pre class="last literal-block">
+http-proxy-option VERSION 1.1
+http-proxy-option AGENT OpenVPN/2.4
+http-proxy-option X-Proxy-Flag some-flags
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--socks-proxy <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Connect to remote host through a Socks5 proxy. A required <tt class="docutils literal">server</tt>
+argument is needed. Optionally a <tt class="docutils literal">port</tt> (default <code>1080</code>) and
+<tt class="docutils literal">authfile</tt> can be given. The <tt class="docutils literal">authfile</tt> is a file containing a
+username and password on 2 lines, or <code>stdin</code> can be used to
+prompt from console.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="server-options">
+<h2>Server Options</h2>
+<p>Starting with OpenVPN 2.0, a multi-client TCP/UDP server mode is
+supported, and can be enabled with the <tt class="docutils literal"><span class="pre">--mode</span> server</tt> option. In
+server mode, OpenVPN will listen on a single port for incoming client
+connections. All client connections will be routed through a single tun
+or tap interface. This mode is designed for scalability and should be
+able to support hundreds or even thousands of clients on sufficiently
+fast hardware. SSL/TLS authentication must be used in this mode.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-gen-token <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Returns an authentication token to successfully authenticated clients.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+auth-gen-token [lifetime] [external-auth]
+</pre>
+<p>After successful user/password authentication, the OpenVPN server will
+with this option generate a temporary authentication token and push that
+to the client. On the following renegotiations, the OpenVPN client will pass
+this token instead of the users password. On the server side the server
+will do the token authentication internally and it will NOT do any
+additional authentications against configured external user/password
+authentication mechanisms.</p>
+<p>The tokens implemented by this mechanism include an initial timestamp and
+a renew timestamp and are secured by HMAC.</p>
+<p>The <tt class="docutils literal">lifetime</tt> argument defines how long the generated token is valid.
+The lifetime is defined in seconds. If lifetime is not set or it is set
+to <code>0</code>, the token will never expire.</p>
+<p>The token will expire either after the configured <tt class="docutils literal">lifetime</tt> of the
+token is reached or after not being renewed for more than 2 *
+<tt class="docutils literal"><span class="pre">reneg-sec</span></tt> seconds. Clients will be sent renewed tokens on every TLS
+renogiation to keep the client's token updated. This is done to
+invalidate a token if a client is disconnected for a sufficently long
+time, while at the same time permitting much longer token lifetimes for
+active clients.</p>
+<p>This feature is useful for environments which are configured to use One
+Time Passwords (OTP) as part of the user/password authentications and
+that authentication mechanism does not implement any auth-token support.</p>
+<p>When the <code>external-auth</code> keyword is present the normal
+authentication method will always be called even if auth-token succeeds.
+Normally other authentications method are skipped if auth-token
+verification suceeds or fails.</p>
+<p>This option postpones this decision to the external authentication
+methods and checks the validity of the account and do other checks.</p>
+<p>In this mode the environment will have a <tt class="docutils literal">session_id</tt> variable that
+holds the session id from auth-gen-token. Also an environment variable
+<tt class="docutils literal">session_state</tt> is present. This variable indicates whether the
+auth-token has succeeded or not. It can have the following values:</p>
+<dl class="docutils">
+<dt><code>Initial</code></dt>
+<dd>No token from client.</dd>
+<dt><code>Authenticated</code></dt>
+<dd>Token is valid and not expired.</dd>
+<dt><code>Expired</code></dt>
+<dd>Token is valid but has expired.</dd>
+<dt><code>Invalid</code></dt>
+<dd>Token is invalid (failed HMAC or wrong length)</dd>
+<dt><code>AuthenticatedEmptyUser</code> / <code>ExpiredEmptyUser</code></dt>
+<dd>The token is not valid with the username sent from the client but
+would be valid (or expired) if we assume an empty username was
+used instead. These two cases are a workaround for behaviour in
+OpenVPN 3. If this workaround is not needed these two cases should
+be handled in the same way as <code>Invalid</code>.</dd>
+</dl>
+<p class="last"><strong>Warning:</strong> Use this feature only if you want your authentication
+method called on every verification. Since the external authentication
+is called it needs to also indicate a success or failure of the
+authentication. It is strongly recommended to return an authentication
+failure in the case of the Invalid/Expired auth-token with the
+external-auth option unless the client could authenticate in another
+acceptable way (e.g. client certificate), otherwise returning success
+will lead to authentication bypass (as does returning success on a wrong
+password from a script).</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-gen-token-secret <var>file</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specifies a file that holds a secret for the HMAC used in
+<tt class="docutils literal"><span class="pre">--auth-gen-token</span></tt> If <tt class="docutils literal">file</tt> is not present OpenVPN will generate a
+random secret on startup. This file should be used if auth-token should
+validate after restarting a server or if client should be able to roam
+between multiple OpenVPN servers with their auth-token.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-user-pass-optional</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Allow connections by clients that do not specify a username/password.
+Normally, when <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> or
+<tt class="docutils literal"><span class="pre">--management-client-auth</span></tt> are specified (or an authentication plugin
+module), the OpenVPN server daemon will require connecting clients to
+specify a username and password. This option makes the submission of a
+username/password by clients optional, passing the responsibility to the
+user-defined authentication module/script to accept or deny the client
+based on other factors (such as the setting of X509 certificate fields).
+When this option is used, and a connecting client does not submit a
+username/password, the user-defined authentication module/script will
+see the username and password as being set to empty strings (&quot;&quot;). The
+authentication module/script MUST have logic to detect this condition
+and respond accordingly.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ccd-exclusive</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Require, as a condition of authentication, that a connecting client has
+a <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> file.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-config-dir <var>dir</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify a directory <tt class="docutils literal">dir</tt> for custom client config files. After a
+connecting client has been authenticated, OpenVPN will look in this
+directory for a file having the same name as the client's X509 common
+name. If a matching file exists, it will be opened and parsed for
+client-specific configuration options. If no matching file is found,
+OpenVPN will instead try to open and parse a default file called
+&quot;DEFAULT&quot;, which may be provided but is not required. Note that the
+configuration files must be readable by the OpenVPN process after it has
+dropped it's root privileges.</p>
+<p>This file can specify a fixed IP address for a given client using
+<tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt>, as well as fixed subnets owned by the client using
+<tt class="docutils literal"><span class="pre">--iroute</span></tt>.</p>
+<p>One of the useful properties of this option is that it allows client
+configuration files to be conveniently created, edited, or removed while
+the server is live, without needing to restart the server.</p>
+<p class="last">The following options are legal in a client-specific context: <tt class="docutils literal"><span class="pre">--push</span></tt>,
+<tt class="docutils literal"><span class="pre">--push-reset</span></tt>, <tt class="docutils literal"><span class="pre">--push-remove</span></tt>, <tt class="docutils literal"><span class="pre">--iroute</span></tt>, <tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt>,
+<tt class="docutils literal"><span class="pre">--vlan-pvid</span></tt> and <tt class="docutils literal"><span class="pre">--config</span></tt>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-to-client</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Because the OpenVPN server mode handles multiple clients through a
+single tun or tap interface, it is effectively a router. The
+<tt class="docutils literal"><span class="pre">--client-to-client</span></tt> flag tells OpenVPN to internally route
+client-to-client traffic rather than pushing all client-originating
+traffic to the TUN/TAP interface.</p>
+<p class="last">When this option is used, each client will &quot;see&quot; the other clients which
+are currently connected. Otherwise, each client will only see the
+server. Don't use this option if you want to firewall tunnel traffic
+using custom, per-client rules.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--disable</span></kbd></td>
+<td><p class="first">Disable a particular client (based on the common name) from connecting.
+Don't use this option to disable a client due to key or password
+compromise. Use a CRL (certificate revocation list) instead (see the
+<tt class="docutils literal"><span class="pre">--crl-verify</span></tt> option).</p>
+<p class="last">This option must be associated with a specific client instance, which
+means that it must be specified either in a client instance config file
+using <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> or dynamically generated using a
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--connect-freq <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Allow a maximum of <tt class="docutils literal">n</tt> new connections per <tt class="docutils literal">sec</tt> seconds from
+clients.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+connect-freq n sec
+</pre>
+<p>This is designed to contain DoS attacks which flood the server
+with connection requests using certificates which will ultimately fail
+to authenticate.</p>
+<p>This is an imperfect solution however, because in a real DoS scenario,
+legitimate connections might also be refused.</p>
+<p class="last">For the best protection against DoS attacks in server mode, use
+<tt class="docutils literal"><span class="pre">--proto</span> udp</tt> and either <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> or <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt>.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--duplicate-cn</span></kbd></td>
+<td>Allow multiple clients with the same common name to concurrently
+connect. In the absence of this option, OpenVPN will disconnect a client
+instance upon connection of a new client having the same common name.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-pool <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set aside a pool of subnets to be dynamically allocated to connecting
+clients, similar to a DHCP server.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+ifconfig-pool start-IP end-IP [netmask]
+</pre>
+<p class="last">For tun-style tunnels, each client
+will be given a /30 subnet (for interoperability with Windows clients).
+For tap-style tunnels, individual addresses will be allocated, and the
+optional <tt class="docutils literal">netmask</tt> parameter will also be pushed to clients.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-ipv6-pool <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify an IPv6 address pool for dynamic assignment to clients.</p>
+<p>Valid args:</p>
+<pre class="literal-block">
+ifconfig-ipv6-pool ipv6addr/bits
+</pre>
+<p class="last">The pool starts at <tt class="docutils literal">ipv6addr</tt> and matches the offset determined from
+the start of the IPv4 pool. If the host part of the given IPv6
+address is <tt class="docutils literal">0</tt>, the pool starts at <tt class="docutils literal">ipv6addr</tt> +1.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-pool-persist <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Persist/unpersist ifconfig-pool data to <tt class="docutils literal">file</tt>, at <tt class="docutils literal">seconds</tt>
+intervals (default <code>600</code>), as well as on program startup and shutdown.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+ifconfig-pool-persist file [seconds]
+</pre>
+<p>The goal of this option is to provide a long-term association between
+clients (denoted by their common name) and the virtual IP address
+assigned to them from the ifconfig-pool. Maintaining a long-term
+association is good for clients because it allows them to effectively
+use the <tt class="docutils literal"><span class="pre">--persist-tun</span></tt> option.</p>
+<p><tt class="docutils literal">file</tt> is a comma-delimited ASCII file, formatted as
+<code>&lt;Common-Name&gt;,&lt;IP-address&gt;</code>.</p>
+<p>If <tt class="docutils literal">seconds</tt> = <code>0</code>, <tt class="docutils literal">file</tt> will be treated as read-only. This
+is useful if you would like to treat <tt class="docutils literal">file</tt> as a configuration file.</p>
+<p class="last">Note that the entries in this file are treated by OpenVPN as
+<em>suggestions</em> only, based on past associations between a common name and
+IP address. They do not guarantee that the given common name will always
+receive the given IP address. If you want guaranteed assignment, use
+<tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-push <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Push virtual IP endpoints for client tunnel, overriding the
+<tt class="docutils literal"><span class="pre">--ifconfig-pool</span></tt> dynamic allocation.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+ifconfig-push local remote-netmask [alias]
+</pre>
+<p>The parameters <tt class="docutils literal">local</tt> and <tt class="docutils literal"><span class="pre">remote-netmask</span></tt> are set according to the
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> directive which you want to execute on the client machine
+to configure the remote end of the tunnel. Note that the parameters
+<tt class="docutils literal">local</tt> and <tt class="docutils literal"><span class="pre">remote-netmask</span></tt> are from the perspective of the client,
+not the server. They may be DNS names rather than IP addresses, in which
+case they will be resolved on the server at the time of client
+connection.</p>
+<p>The optional <tt class="docutils literal">alias</tt> parameter may be used in cases where NAT causes
+the client view of its local endpoint to differ from the server view. In
+this case <tt class="docutils literal"><span class="pre">local/remote-netmask</span></tt> will refer to the server view while
+<tt class="docutils literal"><span class="pre">alias/remote-netmask</span></tt> will refer to the client view.</p>
+<p>This option must be associated with a specific client instance, which
+means that it must be specified either in a client instance config file
+using <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> or dynamically generated using a
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</p>
+<p>Remember also to include a <tt class="docutils literal"><span class="pre">--route</span></tt> directive in the main OpenVPN
+config file which encloses <tt class="docutils literal">local</tt>, so that the kernel will know to
+route it to the server's TUN/TAP interface.</p>
+<p>OpenVPN's internal client IP address selection algorithm works as
+follows:</p>
+<ol class="last arabic simple">
+<li>Use <tt class="docutils literal"><span class="pre">--client-connect</span> script</tt> generated file for static IP
+(first choice).</li>
+<li>Use <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> file for static IP (next choice).</li>
+<li>Use <tt class="docutils literal"><span class="pre">--ifconfig-pool</span></tt> allocation for dynamic IP (last
+choice).</li>
+</ol>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-ipv6-push <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">for <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> per-client static IPv6 interface
+configuration, see <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> and <tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt> for
+more details.</p>
+<p>Valid syntax:</p>
+<pre class="last literal-block">
+ifconfig-ipv6-push ipv6addr/bits ipv6remote
+</pre>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--inetd <var>args</var></span></kbd></td>
+<td><p class="first">Valid syntaxes:</p>
+<pre class="literal-block">
+inetd
+inetd wait
+inetd nowait
+inetd wait progname
+</pre>
+<p>Use this option when OpenVPN is being run from the inetd or <tt class="docutils literal">xinetd</tt>(8)
+server.</p>
+<p>The <code>wait</code> and <code>nowait</code> option must match what is specified
+in the inetd/xinetd config file. The <code>nowait</code> mode can only be used
+with <tt class="docutils literal"><span class="pre">--proto</span> <span class="pre">tcp-server</span></tt> The default is <code>wait</code>. The
+<code>nowait</code> mode can be used to instantiate the OpenVPN daemon as a
+classic TCP server, where client connection requests are serviced on a
+single port number. For additional information on this kind of
+configuration, see the OpenVPN FAQ:
+<a class="reference external" href="https://community.openvpn.net/openvpn/wiki/325-openvpn-as-a--forking-tcp-server-which-can-service-multiple-clients-over-a-single-tcp-port">https://community.openvpn.net/openvpn/wiki/325-openvpn-as-a--forking-tcp-server-which-can-service-multiple-clients-over-a-single-tcp-port</a></p>
+<p>This option precludes the use of <tt class="docutils literal"><span class="pre">--daemon</span></tt>, <tt class="docutils literal"><span class="pre">--local</span></tt> or
+<tt class="docutils literal"><span class="pre">--remote</span></tt>. Note that this option causes message and error output to
+be handled in the same way as the <tt class="docutils literal"><span class="pre">--daemon</span></tt> option. The optional
+<tt class="docutils literal">progname</tt> parameter is also handled exactly as in <tt class="docutils literal"><span class="pre">--daemon</span></tt>.</p>
+<p class="last">Also note that in <tt class="docutils literal">wait</tt> mode, each OpenVPN tunnel requires a separate
+TCP/UDP port and a separate inetd or xinetd entry. See the OpenVPN 1.x
+HOWTO for an example on using OpenVPN with xinetd:
+<a class="reference external" href="https://openvpn.net/community-resources/1xhowto/">https://openvpn.net/community-resources/1xhowto/</a></p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--multihome</span></kbd></td>
+<td><p class="first">Configure a multi-homed UDP server. This option needs to be used when a
+server has more than one IP address (e.g. multiple interfaces, or
+secondary IP addresses), and is not using <tt class="docutils literal"><span class="pre">--local</span></tt> to force binding
+to one specific address only. This option will add some extra lookups to
+the packet path to ensure that the UDP reply packets are always sent
+from the address that the client is talking to. This is not supported on
+all platforms, and it adds more processing, so it's not enabled by
+default.</p>
+<dl class="last docutils">
+<dt><em>Notes:</em></dt>
+<dd><ul class="first last simple">
+<li>This option is only relevant for UDP servers.</li>
+<li>If you do an IPv6+IPv4 dual-stack bind on a Linux machine with
+multiple IPv4 address, connections to IPv4 addresses will not
+work right on kernels before 3.15, due to missing kernel
+support for the IPv4-mapped case (some distributions have
+ported this to earlier kernel versions, though).</li>
+</ul>
+</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--iroute <var>args</var></span></kbd></td>
+<td><p class="first">Generate an internal route to a specific client. The <tt class="docutils literal">netmask</tt>
+parameter, if omitted, defaults to <code>255.255.255.255</code>.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+iroute network [netmask]
+</pre>
+<p>This directive can be used to route a fixed subnet from the server to a
+particular client, regardless of where the client is connecting from.
+Remember that you must also add the route to the system routing table as
+well (such as by using the <tt class="docutils literal"><span class="pre">--route</span></tt> directive). The reason why two
+routes are needed is that the <tt class="docutils literal"><span class="pre">--route</span></tt> directive routes the packet
+from the kernel to OpenVPN. Once in OpenVPN, the <tt class="docutils literal"><span class="pre">--iroute</span></tt> directive
+routes to the specific client.</p>
+<p>This option must be specified either in a client instance config file
+using <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> or dynamically generated using a
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</p>
+<p class="last">The <tt class="docutils literal"><span class="pre">--iroute</span></tt> directive also has an important interaction with
+<tt class="docutils literal"><span class="pre">--push</span> &quot;route <span class="pre">...&quot;</span></tt>. <tt class="docutils literal"><span class="pre">--iroute</span></tt> essentially defines a subnet which
+is owned by a particular client (we will call this client <em>A</em>). If you
+would like other clients to be able to reach <em>A</em>'s subnet, you can use
+<tt class="docutils literal"><span class="pre">--push</span> &quot;route <span class="pre">...&quot;</span></tt> together with <tt class="docutils literal"><span class="pre">--client-to-client</span></tt> to effect
+this. In order for all clients to see <em>A</em>'s subnet, OpenVPN must push
+this route to all clients EXCEPT for <em>A</em>, since the subnet is already
+owned by <em>A</em>. OpenVPN accomplishes this by not not pushing a route to
+a client if it matches one of the client's iroutes.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--iroute-ipv6 <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">for <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> per-client static IPv6 route configuration,
+see <tt class="docutils literal"><span class="pre">--iroute</span></tt> for more details how to setup and use this, and how
+<tt class="docutils literal"><span class="pre">--iroute</span></tt> and <tt class="docutils literal"><span class="pre">--route</span></tt> interact.</p>
+<p>Valid syntax:</p>
+<pre class="last literal-block">
+iroute-ipv6 ipv6addr/bits
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--max-clients <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Limit server to a maximum of <tt class="docutils literal">n</tt> concurrent clients.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--max-routes-per-client <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Allow a maximum of <tt class="docutils literal">n</tt> internal routes per client (default
+<code>256</code>). This is designed to help contain DoS attacks where an
+authenticated client floods the server with packets appearing to come
+from many unique MAC addresses, forcing the server to deplete virtual
+memory as its internal routing table expands. This directive can be used
+in a <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> file or auto-generated by a
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script to override the global value for a particular
+client.</p>
+<p class="last">Note that this directive affects OpenVPN's internal routing table, not
+the kernel routing table.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--opt-verify</span></kbd></td>
+<td><p class="first">Clients that connect with options that are incompatible with those of the
+server will be disconnected.</p>
+<p>Options that will be compared for compatibility include <tt class="docutils literal"><span class="pre">dev-type</span></tt>,
+<tt class="docutils literal"><span class="pre">link-mtu</span></tt>, <tt class="docutils literal"><span class="pre">tun-mtu</span></tt>, <tt class="docutils literal">proto</tt>, <tt class="docutils literal">ifconfig</tt>,
+<tt class="docutils literal"><span class="pre">comp-lzo</span></tt>, <tt class="docutils literal">fragment</tt>, <tt class="docutils literal">keydir</tt>, <tt class="docutils literal">cipher</tt>,
+<tt class="docutils literal">auth</tt>, <tt class="docutils literal">keysize</tt>, <tt class="docutils literal">secret</tt>, <tt class="docutils literal"><span class="pre">no-replay</span></tt>,
+<tt class="docutils literal"><span class="pre">tls-auth</span></tt>, <tt class="docutils literal"><span class="pre">key-method</span></tt>, <tt class="docutils literal"><span class="pre">tls-server</span></tt>
+and <tt class="docutils literal"><span class="pre">tls-client</span></tt>.</p>
+<p class="last">This option requires that <tt class="docutils literal"><span class="pre">--disable-occ</span></tt> NOT be used.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--port-share <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Share OpenVPN TCP with another service</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+port-share host port [dir]
+</pre>
+<p>When run in TCP server mode, share the OpenVPN port with another
+application, such as an HTTPS server. If OpenVPN senses a connection to
+its port which is using a non-OpenVPN protocol, it will proxy the
+connection to the server at <tt class="docutils literal">host</tt>:<tt class="docutils literal">port</tt>. Currently only designed to
+work with HTTP/HTTPS, though it would be theoretically possible to
+extend to other protocols such as ssh.</p>
+<p><tt class="docutils literal">dir</tt> specifies an optional directory where a temporary file with name
+N containing content C will be dynamically generated for each proxy
+connection, where N is the source IP:port of the client connection and C
+is the source IP:port of the connection to the proxy receiver. This
+directory can be used as a dictionary by the proxy receiver to determine
+the origin of the connection. Each generated file will be automatically
+deleted when the proxied connection is torn down.</p>
+<p class="last">Not implemented on Windows.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--push <var>option</var></span></kbd></td>
+<td><p class="first">Push a config file option back to the client for remote execution. Note
+that <tt class="docutils literal">option</tt> must be enclosed in double quotes (<code>&quot;&quot;</code>). The
+client must specify <tt class="docutils literal"><span class="pre">--pull</span></tt> in its config file. The set of options
+which can be pushed is limited by both feasibility and security. Some
+options such as those which would execute scripts are banned, since they
+would effectively allow a compromised server to execute arbitrary code
+on the client. Other options such as TLS or MTU parameters cannot be
+pushed because the client needs to know them before the connection to the
+server can be initiated.</p>
+<p class="last">This is a partial list of options which can currently be pushed:
+<tt class="docutils literal"><span class="pre">--route</span></tt>, <tt class="docutils literal"><span class="pre">--route-gateway</span></tt>, <tt class="docutils literal"><span class="pre">--route-delay</span></tt>,
+<tt class="docutils literal"><span class="pre">--redirect-gateway</span></tt>, <tt class="docutils literal"><span class="pre">--ip-win32</span></tt>, <tt class="docutils literal"><span class="pre">--dhcp-option</span></tt>,
+<tt class="docutils literal"><span class="pre">--inactive</span></tt>, <tt class="docutils literal"><span class="pre">--ping</span></tt>, <tt class="docutils literal"><span class="pre">--ping-exit</span></tt>, <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>,
+<tt class="docutils literal"><span class="pre">--setenv</span></tt>, <tt class="docutils literal"><span class="pre">--auth-token</span></tt>, <tt class="docutils literal"><span class="pre">--persist-key</span></tt>, <tt class="docutils literal"><span class="pre">--persist-tun</span></tt>,
+<tt class="docutils literal"><span class="pre">--echo</span></tt>, <tt class="docutils literal"><span class="pre">--comp-lzo</span></tt>, <tt class="docutils literal"><span class="pre">--socket-flags</span></tt>, <tt class="docutils literal"><span class="pre">--sndbuf</span></tt>,
+<tt class="docutils literal"><span class="pre">--rcvbuf</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--push-peer-info</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Push additional information about the client to server. The following
+data is always pushed to the server:</p>
+<dl class="docutils">
+<dt><code>IV_VER=&lt;version&gt;</code></dt>
+<dd>The client OpenVPN version</dd>
+<dt><code>IV_PLAT=[linux|solaris|openbsd|mac|netbsd|freebsd|win]</code></dt>
+<dd>The client OS platform</dd>
+<dt><code>IV_LZO_STUB=1</code></dt>
+<dd>If client was built with LZO stub capability</dd>
+<dt><code>IV_LZ4=1</code></dt>
+<dd>If the client supports LZ4 compressions.</dd>
+<dt><code>IV_PROTO</code></dt>
+<dd><p class="first">Details about protocol extensions that the peer supports. The
+variable is a bitfield and the bits are defined as follows
+(starting a bit 0 for the first (unused) bit:</p>
+<ul class="last simple">
+<li>bit 1: The peer supports peer-id floating mechanism</li>
+<li>bit 2: The client expects a push-reply and the server may
+send this reply without waiting for a push-request first.</li>
+</ul>
+</dd>
+<dt><code>IV_NCP=2</code></dt>
+<dd>Negotiable ciphers, client supports <tt class="docutils literal"><span class="pre">--cipher</span></tt> pushed by
+the server, a value of 2 or greater indicates client supports
+<em>AES-GCM-128</em> and <em>AES-GCM-256</em>.</dd>
+<dt><code>IV_CIPHERS=&lt;ncp-ciphers&gt;</code></dt>
+<dd>The client announces the list of supported ciphers configured with the
+<tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> option to the server.</dd>
+<dt><code>IV_GUI_VER=&lt;gui_id&gt; &lt;version&gt;</code></dt>
+<dd>The UI version of a UI if one is running, for example
+<code>de.blinkt.openvpn 0.5.47</code> for the Android app.</dd>
+<dt><code>IV_SSO=[crtext,][openurl,][proxy_url]</code></dt>
+<dd>Additional authentication methods supported by the client.
+This may be set by the client UI/GUI using <tt class="docutils literal"><span class="pre">--setenv</span></tt></dd>
+</dl>
+<p>When <tt class="docutils literal"><span class="pre">--push-peer-info</span></tt> is enabled the additional information consists
+of the following data:</p>
+<dl class="last docutils">
+<dt><code>IV_HWADDR=&lt;string&gt;</code></dt>
+<dd>This is intended to be a unique and persistent ID of the client.
+The string value can be any readable ASCII string up to 64 bytes.
+OpenVPN 2.x and some other implementations use the MAC address of
+the client's interface used to reach the default gateway. If this
+string is generated by the client, it should be consistent and
+preserved across independent session and preferably
+re-installations and upgrades.</dd>
+<dt><code>IV_SSL=&lt;version string&gt;</code></dt>
+<dd>The ssl version used by the client, e.g.
+<code>OpenSSL 1.0.2f 28 Jan 2016</code>.</dd>
+<dt><code>IV_PLAT_VER=x.y</code></dt>
+<dd>The version of the operating system, e.g. 6.1 for Windows 7.</dd>
+<dt><code>UV_&lt;name&gt;=&lt;value&gt;</code></dt>
+<dd>Client environment variables whose names start with
+<code>UV_</code></dd>
+</dl>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--push-remove <var>opt</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Selectively remove all <tt class="docutils literal"><span class="pre">--push</span></tt> options matching &quot;opt&quot; from the option
+list for a client. <tt class="docutils literal">opt</tt> is matched as a substring against the whole
+option string to-be-pushed to the client, so <tt class="docutils literal"><span class="pre">--push-remove</span> route</tt>
+would remove all <tt class="docutils literal"><span class="pre">--push</span> route ...</tt> and <tt class="docutils literal"><span class="pre">--push</span> <span class="pre">route-ipv6</span> ...</tt>
+statements, while <tt class="docutils literal"><span class="pre">--push-remove</span> <span class="pre">&quot;route-ipv6</span> 2001:&quot;</tt> would only remove
+IPv6 routes for <code>2001:...</code> networks.</p>
+<p><tt class="docutils literal"><span class="pre">--push-remove</span></tt> can only be used in a client-specific context, like in
+a <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> file, or <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script or plugin
+-- similar to <tt class="docutils literal"><span class="pre">--push-reset</span></tt>, just more selective.</p>
+<p><em>NOTE</em>: to <em>change</em> an option, <tt class="docutils literal"><span class="pre">--push-remove</span></tt> can be used to first
+remove the old value, and then add a new <tt class="docutils literal"><span class="pre">--push</span></tt> option with the new
+value.</p>
+<p class="last"><em>NOTE 2</em>: due to implementation details, 'ifconfig' and 'ifconfig-ipv6'
+can only be removed with an exact match on the option (
+<code>push-remove ifconfig</code>), no substring matching and no matching on
+the IPv4/IPv6 address argument is possible.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--push-reset</span></kbd></td>
+<td><p class="first">Don't inherit the global push list for a specific client instance.
+Specify this option in a client-specific context such as with a
+<tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> configuration file. This option will ignore
+<tt class="docutils literal"><span class="pre">--push</span></tt> options at the global config file level.</p>
+<p class="last"><em>NOTE</em>: <tt class="docutils literal"><span class="pre">--push-reset</span></tt> is very thorough: it will remove almost
+all options from the list of to-be-pushed options. In many cases,
+some of these options will need to be re-configured afterwards -
+specifically, <tt class="docutils literal"><span class="pre">--topology</span> subnet</tt> and <tt class="docutils literal"><span class="pre">--route-gateway</span></tt> will get
+lost and this will break client configs in many cases. Thus, for most
+purposes, <tt class="docutils literal"><span class="pre">--push-remove</span></tt> is better suited to selectively remove
+push options for individual clients.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--server <var>args</var></span></kbd></td>
+<td><p class="first">A helper directive designed to simplify the configuration of OpenVPN's
+server mode. This directive will set up an OpenVPN server which will
+allocate addresses to clients out of the given network/netmask. The
+server itself will take the <code>.1</code> address of the given network for
+use as the server-side endpoint of the local TUN/TAP interface. If the
+optional <code>nopool</code> flag is given, no dynamic IP address pool will
+prepared for VPN clients.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+server network netmask [nopool]
+</pre>
+<p>For example, <tt class="docutils literal"><span class="pre">--server</span> 10.8.0.0 255.255.255.0</tt> expands as follows:</p>
+<pre class="literal-block">
+mode server
+tls-server
+push &quot;topology [topology]&quot;
+
+if dev tun AND (topology == net30 OR topology == p2p):
+ ifconfig 10.8.0.1 10.8.0.2
+ if !nopool:
+ ifconfig-pool 10.8.0.4 10.8.0.251
+ route 10.8.0.0 255.255.255.0
+ if client-to-client:
+ push &quot;route 10.8.0.0 255.255.255.0&quot;
+ else if topology == net30:
+ push &quot;route 10.8.0.1&quot;
+
+if dev tap OR (dev tun AND topology == subnet):
+ ifconfig 10.8.0.1 255.255.255.0
+ if !nopool:
+ ifconfig-pool 10.8.0.2 10.8.0.253 255.255.255.0
+ push &quot;route-gateway 10.8.0.1&quot;
+ if route-gateway unset:
+ route-gateway 10.8.0.2
+</pre>
+<p class="last">Don't use <tt class="docutils literal"><span class="pre">--server</span></tt> if you are ethernet bridging. Use
+<tt class="docutils literal"><span class="pre">--server-bridge</span></tt> instead.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--server-bridge <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">A helper directive similar to <tt class="docutils literal"><span class="pre">--server</span></tt> which is designed to simplify
+the configuration of OpenVPN's server mode in ethernet bridging
+configurations.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+server-bridge gateway netmask pool-start-IP pool-end-IP
+server-bridge [nogw]
+</pre>
+<p>If <tt class="docutils literal"><span class="pre">--server-bridge</span></tt> is used without any parameters, it will enable a
+DHCP-proxy mode, where connecting OpenVPN clients will receive an IP
+address for their TAP adapter from the DHCP server running on the
+OpenVPN server-side LAN. Note that only clients that support the binding
+of a DHCP client with the TAP adapter (such as Windows) can support this
+mode. The optional <code>nogw</code> flag (advanced) indicates that gateway
+information should not be pushed to the client.</p>
+<p>To configure ethernet bridging, you must first use your OS's bridging
+capability to bridge the TAP interface with the ethernet NIC interface.
+For example, on Linux this is done with the <code>brctl</code> tool, and with
+Windows XP it is done in the Network Connections Panel by selecting the
+ethernet and TAP adapters and right-clicking on &quot;Bridge Connections&quot;.</p>
+<p>Next you you must manually set the IP/netmask on the bridge interface.
+The <tt class="docutils literal">gateway</tt> and <tt class="docutils literal">netmask</tt> parameters to <tt class="docutils literal"><span class="pre">--server-bridge</span></tt> can be
+set to either the IP/netmask of the bridge interface, or the IP/netmask
+of the default gateway/router on the bridged subnet.</p>
+<p>Finally, set aside a IP range in the bridged subnet, denoted by
+<tt class="docutils literal"><span class="pre">pool-start-IP</span></tt> and <tt class="docutils literal"><span class="pre">pool-end-IP</span></tt>, for OpenVPN to allocate to
+connecting clients.</p>
+<p>For example, <tt class="docutils literal"><span class="pre">server-bridge</span> 10.8.0.4 255.255.255.0 10.8.0.128
+10.8.0.254</tt> expands as follows:</p>
+<pre class="literal-block">
+mode server
+tls-server
+
+ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
+push &quot;route-gateway 10.8.0.4&quot;
+</pre>
+<p>In another example, <tt class="docutils literal"><span class="pre">--server-bridge</span></tt> (without parameters) expands as
+follows:</p>
+<pre class="literal-block">
+mode server
+tls-server
+
+push &quot;route-gateway dhcp&quot;
+</pre>
+<p>Or <tt class="docutils literal"><span class="pre">--server-bridge</span> nogw</tt> expands as follows:</p>
+<pre class="last literal-block">
+mode server
+tls-server
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--server-ipv6 <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Convenience-function to enable a number of IPv6 related options at once,
+namely <tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt>, <tt class="docutils literal"><span class="pre">--ifconfig-ipv6-pool</span></tt> and
+<tt class="docutils literal"><span class="pre">--push</span> <span class="pre">tun-ipv6</span></tt>.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+server-ipv6 ipv6addr/bits
+</pre>
+<p class="last">Pushing of the <tt class="docutils literal"><span class="pre">--tun-ipv6</span></tt> directive is done for older clients which
+require an explicit <tt class="docutils literal"><span class="pre">--tun-ipv6</span></tt> in their configuration.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--stale-routes-check <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Remove routes which haven't had activity for <tt class="docutils literal">n</tt> seconds (i.e. the ageing
+time). This check is run every <tt class="docutils literal">t</tt> seconds (i.e. check interval).</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+stale-routes-check n [t]
+</pre>
+<p>If <tt class="docutils literal">t</tt> is not present it defaults to <tt class="docutils literal">n</tt>.</p>
+<p class="last">This option helps to keep the dynamic routing table small. See also
+<tt class="docutils literal"><span class="pre">--max-routes-per-client</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--username-as-common-name</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Use the authenticated username as the common-name, rather than the
+common-name from the client certificate. Requires that some form of
+<tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> verification is in effect. As the replacement happens
+after <tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> verification, the verification script or
+plugin will still receive the common-name from the certificate.</p>
+<p class="last">The common_name environment variable passed to scripts and plugins invoked
+after authentication (e.g, client-connect script) and file names parsed in
+client-config directory will match the username.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--verify-client-cert <var>mode</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify whether the client is required to supply a valid certificate.</p>
+<p>Possible <tt class="docutils literal">mode</tt> options are:</p>
+<dl class="docutils">
+<dt><code>none</code></dt>
+<dd><p class="first">A client certificate is not required. the client needs to
+authenticate using username/password only. Be aware that using this
+directive is less secure than requiring certificates from all
+clients.</p>
+<p>If you use this directive, the entire responsibility of authentication
+will rest on your <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script, so keep in mind
+that bugs in your script could potentially compromise the security of
+your VPN.</p>
+<p class="last"><tt class="docutils literal"><span class="pre">--verify-client-cert</span> none</tt> is functionally equivalent to
+<tt class="docutils literal"><span class="pre">--client-cert-not-required</span></tt>.</p>
+</dd>
+<dt><code>optional</code></dt>
+<dd><p class="first">A client may present a certificate but it is not required to do so.
+When using this directive, you should also use a
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script to ensure that clients are
+authenticated using a certificate, a username and password, or
+possibly even both.</p>
+<p class="last">Again, the entire responsibility of authentication will rest on your
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script, so keep in mind that bugs in your
+script could potentially compromise the security of your VPN.</p>
+</dd>
+<dt><code>require</code></dt>
+<dd>This is the default option. A client is required to present a
+certificate, otherwise VPN access is refused.</dd>
+</dl>
+<p class="last">If you don't use this directive (or use <tt class="docutils literal"><span class="pre">--verify-client-cert</span> require</tt>)
+but you also specify an <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script, then OpenVPN
+will perform double authentication. The client certificate verification
+AND the <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script will need to succeed in order
+for a client to be authenticated and accepted onto the VPN.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--vlan-tagging</span></kbd></td>
+<td><p class="first">Server-only option. Turns the OpenVPN server instance into a switch that
+understands VLAN-tagging, based on IEEE 802.1Q.</p>
+<p>The server TAP device and each of the connecting clients is seen as a
+port of the switch. All client ports are in untagged mode and the server
+TAP device is VLAN-tagged, untagged or accepts both, depending on the
+<tt class="docutils literal"><span class="pre">--vlan-accept</span></tt> setting.</p>
+<p>Ethernet frames with a prepended 802.1Q tag are called &quot;tagged&quot;. If the
+VLAN Identifier (VID) field in such a tag is non-zero, the frame is
+called &quot;VLAN-tagged&quot;. If the VID is zero, but the Priority Control Point
+(PCP) field is non-zero, the frame is called &quot;prio-tagged&quot;. If there is
+no 802.1Q tag, the frame is &quot;untagged&quot;.</p>
+<p>Using the <tt class="docutils literal"><span class="pre">--vlan-pvid</span> v</tt> option once per client (see
+--client-config-dir), each port can be associated with a certain VID.
+Packets can only be forwarded between ports having the same VID.
+Therefore, clients with differing VIDs are completely separated from
+one-another, even if <tt class="docutils literal"><span class="pre">--client-to-client</span></tt> is activated.</p>
+<p>The packet filtering takes place in the OpenVPN server. Clients should
+not have any VLAN tagging configuration applied.</p>
+<p>The <tt class="docutils literal"><span class="pre">--vlan-tagging</span></tt> option is off by default. While turned off,
+OpenVPN accepts any Ethernet frame and does not perform any special
+processing for VLAN-tagged packets.</p>
+<p class="last">This option can only be activated in <tt class="docutils literal"><span class="pre">--dev</span> tap mode</tt>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--vlan-accept <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Configure the VLAN tagging policy for the server TAP device.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+vlan-accept all|tagged|untagged
+</pre>
+<p>The following modes are available:</p>
+<dl class="docutils">
+<dt><code>tagged</code></dt>
+<dd>Admit only VLAN-tagged frames. Only VLAN-tagged packets are accepted,
+while untagged or priority-tagged packets are dropped when entering
+the server TAP device.</dd>
+<dt><code>untagged</code></dt>
+<dd>Admit only untagged and prio-tagged frames. VLAN-tagged packets are
+not accepted, while untagged or priority-tagged packets entering the
+server TAP device are tagged with the value configured for the global
+<tt class="docutils literal"><span class="pre">--vlan-pvid</span></tt> setting.</dd>
+<dt><code>all</code> (default)</dt>
+<dd>Admit all frames. All packets are admitted and then treated like
+untagged or tagged mode respectively.</dd>
+<dt><em>Note</em>:</dt>
+<dd>Some vendors refer to switch ports running in <code>tagged</code> mode
+as &quot;trunk ports&quot; and switch ports running in <code>untagged</code> mode
+as &quot;access ports&quot;.</dd>
+</dl>
+<p>Packets forwarded from clients to the server are VLAN-tagged with the
+originating client's PVID, unless the VID matches the global
+<tt class="docutils literal"><span class="pre">--vlan-pvid</span></tt>, in which case the tag is removed.</p>
+<p class="last">If no <em>PVID</em> is configured for a given client (see --vlan-pvid) packets
+are tagged with 1 by default.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--vlan-pvid <var>v</var></span></kbd></td>
+<td><p class="first">Specifies which VLAN identifier a &quot;port&quot; is associated with. Only valid
+when <tt class="docutils literal"><span class="pre">--vlan-tagging</span></tt> is speficied.</p>
+<p>In the client context, the setting specifies which VLAN ID a client is
+associated with. In the global context, the VLAN ID of the server TAP
+device is set. The latter only makes sense for <tt class="docutils literal"><span class="pre">--vlan-accept</span>
+untagged</tt> and <tt class="docutils literal"><span class="pre">--vlan-accept</span> all</tt> modes.</p>
+<p>Valid values for <tt class="docutils literal">v</tt> go from <code>1</code> through to <code>4094</code>. The
+global value defaults to <code>1</code>. If no <tt class="docutils literal"><span class="pre">--vlan-pvid</span></tt> is specified in
+the client context, the global value is inherited.</p>
+<p class="last">In some switch implementations, the <em>PVID</em> is also referred to as &quot;Native
+VLAN&quot;.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+<div class="section" id="encryption-options">
+<h1>Encryption Options</h1>
+<div class="section" id="ssl-library-information">
+<h2>SSL Library information</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--show-ciphers</span></kbd></td>
+<td>(Standalone) Show all cipher algorithms to use with the <tt class="docutils literal"><span class="pre">--cipher</span></tt>
+option.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-digests</span></kbd></td>
+<td>(Standalone) Show all message digest algorithms to use with the
+<tt class="docutils literal"><span class="pre">--auth</span></tt> option.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-tls</span></kbd></td>
+<td><p class="first">(Standalone) Show all TLS ciphers supported by the crypto library.
+OpenVPN uses TLS to secure the control channel, over which the keys that
+are used to protect the actual VPN traffic are exchanged. The TLS
+ciphers will be sorted from highest preference (most secure) to lowest.</p>
+<p class="last">Be aware that whether a cipher suite in this list can actually work
+depends on the specific setup of both peers (e.g. both peers must
+support the cipher, and an ECDSA cipher suite will not work if you are
+using an RSA certificate, etc.).</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-engines</span></kbd></td>
+<td>(Standalone) Show currently available hardware-based crypto acceleration
+engines supported by the OpenSSL library.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-groups</span></kbd></td>
+<td>(Standalone) Show all available elliptic curves/groups to use with the
+<tt class="docutils literal"><span class="pre">--ecdh-curve</span></tt> and <tt class="docutils literal"><span class="pre">tls-groups</span></tt> options.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="generating-key-material">
+<h2>Generating key material</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--genkey <var>args</var></span></kbd></td>
+<td><p class="first">(Standalone) Generate a key to be used of the type keytype. if keyfile
+is left out or empty the key will be output on stdout. See the following
+sections for the different keytypes.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+--genkey keytype keyfile
+</pre>
+<p>Valid keytype arguments are:</p>
+<p><code>secret</code> Standard OpenVPN shared secret keys</p>
+<p><code>tls-crypt</code> Alias for <code>secret</code></p>
+<p><code>tls-auth</code> Alias for <code>secret</code></p>
+<p><code>auth-token</code> Key used for <tt class="docutils literal"><span class="pre">--auth-gen-token-key</span></tt></p>
+<p><code>tls-crypt-v2-server</code> TLS Crypt v2 server key</p>
+<p><code>tls-crypt-v2-client</code> TLS Crypt v2 client key</p>
+<p>Examples:</p>
+<pre class="literal-block">
+$ openvpn --genkey secret shared.key
+$ openvpn --genkey tls-crypt shared.key
+$ openvpn --genkey tls-auth shared.key
+$ openvpn --genkey tls-crypt-v2-server v2crypt-server.key
+$ openvpn --tls-crypt-v2 v2crypt-server.key --genkey tls-crypt-v2-client v2crypt-client-1.key
+</pre>
+<ul class="last">
+<li><p class="first">Generating <em>Shared Secret Keys</em>
+Generate a shared secret, for use with the <tt class="docutils literal"><span class="pre">--secret</span></tt>, <tt class="docutils literal"><span class="pre">--tls-auth</span></tt>
+or <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> options.</p>
+<p>Syntax:</p>
+<pre class="literal-block">
+$ openvpn --genkey secret|tls-crypt|tls-auth keyfile
+</pre>
+<p>The key is saved in <tt class="docutils literal">keyfile</tt>. All three variants (<tt class="docutils literal"><span class="pre">--secret</span></tt>,
+<tt class="docutils literal"><span class="pre">tls-crypt</span></tt> and <tt class="docutils literal"><span class="pre">tls-auth</span></tt>) generate the same type of key. The
+aliases are added for convenience.</p>
+<p>If using this for <tt class="docutils literal"><span class="pre">--secret</span></tt>, this file must be shared with the peer
+over a pre-existing secure channel such as <tt class="docutils literal">scp</tt>(1).</p>
+</li>
+<li><p class="first">Generating <em>TLS Crypt v2 Server key</em>
+Generate a <tt class="docutils literal"><span class="pre">--tls-crypt-v2</span></tt> key to be used by an OpenVPN server.
+The key is stored in <tt class="docutils literal">keyfile</tt>.</p>
+<p>Syntax:</p>
+<pre class="literal-block">
+--genkey tls-crypt-v2-server keyfile
+</pre>
+</li>
+<li><p class="first">Generating <em>TLS Crypt v2 Client key</em>
+Generate a --tls-crypt-v2 key to be used by OpenVPN clients. The
+key is stored in <tt class="docutils literal">keyfile</tt>.</p>
+<p>Syntax</p>
+<pre class="literal-block">
+--genkey tls-crypt-v2-client keyfile [metadata]
+</pre>
+<p>If supplied, include the supplied <tt class="docutils literal">metadata</tt> in the wrapped client
+key. This metadata must be supplied in base64-encoded form. The
+metadata must be at most 735 bytes long (980 bytes in base64).</p>
+<p>If no metadata is supplied, OpenVPN will use a 64-bit unix timestamp
+representing the current time in UTC, encoded in network order, as
+metadata for the generated key.</p>
+<p>A tls-crypt-v2 client key is wrapped using a server key. To generate a
+client key, the user must therefore supply the server key using the
+<tt class="docutils literal"><span class="pre">--tls-crypt-v2</span></tt> option.</p>
+<p>Servers can use <tt class="docutils literal"><span class="pre">--tls-crypt-v2-verify</span></tt> to specify a metadata
+verification command.</p>
+</li>
+<li><p class="first">Generate <em>Authentication Token key</em>
+Generate a new secret that can be used with <strong>--auth-gen-token-secret</strong></p>
+<p>Syntax:</p>
+<pre class="literal-block">
+--genkey auth-token [keyfile]
+</pre>
+<dl class="docutils">
+<dt><em>Note:</em></dt>
+<dd><p class="first last">This file should be kept secret to the server as anyone that has
+access to this file will be able to generate auth tokens that the
+OpenVPN server will accept as valid.</p>
+</dd>
+</dl>
+</li>
+</ul>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="data-channel-renegotiation">
+<h2>Data Channel Renegotiation</h2>
+<p>When running OpenVPN in client/server mode, the data channel will use a
+separate ephemeral encryption key which is rotated at regular intervals.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--reneg-bytes <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Renegotiate data channel key after <tt class="docutils literal">n</tt> bytes sent or received
+(disabled by default with an exception, see below). OpenVPN allows the
+lifetime of a key to be expressed as a number of bytes
+encrypted/decrypted, a number of packets, or a number of seconds. A key
+renegotiation will be forced if any of these three criteria are met by
+either peer.</p>
+<p class="last">If using ciphers with cipher block sizes less than 128-bits,
+<tt class="docutils literal"><span class="pre">--reneg-bytes</span></tt> is set to 64MB by default, unless it is explicitly
+disabled by setting the value to <code>0</code>, but this is
+<strong>HIGHLY DISCOURAGED</strong> as this is designed to add some protection against
+the SWEET32 attack vector. For more information see the <tt class="docutils literal"><span class="pre">--cipher</span></tt>
+option.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--reneg-pkts <var>n</var></span></kbd></td>
+<td>Renegotiate data channel key after <strong>n</strong> packets sent and received
+(disabled by default).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--reneg-sec <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Renegotiate data channel key after at most <tt class="docutils literal">max</tt> seconds
+(default <code>3600</code>) and at least <tt class="docutils literal">min</tt> seconds (default is 90% of
+<tt class="docutils literal">max</tt> for servers, and equal to <tt class="docutils literal">max</tt> for clients).</p>
+<pre class="literal-block">
+reneg-sec max [min]
+</pre>
+<p>The effective <tt class="docutils literal"><span class="pre">--reneg-sec</span></tt> value used is per session
+pseudo-uniform-randomized between <tt class="docutils literal">min</tt> and <tt class="docutils literal">max</tt>.</p>
+<p>With the default value of <code>3600</code> this results in an effective per
+session value in the range of <code>3240</code> .. <code>3600</code> seconds for
+servers, or just 3600 for clients.</p>
+<p>When using dual-factor authentication, note that this default value may
+cause the end user to be challenged to reauthorize once per hour.</p>
+<p class="last">Also, keep in mind that this option can be used on both the client and
+server, and whichever uses the lower value will be the one to trigger
+the renegotiation. A common mistake is to set <tt class="docutils literal"><span class="pre">--reneg-sec</span></tt> to a
+higher value on either the client or server, while the other side of the
+connection is still using the default value of <code>3600</code> seconds,
+meaning that the renegotiation will still occur once per <code>3600</code>
+seconds. The solution is to increase --reneg-sec on both the client and
+server, or set it to <code>0</code> on one side of the connection (to
+disable), and to your chosen value on the other side.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="tls-mode-options">
+<h2>TLS Mode Options</h2>
+<p>TLS mode is the most powerful crypto mode of OpenVPN in both security
+and flexibility. TLS mode works by establishing control and data
+channels which are multiplexed over a single TCP/UDP port. OpenVPN
+initiates a TLS session over the control channel and uses it to exchange
+cipher and HMAC keys to protect the data channel. TLS mode uses a robust
+reliability layer over the UDP connection for all control channel
+communication, while the data channel, over which encrypted tunnel data
+passes, is forwarded without any mediation. The result is the best of
+both worlds: a fast data channel that forwards over UDP with only the
+overhead of encrypt, decrypt, and HMAC functions, and a control channel
+that provides all of the security features of TLS, including
+certificate-based authentication and Diffie Hellman forward secrecy.</p>
+<p>To use TLS mode, each peer that runs OpenVPN should have its own local
+certificate/key pair (<tt class="docutils literal"><span class="pre">--cert</span></tt> and <tt class="docutils literal"><span class="pre">--key</span></tt>), signed by the root
+certificate which is specified in <tt class="docutils literal"><span class="pre">--ca</span></tt>.</p>
+<p>When two OpenVPN peers connect, each presents its local certificate to
+the other. Each peer will then check that its partner peer presented a
+certificate which was signed by the master root certificate as specified
+in <tt class="docutils literal"><span class="pre">--ca</span></tt>.</p>
+<p>If that check on both peers succeeds, then the TLS negotiation will
+succeed, both OpenVPN peers will exchange temporary session keys, and
+the tunnel will begin passing data.</p>
+<p>The OpenVPN project provides a set of scripts for managing RSA
+certificates and keys: <a class="reference external" href="https://github.com/OpenVPN/easy-rsa">https://github.com/OpenVPN/easy-rsa</a></p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--askpass <var>file</var></span></kbd></td>
+<td><p class="first">Get certificate password from console or <tt class="docutils literal">file</tt> before we daemonize.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+askpass
+askpass file
+</pre>
+<p>For the extremely security conscious, it is possible to protect your
+private key with a password. Of course this means that every time the
+OpenVPN daemon is started you must be there to type the password. The
+<tt class="docutils literal"><span class="pre">--askpass</span></tt> option allows you to start OpenVPN from the command line.
+It will query you for a password before it daemonizes. To protect a
+private key with a password you should omit the <tt class="docutils literal"><span class="pre">-nodes</span></tt> option when
+you use the <tt class="docutils literal">openssl</tt> command line tool to manage certificates and
+private keys.</p>
+<p class="last">If <tt class="docutils literal">file</tt> is specified, read the password from the first line of
+<tt class="docutils literal">file</tt>. Keep in mind that storing your password in a file to a certain
+extent invalidates the extra security provided by using an encrypted
+key.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ca <var>file</var></span></kbd></td>
+<td><p class="first">Certificate authority (CA) file in .pem format, also referred to as the
+<em>root</em> certificate. This file can have multiple certificates in .pem
+format, concatenated together. You can construct your own certificate
+authority certificate and private key by using a command such as:</p>
+<pre class="literal-block">
+openssl req -nodes -new -x509 -keyout ca.key -out ca.crt
+</pre>
+<p>Then edit your openssl.cnf file and edit the <tt class="docutils literal">certificate</tt> variable to
+point to your new root certificate <tt class="docutils literal">ca.crt</tt>.</p>
+<p class="last">For testing purposes only, the OpenVPN distribution includes a sample CA
+certificate (ca.crt). Of course you should never use the test
+certificates and test keys distributed with OpenVPN in a production
+environment, since by virtue of the fact that they are distributed with
+OpenVPN, they are totally insecure.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--capath <var>dir</var></span></kbd></td>
+<td><p class="first">Directory containing trusted certificates (CAs and CRLs). Not available
+with mbed TLS.</p>
+<p>CAs in the capath directory are expected to be named &lt;hash&gt;.&lt;n&gt;. CRLs
+are expected to be named &lt;hash&gt;.r&lt;n&gt;. See the <tt class="docutils literal"><span class="pre">-CApath</span></tt> option of
+<tt class="docutils literal">openssl verify</tt>, and the <tt class="docutils literal"><span class="pre">-hash</span></tt> option of <tt class="docutils literal">openssl x509</tt>,
+<tt class="docutils literal">openssl crl</tt> and <tt class="docutils literal">X509_LOOKUP_hash_dir()</tt>(3)
+for more information.</p>
+<p class="last">Similar to the <tt class="docutils literal"><span class="pre">--crl-verify</span></tt> option, CRLs are not mandatory -
+OpenVPN will log the usual warning in the logs if the relevant CRL is
+missing, but the connection will be allowed.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--cert <var>file</var></span></kbd></td>
+<td><p class="first">Local peer's signed certificate in .pem format -- must be signed by a
+certificate authority whose certificate is in <tt class="docutils literal"><span class="pre">--ca</span> file</tt>. Each peer
+in an OpenVPN link running in TLS mode should have its own certificate
+and private key file. In addition, each certificate should have been
+signed by the key of a certificate authority whose public key resides in
+the <tt class="docutils literal"><span class="pre">--ca</span></tt> certificate authority file. You can easily make your own
+certificate authority (see above) or pay money to use a commercial
+service such as thawte.com (in which case you will be helping to finance
+the world's second space tourist :). To generate a certificate, you can
+use a command such as:</p>
+<pre class="literal-block">
+openssl req -nodes -new -keyout mycert.key -out mycert.csr
+</pre>
+<p>If your certificate authority private key lives on another machine, copy
+the certificate signing request (mycert.csr) to this other machine (this
+can be done over an insecure channel such as email). Now sign the
+certificate with a command such as:</p>
+<pre class="literal-block">
+openssl ca -out mycert.crt -in mycert.csr
+</pre>
+<p class="last">Now copy the certificate (mycert.crt) back to the peer which initially
+generated the .csr file (this can be over a public medium). Note that
+the <tt class="docutils literal">openssl ca</tt> command reads the location of the certificate
+authority key from its configuration file such as
+<code>/usr/share/ssl/openssl.cnf</code> -- note also that for certificate
+authority functions, you must set up the files <code>index.txt</code> (may be
+empty) and <code>serial</code> (initialize to <code>01</code>).</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--crl-verify <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Check peer certificate against a Certificate Revocation List.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+crl-verify file/directory flag
+</pre>
+<p>Examples:</p>
+<pre class="literal-block">
+crl-verify crl-file.pem
+crl-verify /etc/openvpn/crls dir
+</pre>
+<p>A CRL (certificate revocation list) is used when a particular key is
+compromised but when the overall PKI is still intact.</p>
+<p>Suppose you had a PKI consisting of a CA, root certificate, and a number
+of client certificates. Suppose a laptop computer containing a client
+key and certificate was stolen. By adding the stolen certificate to the
+CRL file, you could reject any connection which attempts to use it,
+while preserving the overall integrity of the PKI.</p>
+<p>The only time when it would be necessary to rebuild the entire PKI from
+scratch would be if the root certificate key itself was compromised.</p>
+<p>The option is not mandatory - if the relevant CRL is missing, OpenVPN
+will log a warning in the logs - e.g.</p>
+<pre class="literal-block">
+VERIFY WARNING: depth=0, unable to get certificate CRL
+</pre>
+<p>but the connection will be allowed. If the optional <code>dir</code> flag
+is specified, enable a different mode where the <tt class="docutils literal"><span class="pre">crl-verify</span></tt> is
+pointed at a directory containing files named as revoked serial numbers
+(the files may be empty, the contents are never read). If a client
+requests a connection, where the client certificate serial number
+(decimal string) is the name of a file present in the directory, it will
+be rejected.</p>
+<dl class="last docutils">
+<dt><em>Note:</em></dt>
+<dd>As the crl file (or directory) is read every time a peer
+connects, if you are dropping root privileges with
+<tt class="docutils literal"><span class="pre">--user</span></tt>, make sure that this user has sufficient
+privileges to read the file.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--dh <var>file</var></span></kbd></td>
+<td><p class="first">File containing Diffie Hellman parameters in .pem format (required for
+<tt class="docutils literal"><span class="pre">--tls-server</span></tt> only).</p>
+<p>Set <tt class="docutils literal">file</tt> to <code>none</code> to disable Diffie Hellman key exchange (and
+use ECDH only). Note that this requires peers to be using an SSL library
+that supports ECDH TLS cipher suites (e.g. OpenSSL 1.0.1+, or
+mbed TLS 2.0+).</p>
+<p class="last">Use <tt class="docutils literal">openssl dhparam <span class="pre">-out</span> dh2048.pem 2048</tt> to generate 2048-bit DH
+parameters. Diffie Hellman parameters may be considered public.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ecdh-curve <var>name</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify the curve to use for elliptic curve Diffie Hellman. Available
+curves can be listed with <tt class="docutils literal"><span class="pre">--show-curves</span></tt>. The specified curve will
+only be used for ECDH TLS-ciphers.</p>
+<p class="last">This option is not supported in mbed TLS builds of OpenVPN.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--extra-certs <var>file</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify a <tt class="docutils literal">file</tt> containing one or more PEM certs (concatenated
+together) that complete the local certificate chain.</p>
+<p class="last">This option is useful for &quot;split&quot; CAs, where the CA for server certs is
+different than the CA for client certs. Putting certs in this file
+allows them to be used to complete the local certificate chain without
+trusting them to verify the peer-submitted certificate, as would be the
+case if the certs were placed in the <tt class="docutils literal">ca</tt> file.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--hand-window <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Handshake Window -- the TLS-based key exchange must finalize within
+<tt class="docutils literal">n</tt> seconds of handshake initiation by any peer (default <code>60</code>
+seconds). If the handshake fails we will attempt to reset our connection
+with our peer and try again. Even in the event of handshake failure we
+will still use our expiring key for up to <tt class="docutils literal"><span class="pre">--tran-window</span></tt> seconds to
+maintain continuity of transmission of tunnel data.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--key <var>file</var></span></kbd></td>
+<td>Local peer's private key in .pem format. Use the private key which was
+generated when you built your peer's certificate (see <tt class="docutils literal"><span class="pre">--cert</span> file</tt>
+above).</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--pkcs12 <var>file</var></span></kbd></td>
+<td>Specify a PKCS #12 file containing local private key, local certificate,
+and root CA certificate. This option can be used instead of <tt class="docutils literal"><span class="pre">--ca</span></tt>,
+<tt class="docutils literal"><span class="pre">--cert</span></tt>, and <tt class="docutils literal"><span class="pre">--key</span></tt>. Not available with mbed TLS.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remote-cert-eku <var>oid</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Require that peer certificate was signed with an explicit <em>extended key
+usage</em>.</p>
+<p>This is a useful security option for clients, to ensure that the host
+they connect to is a designated server.</p>
+<p class="last">The extended key usage should be encoded in <em>oid notation</em>, or <em>OpenSSL
+symbolic representation</em>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remote-cert-ku <var>key-usage</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Require that peer certificate was signed with an explicit
+<tt class="docutils literal"><span class="pre">key-usage</span></tt>.</p>
+<p>If present in the certificate, the <code>keyUsage</code> value is validated by
+the TLS library during the TLS handshake. Specifying this option without
+arguments requires this extension to be present (so the TLS library will
+verify it).</p>
+<p>If <tt class="docutils literal"><span class="pre">key-usage</span></tt> is a list of usage bits, the <code>keyUsage</code> field
+must have <em>at least</em> the same bits set as the bits in <em>one of</em> the values
+supplied in the <tt class="docutils literal"><span class="pre">key-usage</span></tt> list.</p>
+<p>The <tt class="docutils literal"><span class="pre">key-usage</span></tt> values in the list must be encoded in hex, e.g.</p>
+<pre class="last literal-block">
+remote-cert-ku a0
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--remote-cert-tls <var>type</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Require that peer certificate was signed with an explicit <em>key usage</em>
+and <em>extended key usage</em> based on RFC3280 TLS rules.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+remote-cert-tls server
+remote-cert-tls client
+</pre>
+<p>This is a useful security option for clients, to ensure that the host
+they connect to is a designated server. Or the other way around; for a
+server to verify that only hosts with a client certificate can connect.</p>
+<p>The <tt class="docutils literal"><span class="pre">--remote-cert-tls</span> client</tt> option is equivalent to</p>
+<pre class="literal-block">
+remote-cert-ku
+remote-cert-eku &quot;TLS Web Client Authentication&quot;
+</pre>
+<p>The <tt class="docutils literal"><span class="pre">--remote-cert-tls</span> server</tt> option is equivalent to</p>
+<pre class="literal-block">
+remote-cert-ku
+remote-cert-eku &quot;TLS Web Server Authentication&quot;
+</pre>
+<p class="last">This is an important security precaution to protect against a
+man-in-the-middle attack where an authorized client attempts to connect
+to another client by impersonating the server. The attack is easily
+prevented by having clients verify the server certificate using any one
+of <tt class="docutils literal"><span class="pre">--remote-cert-tls</span></tt>, <tt class="docutils literal"><span class="pre">--verify-x509-name</span></tt>, or <tt class="docutils literal"><span class="pre">--tls-verify</span></tt>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-auth <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Add an additional layer of HMAC authentication on top of the TLS control
+channel to mitigate DoS attacks and attacks on the TLS stack.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+tls-auth file
+tls-auth file 0
+tls-auth file 1
+</pre>
+<p>In a nutshell, <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> enables a kind of &quot;HMAC firewall&quot; on
+OpenVPN's TCP/UDP port, where TLS control channel packets bearing an
+incorrect HMAC signature can be dropped immediately without response.</p>
+<p><tt class="docutils literal">file</tt> (required) is a file in OpenVPN static key format which can be
+generated by <tt class="docutils literal"><span class="pre">--genkey</span></tt>.</p>
+<p>Older versions (up to OpenVPN 2.3) supported a freeform passphrase file.
+This is no longer supported in newer versions (v2.4+).</p>
+<p>See the <tt class="docutils literal"><span class="pre">--secret</span></tt> option for more information on the optional
+<tt class="docutils literal">direction</tt> parameter.</p>
+<p><tt class="docutils literal"><span class="pre">--tls-auth</span></tt> is recommended when you are running OpenVPN in a mode
+where it is listening for packets from any IP address, such as when
+<tt class="docutils literal"><span class="pre">--remote</span></tt> is not specified, or <tt class="docutils literal"><span class="pre">--remote</span></tt> is specified with
+<tt class="docutils literal"><span class="pre">--float</span></tt>.</p>
+<p>The rationale for this feature is as follows. TLS requires a
+multi-packet exchange before it is able to authenticate a peer. During
+this time before authentication, OpenVPN is allocating resources (memory
+and CPU) to this potential peer. The potential peer is also exposing
+many parts of OpenVPN and the OpenSSL library to the packets it is
+sending. Most successful network attacks today seek to either exploit
+bugs in programs (such as buffer overflow attacks) or force a program to
+consume so many resources that it becomes unusable. Of course the first
+line of defense is always to produce clean, well-audited code. OpenVPN
+has been written with buffer overflow attack prevention as a top
+priority. But as history has shown, many of the most widely used network
+applications have, from time to time, fallen to buffer overflow attacks.</p>
+<p>So as a second line of defense, OpenVPN offers this special layer of
+authentication on top of the TLS control channel so that every packet on
+the control channel is authenticated by an HMAC signature and a unique
+ID for replay protection. This signature will also help protect against
+DoS (Denial of Service) attacks. An important rule of thumb in reducing
+vulnerability to DoS attacks is to minimize the amount of resources a
+potential, but as yet unauthenticated, client is able to consume.</p>
+<p><tt class="docutils literal"><span class="pre">--tls-auth</span></tt> does this by signing every TLS control channel packet
+with an HMAC signature, including packets which are sent before the TLS
+level has had a chance to authenticate the peer. The result is that
+packets without the correct signature can be dropped immediately upon
+reception, before they have a chance to consume additional system
+resources such as by initiating a TLS handshake. <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> can be
+strengthened by adding the <tt class="docutils literal"><span class="pre">--replay-persist</span></tt> option which will keep
+OpenVPN's replay protection state in a file so that it is not lost
+across restarts.</p>
+<p>It should be emphasized that this feature is optional and that the key
+file used with <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> gives a peer nothing more than the power
+to initiate a TLS handshake. It is not used to encrypt or authenticate
+any tunnel data.</p>
+<p class="last">Use <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> instead if you want to use the key file to not only
+authenticate, but also encrypt the TLS control channel.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-groups <var>list</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">A list of allowable groups/curves in order of preference.</p>
+<p>Set the allowed elliptic curves/groups for the TLS session.
+These groups are allowed to be used in signatures and key exchange.</p>
+<p>mbedTLS currently allows all known curves per default.</p>
+<p>OpenSSL 1.1+ restricts the list per default to</p>
+<pre class="literal-block">
+&quot;X25519:secp256r1:X448:secp521r1:secp384r1&quot;.
+</pre>
+<p>If you use certificates that use non-standard curves, you
+might need to add them here. If you do not force the ecdh curve
+by using <tt class="docutils literal"><span class="pre">--ecdh-curve</span></tt>, the groups for ecdh will also be picked
+from this list.</p>
+<p>OpenVPN maps the curve name <cite>secp256r1</cite> to <cite>prime256v1</cite> to allow
+specifying the same tls-groups option for mbedTLS and OpenSSL.</p>
+<p class="last">Warning: this option not only affects elliptic curve certificates
+but also the key exchange in TLS 1.3 and using this option improperly
+will disable TLS 1.3.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-cert-profile <var>profile</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set the allowed cryptographic algorithms for certificates according to
+<tt class="docutils literal">profile</tt>.</p>
+<p>The following profiles are supported:</p>
+<dl class="docutils">
+<dt><code>legacy</code> (default)</dt>
+<dd>SHA1 and newer, RSA 2048-bit+, any elliptic curve.</dd>
+<dt><code>preferred</code></dt>
+<dd>SHA2 and newer, RSA 2048-bit+, any elliptic curve.</dd>
+<dt><code>suiteb</code></dt>
+<dd>SHA256/SHA384, ECDSA with P-256 or P-384.</dd>
+</dl>
+<p>This option is only fully supported for mbed TLS builds. OpenSSL builds
+use the following approximation:</p>
+<dl class="docutils">
+<dt><code>legacy</code> (default)</dt>
+<dd>sets &quot;security level 1&quot;</dd>
+<dt><code>preferred</code></dt>
+<dd>sets &quot;security level 2&quot;</dd>
+<dt><code>suiteb</code></dt>
+<dd>sets &quot;security level 3&quot; and <tt class="docutils literal"><span class="pre">--tls-cipher</span> &quot;SUITEB128&quot;</tt>.</dd>
+</dl>
+<p class="last">OpenVPN will migrate to 'preferred' as default in the future. Please
+ensure that your keys already comply.</p>
+</td></tr>
+</tbody>
+</table>
+<dl class="docutils">
+<dt><em>WARNING:</em> <tt class="docutils literal"><span class="pre">--tls-ciphers</span></tt>, <tt class="docutils literal"><span class="pre">--tls-ciphersuites</span></tt> and <tt class="docutils literal"><span class="pre">tls-groups</span></tt></dt>
+<dd>These options are expert features, which - if used correctly - can
+improve the security of your VPN connection. But it is also easy to
+unwittingly use them to carefully align a gun with your foot, or just
+break your connection. Use with care!</dd>
+</dl>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--tls-cipher <var>l</var></span></kbd></td>
+<td><p class="first">A list <tt class="docutils literal">l</tt> of allowable TLS ciphers delimited by a colon (&quot;<code>:</code>&quot;).</p>
+<p>These setting can be used to ensure that certain cipher suites are used
+(or not used) for the TLS connection. OpenVPN uses TLS to secure the
+control channel, over which the keys that are used to protect the actual
+VPN traffic are exchanged.</p>
+<p>The supplied list of ciphers is (after potential OpenSSL/IANA name
+translation) simply supplied to the crypto library. Please see the
+OpenSSL and/or mbed TLS documentation for details on the cipher list
+interpretation.</p>
+<p>For OpenSSL, the <tt class="docutils literal"><span class="pre">--tls-cipher</span></tt> is used for TLS 1.2 and below.</p>
+<p>Use <tt class="docutils literal"><span class="pre">--show-tls</span></tt> to see a list of TLS ciphers supported by your crypto
+library.</p>
+<p class="last">The default for <tt class="docutils literal"><span class="pre">--tls-cipher</span></tt> is to use mbed TLS's default cipher list
+when using mbed TLS or
+<code>DEFAULT:!EXP:!LOW:!MEDIUM:!kDH:!kECDH:!DSS:!PSK:!SRP:!kRSA</code> when
+using OpenSSL.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-ciphersuites <var>l</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Same as <tt class="docutils literal"><span class="pre">--tls-cipher</span></tt> but for TLS 1.3 and up. mbed TLS has no
+TLS 1.3 support yet and only the <tt class="docutils literal"><span class="pre">--tls-cipher</span></tt> setting is used.</p>
+<p class="last">The default for <cite>--tls-ciphersuites</cite> is to use the crypto library's
+default.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tls-client</span></kbd></td>
+<td>Enable TLS and assume client role during TLS handshake.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-crypt <var>keyfile</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Encrypt and authenticate all control channel packets with the key from
+<tt class="docutils literal">keyfile</tt>. (See <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> for more background.)</p>
+<p>Encrypting (and authenticating) control channel packets:</p>
+<ul class="simple">
+<li>provides more privacy by hiding the certificate used for the TLS
+connection,</li>
+<li>makes it harder to identify OpenVPN traffic as such,</li>
+<li>provides &quot;poor-man's&quot; post-quantum security, against attackers who will
+never know the pre-shared key (i.e. no forward secrecy).</li>
+</ul>
+<p>In contrast to <tt class="docutils literal"><span class="pre">--tls-auth</span></tt>, <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> does <em>not</em> require the
+user to set <tt class="docutils literal"><span class="pre">--key-direction</span></tt>.</p>
+<p><strong>Security Considerations</strong></p>
+<p>All peers use the same <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> pre-shared group key to
+authenticate and encrypt control channel messages. To ensure that IV
+collisions remain unlikely, this key should not be used to encrypt more
+than 2^48 client-to-server or 2^48 server-to-client control channel
+messages. A typical initial negotiation is about 10 packets in each
+direction. Assuming both initial negotiation and renegotiations are at
+most 2^16 (65536) packets (to be conservative), and (re)negotiations
+happen each minute for each user (24/7), this limits the tls-crypt key
+lifetime to 8171 years divided by the number of users. So a setup with
+1000 users should rotate the key at least once each eight years. (And a
+setup with 8000 users each year.)</p>
+<p>If IV collisions were to occur, this could result in the security of
+<tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> degrading to the same security as using <tt class="docutils literal"><span class="pre">--tls-auth</span></tt>.
+That is, the control channel still benefits from the extra protection
+against active man-in-the-middle-attacks and DoS attacks, but may no
+longer offer extra privacy and post-quantum security on top of what TLS
+itself offers.</p>
+<p class="last">For large setups or setups where clients are not trusted, consider using
+<tt class="docutils literal"><span class="pre">--tls-crypt-v2</span></tt> instead. That uses per-client unique keys, and
+thereby improves the bounds to 'rotate a client key at least once per
+8000 years'.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-crypt-v2 <var>keyfile</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Use client-specific tls-crypt keys.</p>
+<p>For clients, <tt class="docutils literal">keyfile</tt> is a client-specific tls-crypt key. Such a key
+can be generated using the <code>--genkey tls-crypt-v2-client</code> option.</p>
+<p>For servers, <tt class="docutils literal">keyfile</tt> is used to unwrap client-specific keys supplied
+by the client during connection setup. This key must be the same as the
+key used to generate the client-specific key (see <code>--genkey
+tls-crypt-v2-client</code>).</p>
+<p class="last">On servers, this option can be used together with the <tt class="docutils literal"><span class="pre">--tls-auth</span></tt> or
+<tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> option. In that case, the server will detect whether the
+client is using client-specific keys, and automatically select the right
+mode.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-crypt-v2-verify <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Run command <tt class="docutils literal">cmd</tt> to verify the metadata of the client-specific
+tls-crypt-v2 key of a connecting client. This allows server
+administrators to reject client connections, before exposing the TLS
+stack (including the notoriously dangerous X.509 and ASN.1 stacks) to
+the connecting client.</p>
+<p>OpenVPN supplies the following environment variables to the command:</p>
+<ul class="simple">
+<li><code>script_type</code> is set to <code>tls-crypt-v2-verify</code></li>
+<li><code>metadata_type</code> is set to <code>0</code> if the metadata was user
+supplied, or <code>1</code> if it's a 64-bit unix timestamp representing
+the key creation time.</li>
+<li><code>metadata_file</code> contains the filename of a temporary file that
+contains the client metadata.</li>
+</ul>
+<p class="last">The command can reject the connection by exiting with a non-zero exit
+code.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tls-exit</span></kbd></td>
+<td>Exit on TLS negotiation failure.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-export-cert <var>directory</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Store the certificates the clients use upon connection to this
+directory. This will be done before <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> is called. The
+certificates will use a temporary name and will be deleted when the
+tls-verify script returns. The file name used for the certificate is
+available via the <tt class="docutils literal">peer_cert</tt> environment variable.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tls-server</span></kbd></td>
+<td>Enable TLS and assume server role during TLS handshake. Note that
+OpenVPN is designed as a peer-to-peer application. The designation of
+client or server is only for the purpose of negotiating the TLS control
+channel.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-timeout <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Packet retransmit timeout on TLS control channel if no acknowledgment
+from remote within <tt class="docutils literal">n</tt> seconds (default <code>2</code>). When OpenVPN sends
+a control packet to its peer, it will expect to receive an
+acknowledgement within <tt class="docutils literal">n</tt> seconds or it will retransmit the packet,
+subject to a TCP-like exponential backoff algorithm. This parameter only
+applies to control channel packets. Data channel packets (which carry
+encrypted tunnel data) are never acknowledged, sequenced, or
+retransmitted by OpenVPN because the higher level network protocols
+running on top of the tunnel such as TCP expect this role to be left to
+them.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-version-min <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Sets the minimum TLS version we will accept from the peer (default is
+&quot;1.0&quot;).</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+tls-version-min version ['or-highest']
+</pre>
+<p class="last">Examples for version include <code>1.0</code>, <code>1.1</code>, or <code>1.2</code>. If
+<code>or-highest</code> is specified and version is not recognized, we will
+only accept the highest TLS version supported by the local SSL
+implementation.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-version-max <var>version</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Set the maximum TLS version we will use (default is the highest version
+supported). Examples for version include <code>1.0</code>, <code>1.1</code>, or
+<code>1.2</code>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--verify-hash <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify SHA1 or SHA256 fingerprint for level-1 cert.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+verify-hash hash [algo]
+</pre>
+<p>The level-1 cert is the CA (or intermediate cert) that signs the leaf
+certificate, and is one removed from the leaf certificate in the
+direction of the root. When accepting a connection from a peer, the
+level-1 cert fingerprint must match <tt class="docutils literal">hash</tt> or certificate verification
+will fail. Hash is specified as XX:XX:... For example:</p>
+<pre class="literal-block">
+AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+</pre>
+<p class="last">The <tt class="docutils literal">algo</tt> flag can be either <code>SHA1</code> or <code>SHA256</code>. If not
+provided, it defaults to <code>SHA1</code>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--verify-x509-name <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Accept connections only if a host's X.509 name is equal to <strong>name.</strong> The
+remote host must also pass all other tests of verification.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+verify-x509 name type
+</pre>
+<p>Which X.509 name is compared to <tt class="docutils literal">name</tt> depends on the setting of type.
+<tt class="docutils literal">type</tt> can be <code>subject</code> to match the complete subject DN
+(default), <code>name</code> to match a subject RDN or <code>name-prefix</code> to
+match a subject RDN prefix. Which RDN is verified as name depends on the
+<tt class="docutils literal"><span class="pre">--x509-username-field</span></tt> option. But it defaults to the common name
+(CN), e.g. a certificate with a subject DN</p>
+<pre class="literal-block">
+C=KG, ST=NA, L=Bishkek, CN=Server-1
+</pre>
+<p>would be matched by:</p>
+<pre class="literal-block">
+verify-x509-name 'C=KG, ST=NA, L=Bishkek, CN=Server-1'
+verify-x509-name Server-1 name
+verify-x509-name Server- name-prefix
+</pre>
+<p>The last example is useful if you want a client to only accept
+connections to <code>Server-1</code>, <code>Server-2</code>, etc.</p>
+<p><tt class="docutils literal"><span class="pre">--verify-x509-name</span></tt> is a useful replacement for the <tt class="docutils literal"><span class="pre">--tls-verify</span></tt>
+option to verify the remote host, because <tt class="docutils literal"><span class="pre">--verify-x509-name</span></tt> works
+in a <tt class="docutils literal"><span class="pre">--chroot</span></tt> environment without any dependencies.</p>
+<p>Using a name prefix is a useful alternative to managing a CRL
+(Certificate Revocation List) on the client, since it allows the client
+to refuse all certificates except for those associated with designated
+servers.</p>
+<dl class="last docutils">
+<dt><em>NOTE:</em></dt>
+<dd>Test against a name prefix only when you are using OpenVPN
+with a custom CA certificate that is under your control. Never use
+this option with type <code>name-prefix</code> when your client
+certificates are signed by a third party, such as a commercial
+web CA.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--x509-track <var>attribute</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Save peer X509 <strong>attribute</strong> value in environment for use by plugins and
+management interface. Prepend a <code>+</code> to <tt class="docutils literal">attribute</tt> to save values
+from full cert chain. Values will be encoded as
+<code>X509_&lt;depth&gt;_&lt;attribute&gt;=&lt;value&gt;</code>. Multiple <tt class="docutils literal"><span class="pre">--x509-track</span></tt>
+options can be defined to track multiple attributes.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--x509-username-field <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Field in the X.509 certificate subject to be used as the username
+(default <code>CN</code>).</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+x509-username-field [ext:]fieldname
+</pre>
+<p>Typically, this option is specified with <strong>fieldname</strong> as
+either of the following:</p>
+<pre class="literal-block">
+x509-username-field emailAddress
+x509-username-field ext:subjectAltName
+</pre>
+<p>The first example uses the value of the <code>emailAddress</code> attribute
+in the certificate's Subject field as the username. The second example
+uses the <code>ext:</code> prefix to signify that the X.509 extension
+<tt class="docutils literal">fieldname</tt> <code>subjectAltName</code> be searched for an rfc822Name
+(email) field to be used as the username. In cases where there are
+multiple email addresses in <code>ext:fieldname</code>, the last occurrence
+is chosen.</p>
+<p>When this option is used, the <tt class="docutils literal"><span class="pre">--verify-x509-name</span></tt> option will match
+against the chosen <tt class="docutils literal">fieldname</tt> instead of the Common Name.</p>
+<p>Only the <code>subjectAltName</code> and <code>issuerAltName</code> X.509
+extensions are supported.</p>
+<p class="last"><strong>Please note:</strong> This option has a feature which will convert an
+all-lowercase <tt class="docutils literal">fieldname</tt> to uppercase characters, e.g.,
+<code>ou</code> -&gt; <code>OU</code>. A mixed-case <tt class="docutils literal">fieldname</tt> or one having the
+<code>ext:</code> prefix will be left as-is. This automatic upcasing feature is
+deprecated and will be removed in a future release.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="pkcs-11-smartcard-options">
+<h2>PKCS#11 / SmartCard options</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-cert-private <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set if access to certificate object should be performed after login.
+Every provider has its own setting.</p>
+<p>Valid syntaxes:</p>
+<pre class="last literal-block">
+pkcs11-cert-private 0
+pkcs11-cert-private 1
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-id <var>name</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specify the serialized certificate id to be used. The id can be gotten
+by the standalone <tt class="docutils literal"><span class="pre">--show-pkcs11-ids</span></tt> option.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-id-management</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Acquire PKCS#11 id from management interface. In this case a
+<code>NEED-STR 'pkcs11-id-request'</code> real-time message will be triggered,
+application may use pkcs11-id-count command to retrieve available number of
+certificates, and pkcs11-id-get command to retrieve certificate id and
+certificate body.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-pin-cache <var>seconds</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specify how many seconds the PIN can be cached, the default is until the
+token is removed.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-private-mode <var>mode</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify which method to use in order to perform private key operations.
+A different mode can be specified for each provider. Mode is encoded as
+hex number, and can be a mask one of the following:</p>
+<p><code>0</code> (default) Try to determine automatically.</p>
+<p><code>1</code> Use sign.</p>
+<p><code>2</code> Use sign recover.</p>
+<p><code>4</code> Use decrypt.</p>
+<p class="last"><code>8</code> Use unwrap.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-protected-authentication <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Use PKCS#11 protected authentication path, useful for biometric and
+external keypad devices. Every provider has its own setting.</p>
+<p>Valid syntaxes:</p>
+<pre class="last literal-block">
+pkcs11-protected-authentication 0
+pkcs11-protected-authentication 1
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--pkcs11-providers <var>provider</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify an RSA Security Inc. PKCS #11 Cryptographic Token Interface
+(Cryptoki) providers to load. This option can be used instead of
+<tt class="docutils literal"><span class="pre">--cert</span></tt>, <tt class="docutils literal"><span class="pre">--key</span></tt> and <tt class="docutils literal"><span class="pre">--pkcs12</span></tt>.</p>
+<p class="last">If p11-kit is present on the system, its <code>p11-kit-proxy.so</code> module
+will be loaded by default if either the <tt class="docutils literal"><span class="pre">--pkcs11-id</span></tt> or
+<tt class="docutils literal"><span class="pre">--pkcs11-id-management</span></tt> options are specified without
+<tt class="docutils literal"><span class="pre">--pkcs11-provider</span></tt> being given.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--show-pkcs11-ids <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">(Standalone) Show PKCS#11 token object list.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+show-pkcs11 [provider] [cert_private]
+</pre>
+<p>Specify <tt class="docutils literal">cert_private</tt> as <code>1</code> if certificates are stored as
+private objects.</p>
+<p>If <em>p11-kit</em> is present on the system, the <tt class="docutils literal">provider</tt> argument is
+optional; if omitted the default <code>p11-kit-proxy.so</code> module will be
+queried.</p>
+<p class="last"><tt class="docutils literal"><span class="pre">--verb</span></tt> option can be used BEFORE this option to produce debugging
+information.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+<div class="section" id="data-channel-cipher-negotiation">
+<h1>Data channel cipher negotiation</h1>
+<p>OpenVPN 2.4 and higher have the capability to negotiate the data cipher that
+is used to encrypt data packets. This section describes the mechanism in more detail and the
+different backwards compatibility mechanism with older server and clients.</p>
+<div class="section" id="openvpn-2-5-and-higher-behaviour">
+<h2>OpenVPN 2.5 and higher behaviour</h2>
+<p>When both client and server are at least running OpenVPN 2.5, that the order of
+the ciphers of the server's <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> is used to pick the the data cipher.
+That means that the first cipher in that list that is also in the client's
+<tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> list is chosen. If no common cipher is found the client is rejected
+with a AUTH_FAILED message (as seen in client log):</p>
+<blockquote>
+AUTH: Received control message: AUTH_FAILED,Data channel cipher negotiation failed (no shared cipher)</blockquote>
+<p>OpenVPN 2.5 will only allow the ciphers specified in <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt>. To ensure
+backwards compatibility also if a cipher is specified using the <tt class="docutils literal"><span class="pre">--cipher</span></tt> option
+it is automatically added to this list. If both options are unset the default is
+<code>AES-256-GCM:AES-128-GCM</code>.</p>
+</div>
+<div class="section" id="openvpn-2-4-clients">
+<h2>OpenVPN 2.4 clients</h2>
+<p>The negotiation support in OpenVPN 2.4 was the first iteration of the implementation
+and still had some quirks. Its main goal was &quot;upgrade to AES-256-GCM when possible&quot;.
+An OpenVPN 2.4 client that is built against a crypto library that supports AES in GCM
+mode and does not have <tt class="docutils literal"><span class="pre">--ncp-disable</span></tt> will always announce support for
+<cite>AES-256-GCM</cite> and <cite>AES-128-GCM</cite> to a server by sending <code>IV_NCP=2</code>.</p>
+<p>This only causes a problem if <tt class="docutils literal"><span class="pre">--ncp-ciphers</span></tt> option has been changed from the
+default of <code>AES-256-GCM:AES-128-GCM</code> to a value that does not include
+these two ciphers. When a OpenVPN servers try to use <cite>AES-256-GCM</cite> or
+<cite>AES-128-GCM</cite> the connection will then fail. It is therefore recommended to
+always have the <cite>AES-256-GCM</cite> and <cite>AES-128-GCM</cite> ciphers to the <tt class="docutils literal"><span class="pre">--ncp-ciphers</span></tt>
+options to avoid this behaviour.</p>
+</div>
+<div class="section" id="openvpn-3-clients">
+<h2>OpenVPN 3 clients</h2>
+<p>Clients based on the OpenVPN 3.x library (<a class="reference external" href="https://github.com/openvpn/openvpn3/">https://github.com/openvpn/openvpn3/</a>)
+do not have a configurable <tt class="docutils literal"><span class="pre">--ncp-ciphers</span></tt> or <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> option. Instead
+these clients will announce support for all their supported AEAD ciphers
+(<cite>AES-256-GCM</cite>, <cite>AES-128-GCM</cite> and in newer versions also <cite>Chacha20-Poly1305</cite>).</p>
+<p>To support OpenVPN 3.x based clients at least one of these ciphers needs to be
+included in the server's <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> option.</p>
+</div>
+<div class="section" id="openvpn-2-3-and-older-clients-and-clients-with-ncp-disable">
+<h2>OpenVPN 2.3 and older clients (and clients with <tt class="docutils literal"><span class="pre">--ncp-disable</span></tt>)</h2>
+<p>When a client without cipher negotiation support connects to a server the
+cipher specified with the <tt class="docutils literal"><span class="pre">--cipher</span></tt> option in the client configuration
+must be included in the <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> option of the server to allow
+the client to connect. Otherwise the client will be sent the <tt class="docutils literal">AUTH_FAILED</tt>
+message that indicates no shared cipher.</p>
+<p>If the client is 2.3 or older and has been configured with the
+<tt class="docutils literal"><span class="pre">--enable-small</span></tt> <code>./configure</code> argument, using
+<tt class="docutils literal"><span class="pre">data-ciphers-fallback</span> cipher</tt> in the server config file with the explicit
+cipher used by the client is necessary.</p>
+</div>
+<div class="section" id="openvpn-2-4-server">
+<h2>OpenVPN 2.4 server</h2>
+<p>When a client indicates support for <cite>AES-128-GCM</cite> and <cite>AES-256-GCM</cite>
+(with <tt class="docutils literal">IV_NCP=2</tt>) an OpenVPN 2.4 server will send the first
+cipher of the <tt class="docutils literal"><span class="pre">--ncp-ciphers</span></tt> to the OpenVPN client regardless of what
+the cipher is. To emulate the behaviour of an OpenVPN 2.4 client as close
+as possible and have compatibility to a setup that depends on this quirk,
+adding <cite>AES-128-GCM</cite> and <cite>AES-256-GCM</cite> to the client's <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt>
+option is required. OpenVPN 2.5+ will only announce the <tt class="docutils literal">IV_NCP=2</tt> flag if
+those ciphers are present.</p>
+</div>
+<div class="section" id="openvpn-2-3-and-older-servers-and-servers-with-ncp-disable">
+<h2>OpenVPN 2.3 and older servers (and servers with <tt class="docutils literal"><span class="pre">--ncp-disable</span></tt>)</h2>
+<p>The cipher used by the server must be included in <tt class="docutils literal"><span class="pre">--data-ciphers</span></tt> to
+allow the client connecting to a server without cipher negotiation
+support.
+(For compatibility OpenVPN 2.5 will also accept the cipher set with
+<tt class="docutils literal"><span class="pre">--cipher</span></tt>)</p>
+<p>If the server is 2.3 or older and has been configured with the
+<tt class="docutils literal"><span class="pre">--enable-small</span></tt> <code>./configure</code> argument, adding
+<tt class="docutils literal"><span class="pre">data-ciphers-fallback</span> cipher</tt> to the client config with the explicit
+cipher used by the server is necessary.</p>
+</div>
+<div class="section" id="blowfish-in-cbc-mode-bf-cbc-deprecation">
+<h2>Blowfish in CBC mode (BF-CBC) deprecation</h2>
+<p>The <tt class="docutils literal"><span class="pre">--cipher</span></tt> option defaulted to <tt class="docutils literal"><span class="pre">BF-CBC</span></tt> in OpenVPN 2.4 and older
+version. The default was never changed to ensure backwards compatibility.
+In OpenVPN 2.5 this behaviour has now been changed so that if the <tt class="docutils literal"><span class="pre">--cipher</span></tt>
+is not explicitly set it does not allow the weak <tt class="docutils literal"><span class="pre">BF-CBC</span></tt> cipher any more
+and needs to explicitly added as <tt class="docutils literal"><span class="pre">--cipher</span> <span class="pre">BFC-CBC</span></tt> or added to
+<tt class="docutils literal"><span class="pre">--data-ciphers</span></tt>.</p>
+<p>We strongly recommend to switching away from BF-CBC to a
+more secure cipher as soon as possible instead.</p>
+</div>
+</div>
+<div class="section" id="network-configuration">
+<h1>NETWORK CONFIGURATION</h1>
+<p>OpenVPN consists of two sides of network configuration. One side is the
+<em>link</em> between the local and remote side, the other side is the <em>virtual
+network adapter</em> (tun/tap device).</p>
+<div class="section" id="link-options">
+<h2>Link Options</h2>
+<p>This link options section covers options related to the connection between
+the local and the remote host.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--bind <var>keywords</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Bind to local address and port. This is the default unless any of
+<tt class="docutils literal"><span class="pre">--proto</span> <span class="pre">tcp-client</span></tt> , <tt class="docutils literal"><span class="pre">--http-proxy</span></tt> or <tt class="docutils literal"><span class="pre">--socks-proxy</span></tt> are used.</p>
+<p class="last">If the optional <code>ipv6only</code> keyword is present OpenVPN will bind only
+to IPv6 (as opposed to IPv6 and IPv4) when a IPv6 socket is opened.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--float</span></kbd></td>
+<td><p class="first">Allow remote peer to change its IP address and/or port number, such as
+due to DHCP (this is the default if <tt class="docutils literal"><span class="pre">--remote</span></tt> is not used).
+<tt class="docutils literal"><span class="pre">--float</span></tt> when specified with <tt class="docutils literal"><span class="pre">--remote</span></tt> allows an OpenVPN session
+to initially connect to a peer at a known address, however if packets
+arrive from a new address and pass all authentication tests, the new
+address will take control of the session. This is useful when you are
+connecting to a peer which holds a dynamic address such as a dial-in
+user or DHCP client.</p>
+<p class="last">Essentially, <tt class="docutils literal"><span class="pre">--float</span></tt> tells OpenVPN to accept authenticated packets
+from any address, not only the address which was specified in the
+<tt class="docutils literal"><span class="pre">--remote</span></tt> option.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--fragment <var>max</var></span></kbd></td>
+<td><p class="first">Enable internal datagram fragmentation so that no UDP datagrams are sent
+which are larger than <tt class="docutils literal">max</tt> bytes.</p>
+<p>The <tt class="docutils literal">max</tt> parameter is interpreted in the same way as the
+<tt class="docutils literal"><span class="pre">--link-mtu</span></tt> parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including the UDP header itself.</p>
+<p>The <tt class="docutils literal"><span class="pre">--fragment</span></tt> option only makes sense when you are using the UDP
+protocol (<tt class="docutils literal"><span class="pre">--proto</span> udp</tt>).</p>
+<p><tt class="docutils literal"><span class="pre">--fragment</span></tt> adds 4 bytes of overhead per datagram.</p>
+<p>See the <tt class="docutils literal"><span class="pre">--mssfix</span></tt> option below for an important related option to
+<tt class="docutils literal"><span class="pre">--fragment</span></tt>.</p>
+<p>It should also be noted that this option is not meant to replace UDP
+fragmentation at the IP stack level. It is only meant as a last resort
+when path MTU discovery is broken. Using this option is less efficient
+than fixing path MTU discovery for your IP link and using native IP
+fragmentation instead.</p>
+<p class="last">Having said that, there are circumstances where using OpenVPN's internal
+fragmentation capability may be your only option, such as tunneling a
+UDP multicast stream which requires fragmentation.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--keepalive <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">A helper directive designed to simplify the expression of <tt class="docutils literal"><span class="pre">--ping</span></tt> and
+<tt class="docutils literal"><span class="pre">--ping-restart</span></tt>.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+keepalive interval timeout
+</pre>
+<p>This option can be used on both client and server side, but it is enough
+to add this on the server side as it will push appropriate <tt class="docutils literal"><span class="pre">--ping</span></tt>
+and <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> options to the client. If used on both server and
+client, the values pushed from server will override the client local
+values.</p>
+<p>The <tt class="docutils literal">timeout</tt> argument will be twice as long on the server side. This
+ensures that a timeout is detected on client side before the server side
+drops the connection.</p>
+<p>For example, <tt class="docutils literal"><span class="pre">--keepalive</span> 10 60</tt> expands as follows:</p>
+<pre class="last literal-block">
+if mode server:
+ ping 10 # Argument: interval
+ ping-restart 120 # Argument: timeout*2
+ push &quot;ping 10&quot; # Argument: interval
+ push &quot;ping-restart 60&quot; # Argument: timeout
+else
+ ping 10 # Argument: interval
+ ping-restart 60 # Argument: timeout
+</pre>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--link-mtu <var>n</var></span></kbd></td>
+<td>Sets an upper bound on the size of UDP packets which are sent between
+OpenVPN peers. <em>It's best not to set this parameter unless you know what
+you're doing.</em></td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--local <var>host</var></span></kbd></td>
+<td>Local host name or IP address for bind. If specified, OpenVPN will bind
+to this address only. If unspecified, OpenVPN will bind to all
+interfaces.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--lport <var>port</var></span></kbd></td>
+<td>Set local TCP/UDP port number or name. Cannot be used together with
+<tt class="docutils literal"><span class="pre">--nobind</span></tt> option.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mark <var>value</var></span></kbd></td>
+<td>Mark encrypted packets being sent with value. The mark value can be
+matched in policy routing and packetfilter rules. This option is only
+supported in Linux and does nothing on other operating systems.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mode <var>m</var></span></kbd></td>
+<td>Set OpenVPN major mode. By default, OpenVPN runs in point-to-point mode
+(<code>p2p</code>). OpenVPN 2.0 introduces a new mode (<code>server</code>) which
+implements a multi-client server capability.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mssfix <var>max</var></span></kbd></td>
+<td><p class="first">Announce to TCP sessions running over the tunnel that they should limit
+their send packet sizes such that after OpenVPN has encapsulated them,
+the resulting UDP packet size that OpenVPN sends to its peer will not
+exceed <tt class="docutils literal">max</tt> bytes. The default value is <code>1450</code>.</p>
+<p>The <tt class="docutils literal">max</tt> parameter is interpreted in the same way as the
+<tt class="docutils literal"><span class="pre">--link-mtu</span></tt> parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including the UDP header itself.
+Resulting packet would be at most 28 bytes larger for IPv4 and 48 bytes
+for IPv6 (20/40 bytes for IP header and 8 bytes for UDP header). Default
+value of 1450 allows IPv4 packets to be transmitted over a link with MTU
+1473 or higher without IP level fragmentation.</p>
+<p>The <tt class="docutils literal"><span class="pre">--mssfix</span></tt> option only makes sense when you are using the UDP
+protocol for OpenVPN peer-to-peer communication, i.e. <tt class="docutils literal"><span class="pre">--proto</span> udp</tt>.</p>
+<p><tt class="docutils literal"><span class="pre">--mssfix</span></tt> and <tt class="docutils literal"><span class="pre">--fragment</span></tt> can be ideally used together, where
+<tt class="docutils literal"><span class="pre">--mssfix</span></tt> will try to keep TCP from needing packet fragmentation in
+the first place, and if big packets come through anyhow (from protocols
+other than TCP), <tt class="docutils literal"><span class="pre">--fragment</span></tt> will internally fragment them.</p>
+<p>Both <tt class="docutils literal"><span class="pre">--fragment</span></tt> and <tt class="docutils literal"><span class="pre">--mssfix</span></tt> are designed to work around cases
+where Path MTU discovery is broken on the network path between OpenVPN
+peers.</p>
+<p>The usual symptom of such a breakdown is an OpenVPN connection which
+successfully starts, but then stalls during active usage.</p>
+<p>If <tt class="docutils literal"><span class="pre">--fragment</span></tt> and <tt class="docutils literal"><span class="pre">--mssfix</span></tt> are used together, <tt class="docutils literal"><span class="pre">--mssfix</span></tt> will
+take its default <tt class="docutils literal">max</tt> parameter from the <tt class="docutils literal"><span class="pre">--fragment</span> max</tt> option.</p>
+<p>Therefore, one could lower the maximum UDP packet size to 1300 (a good
+first try for solving MTU-related connection problems) with the
+following options:</p>
+<pre class="last literal-block">
+--tun-mtu 1500 --fragment 1300 --mssfix
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--mtu-disc <var>type</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Should we do Path MTU discovery on TCP/UDP channel? Only supported on
+OSes such as Linux that supports the necessary system call to set.</p>
+<p>Valid types:</p>
+<p><code>no</code> Never send DF (Don't Fragment) frames</p>
+<p><code>maybe</code> Use per-route hints</p>
+<p class="last"><code>yes</code> Always DF (Don't Fragment)</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--mtu-test</span></kbd></td>
+<td>To empirically measure MTU on connection startup, add the <tt class="docutils literal"><span class="pre">--mtu-test</span></tt>
+option to your configuration. OpenVPN will send ping packets of various
+sizes to the remote peer and measure the largest packets which were
+successfully received. The <tt class="docutils literal"><span class="pre">--mtu-test</span></tt> process normally takes about 3
+minutes to complete.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--nobind</span></kbd></td>
+<td>Do not bind to local address and port. The IP stack will allocate a
+dynamic port for returning packets. Since the value of the dynamic port
+could not be known in advance by a peer, this option is only suitable
+for peers which will be initiating connections by using the --remote
+option.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--passtos</span></kbd></td>
+<td>Set the TOS field of the tunnel packet to what the payload's TOS is.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ping <var>n</var></span></kbd></td>
+<td><p class="first">Ping remote over the TCP/UDP control channel if no packets have been
+sent for at least <tt class="docutils literal">n</tt> seconds (specify <tt class="docutils literal"><span class="pre">--ping</span></tt> on both peers to
+cause ping packets to be sent in both directions since OpenVPN ping
+packets are not echoed like IP ping packets). When used in one of
+OpenVPN's secure modes (where <tt class="docutils literal"><span class="pre">--secret</span></tt>, <tt class="docutils literal"><span class="pre">--tls-server</span></tt> or
+<tt class="docutils literal"><span class="pre">--tls-client</span></tt> is specified), the ping packet will be
+cryptographically secure.</p>
+<p>This option has two intended uses:</p>
+<ol class="last arabic simple">
+<li>Compatibility with stateful firewalls. The periodic ping will ensure
+that a stateful firewall rule which allows OpenVPN UDP packets to
+pass will not time out.</li>
+<li>To provide a basis for the remote to test the existence of its peer
+using the <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> option.</li>
+</ol>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ping-exit <var>n</var></span></kbd></td>
+<td><p class="first">Causes OpenVPN to exit after <tt class="docutils literal">n</tt> seconds pass without reception of a
+ping or other packet from remote. This option can be combined with
+<tt class="docutils literal"><span class="pre">--inactive</span></tt>, <tt class="docutils literal"><span class="pre">--ping</span></tt> and <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> to create a two-tiered
+inactivity disconnect.</p>
+<p>For example,</p>
+<pre class="literal-block">
+openvpn [options...] --inactive 3600 --ping 10 --ping-exit 60
+</pre>
+<p class="last">when used on both peers will cause OpenVPN to exit within 60 seconds if
+its peer disconnects, but will exit after one hour if no actual tunnel
+data is exchanged.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ping-restart <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Similar to <tt class="docutils literal"><span class="pre">--ping-exit</span></tt>, but trigger a <code>SIGUSR1</code> restart after
+<tt class="docutils literal">n</tt> seconds pass without reception of a ping or other packet from
+remote.</p>
+<p>This option is useful in cases where the remote peer has a dynamic IP
+address and a low-TTL DNS name is used to track the IP address using a
+service such as <a class="reference external" href="http://dyndns.org/">http://dyndns.org/</a> + a dynamic DNS client such as
+<tt class="docutils literal">ddclient</tt>.</p>
+<p>If the peer cannot be reached, a restart will be triggered, causing the
+hostname used with <tt class="docutils literal"><span class="pre">--remote</span></tt> to be re-resolved (if <tt class="docutils literal"><span class="pre">--resolv-retry</span></tt>
+is also specified).</p>
+<p>In server mode, <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>, <tt class="docutils literal"><span class="pre">--inactive</span></tt> or any other type of
+internally generated signal will always be applied to individual client
+instance objects, never to whole server itself. Note also in server mode
+that any internally generated signal which would normally cause a
+restart, will cause the deletion of the client instance object instead.</p>
+<p>In client mode, the <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> parameter is set to 120 seconds
+by default. This default will hold until the client pulls a replacement
+value from the server, based on the <tt class="docutils literal"><span class="pre">--keepalive</span></tt> setting in the
+server configuration. To disable the 120 second default, set
+<tt class="docutils literal"><span class="pre">--ping-restart</span> 0</tt> on the client.</p>
+<p>See the signals section below for more information on <code>SIGUSR1</code>.</p>
+<p>Note that the behavior of <tt class="docutils literal">SIGUSR1</tt> can be modified by the
+<tt class="docutils literal"><span class="pre">--persist-tun</span></tt>, <tt class="docutils literal"><span class="pre">--persist-key</span></tt>, <tt class="docutils literal"><span class="pre">--persist-local-ip</span></tt> and
+<tt class="docutils literal"><span class="pre">--persist-remote-ip</span></tt> options.</p>
+<p class="last">Also note that <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> and <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> are mutually
+exclusive and cannot be used together.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ping-timer-rem</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Run the <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> / <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> timer only if we have a
+remote address. Use this option if you are starting the daemon in listen
+mode (i.e. without an explicit <tt class="docutils literal"><span class="pre">--remote</span></tt> peer), and you don't want to
+start clocking timeouts until a remote peer connects.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--proto <var>p</var></span></kbd></td>
+<td><p class="first">Use protocol <tt class="docutils literal">p</tt> for communicating with remote host. <tt class="docutils literal">p</tt> can be
+<code>udp</code>, <code>tcp-client</code>, or <code>tcp-server</code>.</p>
+<p>The default protocol is <code>udp</code> when <tt class="docutils literal"><span class="pre">--proto</span></tt> is not specified.</p>
+<p>For UDP operation, <tt class="docutils literal"><span class="pre">--proto</span> udp</tt> should be specified on both peers.</p>
+<p>For TCP operation, one peer must use <tt class="docutils literal"><span class="pre">--proto</span> <span class="pre">tcp-server</span></tt> and the
+other must use <tt class="docutils literal"><span class="pre">--proto</span> <span class="pre">tcp-client</span></tt>. A peer started with
+<code>tcp-server</code> will wait indefinitely for an incoming connection. A peer
+started with <code>tcp-client</code> will attempt to connect, and if that fails,
+will sleep for 5 seconds (adjustable via the <tt class="docutils literal"><span class="pre">--connect-retry</span></tt> option)
+and try again infinite or up to N retries (adjustable via the
+<tt class="docutils literal"><span class="pre">--connect-retry-max</span></tt> option). Both TCP client and server will
+simulate a SIGUSR1 restart signal if either side resets the connection.</p>
+<p>OpenVPN is designed to operate optimally over UDP, but TCP capability is
+provided for situations where UDP cannot be used. In comparison with
+UDP, TCP will usually be somewhat less efficient and less robust when
+used over unreliable or congested networks.</p>
+<p>This article outlines some of problems with tunneling IP over TCP:
+<a class="reference external" href="http://sites.inka.de/sites/bigred/devel/tcp-tcp.html">http://sites.inka.de/sites/bigred/devel/tcp-tcp.html</a></p>
+<p class="last">There are certain cases, however, where using TCP may be advantageous
+from a security and robustness perspective, such as tunneling non-IP or
+application-level UDP protocols, or tunneling protocols which don't
+possess a built-in reliability layer.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--port <var>port</var></span></kbd></td>
+<td>TCP/UDP port number or port name for both local and remote (sets both
+<tt class="docutils literal"><span class="pre">--lport</span></tt> and <tt class="docutils literal"><span class="pre">--rport</span></tt> options to given port). The current default
+of 1194 represents the official IANA port number assignment for OpenVPN
+and has been used since version 2.0-beta17. Previous versions used port
+5000 as the default.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--rport <var>port</var></span></kbd></td>
+<td>Set TCP/UDP port number or name used by the <tt class="docutils literal"><span class="pre">--remote</span></tt> option. The
+port can also be set directly using the <tt class="docutils literal"><span class="pre">--remote</span></tt> option.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--replay-window <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Modify the replay protection sliding-window size and time window.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+replay-window n [t]
+</pre>
+<p>Use a replay protection sliding-window of size <strong>n</strong> and a time window
+of <strong>t</strong> seconds.</p>
+<p>By default <strong>n</strong> is 64 (the IPSec default) and <strong>t</strong> is 15 seconds.</p>
+<p>This option is only relevant in UDP mode, i.e. when either <strong>--proto
+udp</strong> is specified, or no <strong>--proto</strong> option is specified.</p>
+<p>When OpenVPN tunnels IP packets over UDP, there is the possibility that
+packets might be dropped or delivered out of order. Because OpenVPN,
+like IPSec, is emulating the physical network layer, it will accept an
+out-of-order packet sequence, and will deliver such packets in the same
+order they were received to the TCP/IP protocol stack, provided they
+satisfy several constraints.</p>
+<ol class="loweralpha simple">
+<li>The packet cannot be a replay (unless <tt class="docutils literal"><span class="pre">--no-replay</span></tt> is
+specified, which disables replay protection altogether).</li>
+<li>If a packet arrives out of order, it will only be accepted if
+the difference between its sequence number and the highest sequence
+number received so far is less than <tt class="docutils literal">n</tt>.</li>
+<li>If a packet arrives out of order, it will only be accepted if it
+arrives no later than <tt class="docutils literal">t</tt> seconds after any packet containing a higher
+sequence number.</li>
+</ol>
+<p>If you are using a network link with a large pipeline (meaning that the
+product of bandwidth and latency is high), you may want to use a larger
+value for <tt class="docutils literal">n</tt>. Satellite links in particular often require this.</p>
+<p>If you run OpenVPN at <tt class="docutils literal"><span class="pre">--verb</span> 4</tt>, you will see the message
+&quot;Replay-window backtrack occurred [x]&quot; every time the maximum sequence
+number backtrack seen thus far increases. This can be used to calibrate
+<tt class="docutils literal">n</tt>.</p>
+<p>There is some controversy on the appropriate method of handling packet
+reordering at the security layer.</p>
+<p>Namely, to what extent should the security layer protect the
+encapsulated protocol from attacks which masquerade as the kinds of
+normal packet loss and reordering that occur over IP networks?</p>
+<p>The IPSec and OpenVPN approach is to allow packet reordering within a
+certain fixed sequence number window.</p>
+<p>OpenVPN adds to the IPSec model by limiting the window size in time as
+well as sequence space.</p>
+<p>OpenVPN also adds TCP transport as an option (not offered by IPSec) in
+which case OpenVPN can adopt a very strict attitude towards message
+deletion and reordering: Don't allow it. Since TCP guarantees
+reliability, any packet loss or reordering event can be assumed to be an
+attack.</p>
+<p>In this sense, it could be argued that TCP tunnel transport is preferred
+when tunneling non-IP or UDP application protocols which might be
+vulnerable to a message deletion or reordering attack which falls within
+the normal operational parameters of IP networks.</p>
+<p class="last">So I would make the statement that one should never tunnel a non-IP
+protocol or UDP application protocol over UDP, if the protocol might be
+vulnerable to a message deletion or reordering attack that falls within
+the normal operating parameters of what is to be expected from the
+physical IP layer. The problem is easily fixed by simply using TCP as
+the VPN transport layer.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--replay-persist <var>file</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Persist replay-protection state across sessions using <tt class="docutils literal">file</tt> to save
+and reload the state.</p>
+<p>This option will strengthen protection against replay attacks,
+especially when you are using OpenVPN in a dynamic context (such as with
+<tt class="docutils literal"><span class="pre">--inetd</span></tt>) when OpenVPN sessions are frequently started and stopped.</p>
+<p>This option will keep a disk copy of the current replay protection state
+(i.e. the most recent packet timestamp and sequence number received from
+the remote peer), so that if an OpenVPN session is stopped and
+restarted, it will reject any replays of packets which were already
+received by the prior session.</p>
+<p class="last">This option only makes sense when replay protection is enabled (the
+default) and you are using either <tt class="docutils literal"><span class="pre">--secret</span></tt> (shared-secret key mode)
+or TLS mode with <tt class="docutils literal"><span class="pre">--tls-auth</span></tt>.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--socket-flags <var>flags</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Apply the given flags to the OpenVPN transport socket. Currently, only
+<code>TCP_NODELAY</code> is supported.</p>
+<p>The <code>TCP_NODELAY</code> socket flag is useful in TCP mode, and causes the
+kernel to send tunnel packets immediately over the TCP connection without
+trying to group several smaller packets into a larger packet. This can
+result in a considerably improvement in latency.</p>
+<p class="last">This option is pushable from server to client, and should be used on
+both client and server for maximum effect.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tcp-nodelay</span></kbd></td>
+<td><p class="first">This macro sets the <code>TCP_NODELAY</code> socket flag on the server as well
+as pushes it to connecting clients. The <code>TCP_NODELAY</code> flag disables
+the Nagle algorithm on TCP sockets causing packets to be transmitted
+immediately with low latency, rather than waiting a short period of time
+in order to aggregate several packets into a larger containing packet.
+In VPN applications over TCP, <code>TCP_NODELAY</code> is generally a good
+latency optimization.</p>
+<p>The macro expands as follows:</p>
+<pre class="last literal-block">
+if mode server:
+ socket-flags TCP_NODELAY
+ push &quot;socket-flags TCP_NODELAY&quot;
+</pre>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="virtual-network-adapter-vpn-interface">
+<h2>Virtual Network Adapter (VPN interface)</h2>
+<p>Options in this section relates to configuration of the virtual tun/tap
+network interface, including setting the VPN IP address and network
+routing.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--bind-dev <var>device</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>(Linux only) Set <tt class="docutils literal">device</tt> to bind the server socket to a
+<a class="reference internal" href="#virtual-routing-and-forwarding">Virtual Routing and Forwarding</a> device</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--block-ipv6</span></kbd></td>
+<td><p class="first">On the client, instead of sending IPv6 packets over the VPN tunnel, all
+IPv6 packets are answered with an ICMPv6 no route host message. On the
+server, all IPv6 packets from clients are answered with an ICMPv6 no
+route to host message. This options is intended for cases when IPv6
+should be blocked and other options are not available. <tt class="docutils literal"><span class="pre">--block-ipv6</span></tt>
+will use the remote IPv6 as source address of the ICMPv6 packets if set,
+otherwise will use <code>fe80::7</code> as source address.</p>
+<p>For this option to make sense you actually have to route traffic to the
+tun interface. The following example config block would send all IPv6
+traffic to OpenVPN and answer all requests with no route to host,
+effectively blocking IPv6 (to avoid IPv6 connections from dual-stacked
+clients leaking around IPv4-only VPN services).</p>
+<dl class="docutils">
+<dt><strong>Client config</strong></dt>
+<dd><pre class="first last literal-block">
+--ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1
+--redirect-gateway ipv6
+--block-ipv6
+</pre>
+</dd>
+<dt><strong>Server config</strong></dt>
+<dd><p class="first">Push a &quot;valid&quot; ipv6 config to the client and block on the server</p>
+<pre class="last literal-block">
+--push &quot;ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1&quot;
+--push &quot;redirect-gateway ipv6&quot;
+--block-ipv6
+</pre>
+</dd>
+</dl>
+<p class="last">Note: this option does not influence traffic sent from the server
+towards the client (neither on the server nor on the client side).
+This is not seen as necessary, as such traffic can be most easily
+avoided by not configuring IPv6 on the server tun, or setting up a
+server-side firewall rule.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--dev <var>device</var></span></kbd></td>
+<td><p class="first">TUN/TAP virtual network device which can be <code>tunX</code>, <code>tapX</code>,
+<code>null</code> or an arbitrary name string (<code>X</code> can be omitted for
+a dynamic device.)</p>
+<p>See examples section below for an example on setting up a TUN device.</p>
+<p>You must use either tun devices on both ends of the connection or tap
+devices on both ends. You cannot mix them, as they represent different
+underlying network layers:</p>
+<dl class="docutils">
+<dt><code>tun</code></dt>
+<dd>devices encapsulate IPv4 or IPv6 (OSI Layer 3)</dd>
+<dt><code>tap</code></dt>
+<dd>devices encapsulate Ethernet 802.3 (OSI Layer 2).</dd>
+</dl>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+dev tun2
+dev tap4
+dev ovpn
+</pre>
+<p class="last">When the device name starts with <code>tun</code> or <code>tap</code>, the device
+type is extracted automatically. Otherwise the <tt class="docutils literal"><span class="pre">--dev-type</span></tt> option
+needs to be added as well.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--dev-node <var>node</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Explicitly set the device node rather than using <code>/dev/net/tun</code>,
+<code>/dev/tun</code>, <code>/dev/tap</code>, etc. If OpenVPN cannot figure out
+whether <tt class="docutils literal">node</tt> is a TUN or TAP device based on the name, you should
+also specify <tt class="docutils literal"><span class="pre">--dev-type</span> tun</tt> or <tt class="docutils literal"><span class="pre">--dev-type</span> tap</tt>.</p>
+<p>Under Mac OS X this option can be used to specify the default tun
+implementation. Using <tt class="docutils literal"><span class="pre">--dev-node</span> utun</tt> forces usage of the native
+Darwin tun kernel support. Use <tt class="docutils literal"><span class="pre">--dev-node</span> utunN</tt> to select a specific
+utun instance. To force using the <code>tun.kext</code> (<code>/dev/tunX</code>)
+use <tt class="docutils literal"><span class="pre">--dev-node</span> tun</tt>. When not specifying a <tt class="docutils literal"><span class="pre">--dev-node</span></tt> option
+openvpn will first try to open utun, and fall back to tun.kext.</p>
+<p class="last">On Windows systems, select the TAP-Win32 adapter which is named <tt class="docutils literal">node</tt>
+in the Network Connections Control Panel or the raw GUID of the adapter
+enclosed by braces. The <tt class="docutils literal"><span class="pre">--show-adapters</span></tt> option under Windows can
+also be used to enumerate all available TAP-Win32 adapters and will show
+both the network connections control panel name and the GUID for each
+TAP-Win32 adapter.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--dev-type <var>device-type</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Which device type are we using? <tt class="docutils literal"><span class="pre">device-type</span></tt> should be <code>tun</code>
+(OSI Layer 3) or <code>tap</code> (OSI Layer 2). Use this option only if
+the TUN/TAP device used with <tt class="docutils literal"><span class="pre">--dev</span></tt> does not begin with <code>tun</code>
+or <code>tap</code>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--dhcp-option <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set additional network parameters on supported platforms. May be specified
+on the client or pushed from the server. On Windows these options are
+handled by the <tt class="docutils literal"><span class="pre">tap-windows6</span></tt> driver by default or directly by OpenVPN
+if dhcp is disabled or the <tt class="docutils literal">wintun</tt> driver is in use. The
+<tt class="docutils literal">OpenVPN for Android</tt> client also handles them internally.</p>
+<p>On all other platforms these options are only saved in the client's
+environment under the name <code>foreign_options_{n}</code> before the
+<tt class="docutils literal"><span class="pre">--up</span></tt> script is called. A plugin or an <tt class="docutils literal"><span class="pre">--up</span></tt> script must be used to
+pick up and interpret these as required. Many Linux distributions include
+such scripts and some third-party user interfaces such as tunnelblick also
+come with scripts that process these options.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+dhcp-options type [parm]
+</pre>
+<dl class="last docutils">
+<dt><code>DOMAIN</code> <tt class="docutils literal">name</tt></dt>
+<dd>Set Connection-specific DNS Suffix to <code>name</code>.</dd>
+<dt><code>ADAPTER_DOMAIN_SUFFIX</code> <tt class="docutils literal">name</tt></dt>
+<dd>Alias to <code>DOMAIN</code>. This is a compatibility option, it
+should not be used in new deployments.</dd>
+<dt><code>DOMAIN-SEARCH</code> <tt class="docutils literal">name</tt></dt>
+<dd>Add <code>name</code> to the domain search list.
+Repeat this option to add more entries. Up to
+10 domains are supported.</dd>
+<dt><code>DNS</code> <tt class="docutils literal">address</tt></dt>
+<dd><p class="first">Set primary domain name server IPv4 or IPv6 address.
+Repeat this option to set secondary DNS server addresses.</p>
+<p class="last">Note: DNS IPv6 servers are currently set using netsh (the existing
+DHCP code can only do IPv4 DHCP, and that protocol only permits
+IPv4 addresses anywhere). The option will be put into the
+environment, so an <tt class="docutils literal"><span class="pre">--up</span></tt> script could act upon it if needed.</p>
+</dd>
+<dt><code>WINS</code> <tt class="docutils literal">address</tt></dt>
+<dd>Set primary WINS server address (NetBIOS over TCP/IP Name Server).
+Repeat this option to set secondary WINS server addresses.</dd>
+<dt><code>NBDD</code> <tt class="docutils literal">address</tt></dt>
+<dd>Set primary NBDD server address (NetBIOS over TCP/IP Datagram
+Distribution Server). Repeat this option to set secondary NBDD
+server addresses.</dd>
+<dt><code>NTP</code> <tt class="docutils literal">address</tt></dt>
+<dd>Set primary NTP server address (Network Time Protocol).
+Repeat this option to set secondary NTP server addresses.</dd>
+<dt><code>NBT</code> <tt class="docutils literal">type</tt></dt>
+<dd><p class="first">Set NetBIOS over TCP/IP Node type. Possible options:</p>
+<dl class="last docutils">
+<dt><code>1</code></dt>
+<dd>b-node (broadcasts)</dd>
+<dt><code>2</code></dt>
+<dd>p-node (point-to-point name queries to a WINS server)</dd>
+<dt><code>4</code></dt>
+<dd>m-node (broadcast then query name server)</dd>
+<dt><code>8</code></dt>
+<dd>h-node (query name server, then broadcast).</dd>
+</dl>
+</dd>
+<dt><code>NBS</code> <tt class="docutils literal"><span class="pre">scope-id</span></tt></dt>
+<dd>Set NetBIOS over TCP/IP Scope. A NetBIOS Scope ID provides an
+extended naming service for the NetBIOS over TCP/IP (Known as NBT)
+module. The primary purpose of a NetBIOS scope ID is to isolate
+NetBIOS traffic on a single network to only those nodes with the
+same NetBIOS scope ID. The NetBIOS scope ID is a character string
+that is appended to the NetBIOS name. The NetBIOS scope ID on two
+hosts must match, or the two hosts will not be able to communicate.
+The NetBIOS Scope ID also allows computers to use the same computer
+name, as they have different scope IDs. The Scope ID becomes a part
+of the NetBIOS name, making the name unique. (This description of
+NetBIOS scopes courtesy of <a class="reference external" href="mailto:NeonSurge&#64;abyss.com">NeonSurge&#64;abyss.com</a>)</dd>
+<dt><code>DISABLE-NBT</code></dt>
+<dd>Disable Netbios-over-TCP/IP.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set TUN/TAP adapter parameters. It requires the <em>IP address</em> of the local
+VPN endpoint. For TUN devices in point-to-point mode, the next argument
+must be the VPN IP address of the remote VPN endpoint. For TAP devices,
+or TUN devices used with <tt class="docutils literal"><span class="pre">--topology</span> subnet</tt>, the second argument
+is the subnet mask of the virtual network segment which is being created
+or connected to.</p>
+<p>For TUN devices, which facilitate virtual point-to-point IP connections
+(when used in <tt class="docutils literal"><span class="pre">--topology</span> net30</tt> or <tt class="docutils literal">p2p</tt> mode), the proper usage of
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> is to use two private IP addresses which are not a member
+of any existing subnet which is in use. The IP addresses may be
+consecutive and should have their order reversed on the remote peer.
+After the VPN is established, by pinging <tt class="docutils literal">rn</tt>, you will be pinging
+across the VPN.</p>
+<p>For TAP devices, which provide the ability to create virtual ethernet
+segments, or TUN devices in <tt class="docutils literal"><span class="pre">--topology</span> subnet</tt> mode (which create
+virtual &quot;multipoint networks&quot;), <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> is used to set an IP
+address and subnet mask just as a physical ethernet adapter would be
+similarly configured. If you are attempting to connect to a remote
+ethernet bridge, the IP address and subnet should be set to values which
+would be valid on the the bridged ethernet segment (note also that DHCP
+can be used for the same purpose).</p>
+<p>This option, while primarily a proxy for the <tt class="docutils literal">ifconfig</tt>(8) command,
+is designed to simplify TUN/TAP tunnel configuration by providing a
+standard interface to the different ifconfig implementations on
+different platforms.</p>
+<p><tt class="docutils literal"><span class="pre">--ifconfig</span></tt> parameters which are IP addresses can also be specified
+as a DNS or /etc/hosts file resolvable name.</p>
+<p>For TAP devices, <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> should not be used if the TAP interface
+will be getting an IP address lease from a DHCP server.</p>
+<p>Examples:</p>
+<pre class="last literal-block">
+# tun device in net30/p2p mode
+ifconfig 10.8.0.2 10.8.0.1
+
+# tun/tap device in subnet mode
+ifconfig 10.8.0.2 255.255.255.0
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-ipv6 <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Configure an IPv6 address on the <em>tun</em> device.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+ifconfig-ipv6 ipv6addr/bits [ipv6remote]
+</pre>
+<p>The <tt class="docutils literal">ipv6addr/bits</tt> argument is the IPv6 address to use. The
+second parameter is used as route target for <tt class="docutils literal"><span class="pre">--route-ipv6</span></tt> if no
+gateway is specified.</p>
+<p class="last">The <tt class="docutils literal"><span class="pre">--topology</span></tt> option has no influence with <tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-noexec</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Don't actually execute ifconfig/netsh commands, instead pass
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> parameters to scripts using environmental variables.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-nowarn</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Don't output an options consistency check warning if the <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>
+option on this side of the connection doesn't match the remote side.
+This is useful when you want to retain the overall benefits of the
+options consistency check (also see <tt class="docutils literal"><span class="pre">--disable-occ</span></tt> option) while only
+disabling the ifconfig component of the check.</p>
+<p>For example, if you have a configuration where the local host uses
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> but the remote host does not, use <tt class="docutils literal"><span class="pre">--ifconfig-nowarn</span></tt>
+on the local host.</p>
+<p class="last">This option will also silence warnings about potential address conflicts
+which occasionally annoy more experienced users by triggering &quot;false
+positive&quot; warnings.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--lladdr <var>address</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specify the link layer address, more commonly known as the MAC address.
+Only applied to TAP devices.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--persist-tun</span></kbd></td>
+<td><p class="first">Don't close and reopen TUN/TAP device or run up/down scripts across
+<code>SIGUSR1</code> or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> restarts.</p>
+<p class="last"><code>SIGUSR1</code> is a restart signal similar to <code>SIGHUP</code>, but which
+offers finer-grained control over reset options.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--redirect-gateway <var>flags</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Automatically execute routing commands to cause all outgoing IP traffic
+to be redirected over the VPN. This is a client-side option.</p>
+<p>This option performs three steps:</p>
+<ol class="arabic simple">
+<li>Create a static route for the <tt class="docutils literal"><span class="pre">--remote</span></tt> address which
+forwards to the pre-existing default gateway. This is done so that
+<tt class="docutils literal">(3)</tt> will not create a routing loop.</li>
+<li>Delete the default gateway route.</li>
+<li>Set the new default gateway to be the VPN endpoint address
+(derived either from <tt class="docutils literal"><span class="pre">--route-gateway</span></tt> or the second parameter to
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> when <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> is specified).</li>
+</ol>
+<p>When the tunnel is torn down, all of the above steps are reversed so
+that the original default route is restored.</p>
+<p>Option flags:</p>
+<dl class="last docutils">
+<dt><code>local</code></dt>
+<dd>Add the <code>local</code> flag if both OpenVPN peers are directly
+connected via a common subnet, such as with wireless. The
+<code>local</code> flag will cause step <tt class="docutils literal">(1)</tt> above to be omitted.</dd>
+<dt><code>autolocal</code></dt>
+<dd>Try to automatically determine whether to enable <code>local</code>
+flag above.</dd>
+<dt><code>def1</code></dt>
+<dd>Use this flag to override the default gateway by using
+<code>0.0.0.0/1</code> and <code>128.0.0.0/1</code> rather than
+<code>0.0.0.0/0</code>. This has the benefit of overriding but not
+wiping out the original default gateway.</dd>
+<dt><code>bypass-dhcp</code></dt>
+<dd>Add a direct route to the DHCP server (if it is non-local) which
+bypasses the tunnel (Available on Windows clients, may not be
+available on non-Windows clients).</dd>
+<dt><code>bypass-dns</code></dt>
+<dd>Add a direct route to the DNS server(s) (if they are non-local)
+which bypasses the tunnel (Available on Windows clients, may
+not be available on non-Windows clients).</dd>
+<dt><code>block-local</code></dt>
+<dd>Block access to local LAN when the tunnel is active, except for
+the LAN gateway itself. This is accomplished by routing the local
+LAN (except for the LAN gateway address) into the tunnel.</dd>
+<dt><code>ipv6</code></dt>
+<dd>Redirect IPv6 routing into the tunnel. This works similar to
+the <code>def1</code> flag, that is, more specific IPv6 routes are added
+(<code>2000::/4</code>, <code>3000::/4</code>), covering the whole IPv6
+unicast space.</dd>
+<dt><code>!ipv4</code></dt>
+<dd>Do not redirect IPv4 traffic - typically used in the flag pair
+<code>ipv6 !ipv4</code> to redirect IPv6-only.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--redirect-private <var>flags</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Like <tt class="docutils literal"><span class="pre">--redirect-gateway</span></tt>, but omit actually changing the default gateway.
+Useful when pushing private subnets.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--route <var>args</var></span></kbd></td>
+<td><p class="first">Add route to routing table after connection is established. Multiple
+routes can be specified. Routes will be automatically torn down in
+reverse order prior to TUN/TAP device close.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+route network/IP
+route network/IP netmask
+route network/IP netmask gateway
+route network/IP netmask gateway metric
+</pre>
+<p>This option is intended as a convenience proxy for the <tt class="docutils literal">route</tt>(8)
+shell command, while at the same time providing portable semantics
+across OpenVPN's platform space.</p>
+<dl class="docutils">
+<dt><tt class="docutils literal">netmask</tt></dt>
+<dd>defaults to <code>255.255.255.255</code> when not given</dd>
+<dt><tt class="docutils literal">gateway</tt></dt>
+<dd>default taken from <tt class="docutils literal"><span class="pre">--route-gateway</span></tt> or the second
+parameter to <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> when <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> is specified.</dd>
+<dt><tt class="docutils literal">metric</tt></dt>
+<dd>default taken from <tt class="docutils literal"><span class="pre">--route-metric</span></tt> if set, otherwise <code>0</code>.</dd>
+</dl>
+<p>The default can be specified by leaving an option blank or setting it to
+<code>default</code>.</p>
+<p>The <tt class="docutils literal">network</tt> and <tt class="docutils literal">gateway</tt> parameters can also be specified as a
+DNS or <code>/etc/hosts</code> file resolvable name, or as one of three special
+keywords:</p>
+<dl class="last docutils">
+<dt><code>vpn_gateway</code></dt>
+<dd>The remote VPN endpoint address (derived either from
+<tt class="docutils literal"><span class="pre">--route-gateway</span></tt> or the second parameter to <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>
+when <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> is specified).</dd>
+<dt><code>net_gateway</code></dt>
+<dd>The pre-existing IP default gateway, read from the
+routing table (not supported on all OSes).</dd>
+<dt><code>remote_host</code></dt>
+<dd>The <tt class="docutils literal"><span class="pre">--remote</span></tt> address if OpenVPN is being run in
+client mode, and is undefined in server mode.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-delay <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Valid syntaxes:</p>
+<pre class="literal-block">
+route-delay
+route-delay n
+route-delay n m
+</pre>
+<p>Delay <tt class="docutils literal">n</tt> seconds (default <code>0</code>) after connection establishment,
+before adding routes. If <tt class="docutils literal">n</tt> is <code>0</code>, routes will be added
+immediately upon connection establishment. If <tt class="docutils literal"><span class="pre">--route-delay</span></tt> is
+omitted, routes will be added immediately after TUN/TAP device open and
+<tt class="docutils literal"><span class="pre">--up</span></tt> script execution, before any <tt class="docutils literal"><span class="pre">--user</span></tt> or <tt class="docutils literal"><span class="pre">--group</span></tt> privilege
+downgrade (or <tt class="docutils literal"><span class="pre">--chroot</span></tt> execution.)</p>
+<p>This option is designed to be useful in scenarios where DHCP is used to
+set tap adapter addresses. The delay will give the DHCP handshake time
+to complete before routes are added.</p>
+<p class="last">On Windows, <tt class="docutils literal"><span class="pre">--route-delay</span></tt> tries to be more intelligent by waiting
+<tt class="docutils literal">w</tt> seconds (default <code>30</code> by default) for the TAP-Win32 adapter
+to come up before adding routes.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-ipv6 <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Setup IPv6 routing in the system to send the specified IPv6 network into
+OpenVPN's <em>tun</em>.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+route-ipv6 ipv6addr/bits [gateway] [metric]
+</pre>
+<p class="last">The gateway parameter is only used for IPv6 routes across <em>tap</em> devices,
+and if missing, the <tt class="docutils literal">ipv6remote</tt> field from <tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt> or
+<tt class="docutils literal"><span class="pre">--route-ipv6-gateway</span></tt> is used.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-gateway <var>arg</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Specify a default <em>gateway</em> for use with <tt class="docutils literal"><span class="pre">--route</span></tt>.</p>
+<p>If <code>dhcp</code> is specified as the parameter, the gateway address will
+be extracted from a DHCP negotiation with the OpenVPN server-side LAN.</p>
+<p>Valid syntaxes:</p>
+<pre class="last literal-block">
+route-gateway gateway
+route-gateway dhcp
+</pre>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-ipv6-gateway <var>gw</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specify a default gateway <tt class="docutils literal">gw</tt> for use with <tt class="docutils literal"><span class="pre">--route-ipv6</span></tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-metric <var>m</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specify a default metric <tt class="docutils literal">m</tt> for use with <tt class="docutils literal"><span class="pre">--route</span></tt>.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--route-noexec</span></kbd></td>
+<td>Don't add or remove routes automatically. Instead pass routes to
+<tt class="docutils literal"><span class="pre">--route-up</span></tt> script using environmental variables.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--route-nopull</span></kbd></td>
+<td><p class="first">When used with <tt class="docutils literal"><span class="pre">--client</span></tt> or <tt class="docutils literal"><span class="pre">--pull</span></tt>, accept options pushed by
+server EXCEPT for routes, block-outside-dns and dhcp options like DNS
+servers.</p>
+<p class="last">When used on the client, this option effectively bars the server from
+adding routes to the client's routing table, however note that this
+option still allows the server to set the TCP/IP properties of the
+client's TUN/TAP interface.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--topology <var>mode</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Configure virtual addressing topology when running in <tt class="docutils literal"><span class="pre">--dev</span> tun</tt>
+mode. This directive has no meaning in <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> mode, which always
+uses a <code>subnet</code> topology.</p>
+<p>If you set this directive on the server, the <tt class="docutils literal"><span class="pre">--server</span></tt> and
+<tt class="docutils literal"><span class="pre">--server-bridge</span></tt> directives will automatically push your chosen
+topology setting to clients as well. This directive can also be manually
+pushed to clients. Like the <tt class="docutils literal"><span class="pre">--dev</span></tt> directive, this directive must
+always be compatible between client and server.</p>
+<p><tt class="docutils literal">mode</tt> can be one of:</p>
+<dl class="docutils">
+<dt><code>net30</code></dt>
+<dd>Use a point-to-point topology, by allocating one /30 subnet
+per client. This is designed to allow point-to-point semantics when some
+or all of the connecting clients might be Windows systems. This is the
+default on OpenVPN 2.0.</dd>
+<dt><code>p2p</code></dt>
+<dd>Use a point-to-point topology where the remote endpoint of
+the client's tun interface always points to the local endpoint of the
+server's tun interface. This mode allocates a single IP address per
+connecting client. Only use when none of the connecting clients are
+Windows systems.</dd>
+<dt><code>subnet</code></dt>
+<dd>Use a subnet rather than a point-to-point topology by
+configuring the tun interface with a local IP address and subnet mask,
+similar to the topology used in <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> and ethernet bridging
+mode. This mode allocates a single IP address per connecting client and
+works on Windows as well. Only available when server and clients are
+OpenVPN 2.1 or higher, or OpenVPN 2.0.x which has been manually patched
+with the <tt class="docutils literal"><span class="pre">--topology</span></tt> directive code. When used on Windows, requires
+version 8.2 or higher of the TAP-Win32 driver. When used on *nix,
+requires that the tun driver supports an <tt class="docutils literal">ifconfig</tt>(8) command which
+sets a subnet instead of a remote endpoint IP address.</dd>
+</dl>
+<p class="last"><em>Note:</em> Using <tt class="docutils literal"><span class="pre">--topology</span> subnet</tt> changes the interpretation of the
+arguments of <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> to mean &quot;address netmask&quot;, no longer &quot;local
+remote&quot;.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tun-mtu <var>n</var></span></kbd></td>
+<td><p class="first">Take the TUN device MTU to be <strong>n</strong> and derive the link MTU from it
+(default <code>1500</code>). In most cases, you will probably want to leave
+this parameter set to its default value.</p>
+<p>The MTU (Maximum Transmission Units) is the maximum datagram size in
+bytes that can be sent unfragmented over a particular network path.
+OpenVPN requires that packets on the control and data channels be sent
+unfragmented.</p>
+<p>MTU problems often manifest themselves as connections which hang during
+periods of active usage.</p>
+<p class="last">It's best to use the <tt class="docutils literal"><span class="pre">--fragment</span></tt> and/or <tt class="docutils literal"><span class="pre">--mssfix</span></tt> options to deal
+with MTU sizing issues.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tun-mtu-extra <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Assume that the TUN/TAP device might return as many as <tt class="docutils literal">n</tt> bytes more
+than the <tt class="docutils literal"><span class="pre">--tun-mtu</span></tt> size on read. This parameter defaults to 0, which
+is sufficient for most TUN devices. TAP devices may introduce additional
+overhead in excess of the MTU size, and a setting of 32 is the default
+when TAP devices are used. This parameter only controls internal OpenVPN
+buffer sizing, so there is no transmission overhead associated with
+using a larger value.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="tun-tap-standalone-operations">
+<h2>TUN/TAP standalone operations</h2>
+<p>These two standalone operations will require <tt class="docutils literal"><span class="pre">--dev</span></tt> and optionally
+<tt class="docutils literal"><span class="pre">--user</span></tt> and/or <tt class="docutils literal"><span class="pre">--group</span></tt>.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--mktun</span></kbd></td>
+<td><p class="first">(Standalone) Create a persistent tunnel on platforms which support them
+such as Linux. Normally TUN/TAP tunnels exist only for the period of
+time that an application has them open. This option takes advantage of
+the TUN/TAP driver's ability to build persistent tunnels that live
+through multiple instantiations of OpenVPN and die only when they are
+deleted or the machine is rebooted.</p>
+<p>One of the advantages of persistent tunnels is that they eliminate the
+need for separate <tt class="docutils literal"><span class="pre">--up</span></tt> and <tt class="docutils literal"><span class="pre">--down</span></tt> scripts to run the appropriate
+<tt class="docutils literal">ifconfig</tt>(8) and <tt class="docutils literal">route</tt>(8) commands. These commands can be
+placed in the the same shell script which starts or terminates an
+OpenVPN session.</p>
+<p>Another advantage is that open connections through the TUN/TAP-based
+tunnel will not be reset if the OpenVPN peer restarts. This can be
+useful to provide uninterrupted connectivity through the tunnel in the
+event of a DHCP reset of the peer's public IP address (see the
+<tt class="docutils literal"><span class="pre">--ipchange</span></tt> option above).</p>
+<p>One disadvantage of persistent tunnels is that it is harder to
+automatically configure their MTU value (see <tt class="docutils literal"><span class="pre">--link-mtu</span></tt> and
+<tt class="docutils literal"><span class="pre">--tun-mtu</span></tt> above).</p>
+<p class="last">On some platforms such as Windows, TAP-Win32 tunnels are persistent by
+default.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--rmtun</span></kbd></td>
+<td>(Standalone) Remove a persistent tunnel.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="virtual-routing-and-forwarding">
+<h2>Virtual Routing and Forwarding</h2>
+<p>Options in this section relates to configuration of virtual routing and
+forwarding in combination with the underlying operating system.</p>
+<p>As of today this is only supported on Linux, a kernel &gt;= 4.9 is
+recommended.</p>
+<p>This could come in handy when for example the external network should be
+only used as a means to connect to some VPN endpoints and all regular
+traffic should only be routed through any tunnel(s). This could be
+achieved by setting up a VRF and configuring the interface connected to
+the external network to be part of the VRF. The examples below will cover
+this setup.</p>
+<p>Another option would be to put the tun/tap interface into a VRF. This could
+be done by an up-script which uses the <code>ip link set</code> command shown
+below.</p>
+<div class="section" id="vrf-setup-with-iproute2">
+<h3>VRF setup with iproute2</h3>
+<p>Create VRF <code>vrf_external</code> and map it to routing table <code>1023</code></p>
+<pre class="literal-block">
+ip link add vrf_external type vrf table 1023
+</pre>
+<p>Move <code>eth0</code> into <code>vrf_external</code></p>
+<pre class="literal-block">
+ip link set master vrf_external dev eth0
+</pre>
+<p>Any prefixes configured on <code>eth0</code> will be moved from the :code`main`
+routing table into routing table <cite>1023</cite></p>
+</div>
+<div class="section" id="vrf-setup-with-ifupdown">
+<h3>VRF setup with ifupdown</h3>
+<p>For Debian based Distributions <code>ifupdown2</code> provides an almost drop-in
+replacement for <code>ifupdown</code> including VRFs and other features.
+A configuration for an interface <code>eth0</code> being part of VRF
+code:<cite>vrf_external</cite> could look like this:</p>
+<pre class="literal-block">
+auto eth0
+iface eth0
+ address 192.0.2.42/24
+ address 2001:db8:08:15::42/64
+ gateway 192.0.2.1
+ gateway 2001:db8:08:15::1
+ vrf vrf_external
+
+auto vrf_external
+iface vrf_external
+ vrf-table 1023
+</pre>
+</div>
+<div class="section" id="openvpn-configuration">
+<h3>OpenVPN configuration</h3>
+<p>The OpenVPN configuration needs to contain this line:</p>
+<pre class="literal-block">
+bind-dev vrf_external
+</pre>
+</div>
+<div class="section" id="further-reading">
+<h3>Further reading</h3>
+<p>Wikipedia has nice page one VRFs: <a class="reference external" href="https://en.wikipedia.org/wiki/Virtual_routing_and_forwarding">https://en.wikipedia.org/wiki/Virtual_routing_and_forwarding</a></p>
+<p>This talk from the Network Track of FrOSCon 2018 provides an overview about
+advanced layer 2 and layer 3 features of Linux</p>
+<blockquote>
+<ul class="simple">
+<li>Slides: <a class="reference external" href="https://www.slideshare.net/BarbarossaTM/l2l3-fr-fortgeschrittene-helle-und-dunkle-magie-im-linuxnetzwerkstack">https://www.slideshare.net/BarbarossaTM/l2l3-fr-fortgeschrittene-helle-und-dunkle-magie-im-linuxnetzwerkstack</a></li>
+<li>Video (german): <a class="reference external" href="https://media.ccc.de/v/froscon2018-2247-l2_l3_fur_fortgeschrittene_-_helle_und_dunkle_magie_im_linux-netzwerkstack">https://media.ccc.de/v/froscon2018-2247-l2_l3_fur_fortgeschrittene_-_helle_und_dunkle_magie_im_linux-netzwerkstack</a></li>
+</ul>
+</blockquote>
+</div>
+</div>
+</div>
+<div class="section" id="scripting-integration">
+<h1>SCRIPTING INTEGRATION</h1>
+<p>OpenVPN can execute external scripts in various phases of the lifetime of
+the OpenVPN process.</p>
+<div class="section" id="script-order-of-execution">
+<h2>Script Order of Execution</h2>
+<ol class="arabic">
+<li><p class="first"><tt class="docutils literal"><span class="pre">--up</span></tt></p>
+<p>Executed after TCP/UDP socket bind and TUN/TAP open.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--tls-verify</span></tt></p>
+<p>Executed when we have a still untrusted remote peer.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--ipchange</span></tt></p>
+<p>Executed after connection authentication, or remote IP address change.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--client-connect</span></tt></p>
+<p>Executed in <strong>--mode server</strong> mode immediately after client
+authentication.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--route-up</span></tt></p>
+<p>Executed after connection authentication, either immediately after, or
+some number of seconds after as defined by the <strong>--route-delay</strong> option.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--route-pre-down</span></tt></p>
+<p>Executed right before the routes are removed.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--client-disconnect</span></tt></p>
+<p>Executed in <tt class="docutils literal"><span class="pre">--mode</span> server</tt> mode on client instance shutdown.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--down</span></tt></p>
+<p>Executed after TCP/UDP and TUN/TAP close.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--learn-address</span></tt></p>
+<p>Executed in <tt class="docutils literal"><span class="pre">--mode</span> server</tt> mode whenever an IPv4 address/route or MAC
+address is added to OpenVPN's internal routing table.</p>
+</li>
+<li><p class="first"><tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt></p>
+<p>Executed in <tt class="docutils literal"><span class="pre">--mode</span> server</tt> mode on new client connections, when the
+client is still untrusted.</p>
+</li>
+</ol>
+</div>
+<div class="section" id="script-hooks">
+<h2>SCRIPT HOOKS</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--auth-user-pass-verify <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Require the client to provide a username/password (possibly in addition
+to a client certificate) for authentication.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+auth-user-pass-verify cmd method
+</pre>
+<p>OpenVPN will run command <tt class="docutils literal">cmd</tt> to validate the username/password
+provided by the client.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>If <tt class="docutils literal">method</tt> is set to <code>via-env</code>, OpenVPN will call <tt class="docutils literal">script</tt>
+with the environmental variables <code>username</code> and <code>password</code>
+set to the username/password strings provided by the client. <em>Beware</em>
+that this method is insecure on some platforms which make the environment
+of a process publicly visible to other unprivileged processes.</p>
+<p>If <tt class="docutils literal">method</tt> is set to <code>via-file</code>, OpenVPN will write the username
+and password to the first two lines of a temporary file. The filename
+will be passed as an argument to <tt class="docutils literal">script</tt>, and the file will be
+automatically deleted by OpenVPN after the script returns. The location
+of the temporary file is controlled by the <tt class="docutils literal"><span class="pre">--tmp-dir</span></tt> option, and
+will default to the current directory if unspecified. For security,
+consider setting <tt class="docutils literal"><span class="pre">--tmp-dir</span></tt> to a volatile storage medium such as
+<code>/dev/shm</code> (if available) to prevent the username/password file
+from touching the hard drive.</p>
+<p>The script should examine the username and password, returning a success
+exit code (<code>0</code>) if the client's authentication request is to be
+accepted, or a failure code (<code>1</code>) to reject the client.</p>
+<p>This directive is designed to enable a plugin-style interface for
+extending OpenVPN's authentication capabilities.</p>
+<p>To protect against a client passing a maliciously formed username or
+password string, the username string must consist only of these
+characters: alphanumeric, underbar ('<code>_</code>'), dash ('<code>-</code>'),
+dot ('<code>.</code>'), or at ('<code>&#64;</code>'). The password string can consist
+of any printable characters except for CR or LF. Any illegal characters
+in either the username or password string will be converted to
+underbar ('<code>_</code>').</p>
+<p>Care must be taken by any user-defined scripts to avoid creating a
+security vulnerability in the way that these strings are handled. Never
+use these strings in such a way that they might be escaped or evaluated
+by a shell interpreter.</p>
+<p class="last">For a sample script that performs PAM authentication, see
+<code>sample-scripts/auth-pam.pl</code> in the OpenVPN source distribution.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-connect <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Run command <tt class="docutils literal">cmd</tt> on client connection.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>The command is passed the common name and IP address of the
+just-authenticated client as environmental variables (see environmental
+variable section below). The command is also passed the pathname of a
+freshly created temporary file as the last argument (after any arguments
+specified in <tt class="docutils literal">cmd</tt> ), to be used by the command to pass dynamically
+generated config file directives back to OpenVPN.</p>
+<p>If the script wants to generate a dynamic config file to be applied on
+the server when the client connects, it should write it to the file
+named by the last argument.</p>
+<p>See the <tt class="docutils literal"><span class="pre">--client-config-dir</span></tt> option below for options which can be
+legally used in a dynamically generated config file.</p>
+<p>Note that the return value of <tt class="docutils literal">script</tt> is significant. If <tt class="docutils literal">script</tt>
+returns a non-zero error status, it will cause the client to be
+disconnected.</p>
+<p class="last">If a <tt class="docutils literal"><span class="pre">--client-connect</span></tt> wants to defer the generating of the
+configuration then the script needs to use the
+<code>client_connect_deferred_file</code> and
+<code>client_connect_config_file</code> environment variables, and write
+status accordingly into these files. See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a>
+section for more details.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-disconnect <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Like <tt class="docutils literal"><span class="pre">--client-connect</span></tt> but called on client instance shutdown. Will
+not be called unless the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script and plugins (if
+defined) were previously called on this instance with successful (0)
+status returns.</p>
+<p>The exception to this rule is if the <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> command or
+plugins are cascaded, and at least one client-connect function
+succeeded, then ALL of the client-disconnect functions for scripts and
+plugins will be called on client instance object deletion, even in cases
+where some of the related client-connect functions returned an error
+status.</p>
+<p class="last">The <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> command is not passed any extra arguments
+(only those arguments specified in cmd, if any).</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--down <var>cmd</var></span></kbd></td>
+<td><p class="first">Run command <tt class="docutils literal">cmd</tt> after TUN/TAP device close (post <tt class="docutils literal"><span class="pre">--user</span></tt> UID
+change and/or <tt class="docutils literal"><span class="pre">--chroot</span></tt> ). <tt class="docutils literal">cmd</tt> consists of a path to script (or
+executable program), optionally followed by arguments. The path and
+arguments may be single- or double-quoted and/or escaped using a
+backslash, and should be separated by one or more spaces.</p>
+<p>Called with the same parameters and environmental variables as the
+<tt class="docutils literal"><span class="pre">--up</span></tt> option above.</p>
+<p class="last">Note that if you reduce privileges by using <tt class="docutils literal"><span class="pre">--user</span></tt> and/or
+<tt class="docutils literal"><span class="pre">--group</span></tt>, your <tt class="docutils literal"><span class="pre">--down</span></tt> script will also run at reduced privilege.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--down-pre</span></kbd></td>
+<td>Call <tt class="docutils literal"><span class="pre">--down</span></tt> cmd/script before, rather than after, TUN/TAP close.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ipchange <var>cmd</var></span></kbd></td>
+<td><p class="first">Run command <tt class="docutils literal">cmd</tt> when our remote ip-address is initially
+authenticated or changes.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>When <tt class="docutils literal">cmd</tt> is executed two arguments are appended after any arguments
+specified in <tt class="docutils literal">cmd</tt> , as follows:</p>
+<pre class="literal-block">
+cmd ip address port number
+</pre>
+<p>Don't use <tt class="docutils literal"><span class="pre">--ipchange</span></tt> in <tt class="docutils literal"><span class="pre">--mode</span> server</tt> mode. Use a
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script instead.</p>
+<p>See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a> section below for additional
+parameters passed as environmental variables.</p>
+<p>If you are running in a dynamic IP address environment where the IP
+addresses of either peer could change without notice, you can use this
+script, for example, to edit the <code>/etc/hosts</code> file with the current
+address of the peer. The script will be run every time the remote peer
+changes its IP address.</p>
+<p class="last">Similarly if <em>our</em> IP address changes due to DHCP, we should configure
+our IP address change script (see man page for <tt class="docutils literal">dhcpcd</tt>(8)) to
+deliver a <tt class="docutils literal">SIGHUP</tt> or <tt class="docutils literal">SIGUSR1</tt> signal to OpenVPN. OpenVPN will
+then re-establish a connection with its most recently authenticated
+peer on its new IP address.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--learn-address <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Run command <tt class="docutils literal">cmd</tt> to validate client virtual addresses or routes.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>Three arguments will be appended to any arguments in <tt class="docutils literal">cmd</tt> as follows:</p>
+<dl class="docutils">
+<dt><code>$1</code> - [operation]</dt>
+<dd><code>&quot;add&quot;</code>, <code>&quot;update&quot;</code>, or <code>&quot;delete&quot;</code> based on whether
+or not the address is being added to, modified, or deleted from
+OpenVPN's internal routing table.</dd>
+<dt><code>$2</code> - [address]</dt>
+<dd>The address being learned or unlearned. This can be an IPv4 address
+such as <code>&quot;198.162.10.14&quot;</code>, an IPv4 subnet such as
+<code>&quot;198.162.10.0/24&quot;</code>, or an ethernet MAC address (when
+<tt class="docutils literal"><span class="pre">--dev</span> tap</tt> is being used) such as <code>&quot;00:FF:01:02:03:04&quot;</code>.</dd>
+<dt><code>$3</code> - [common name]</dt>
+<dd>The common name on the certificate associated with the client linked
+to this address. Only present for <code>&quot;add&quot;</code> or <code>&quot;update&quot;</code>
+operations, not <code>&quot;delete&quot;</code>.</dd>
+</dl>
+<p>On <code>&quot;add&quot;</code> or <code>&quot;update&quot;</code> methods, if the script returns
+a failure code (non-zero), OpenVPN will reject the address and will not
+modify its internal routing table.</p>
+<p class="last">Normally, the <tt class="docutils literal">cmd</tt> script will use the information provided above to
+set appropriate firewall entries on the VPN TUN/TAP interface. Since
+OpenVPN provides the association between virtual IP or MAC address and
+the client's authenticated common name, it allows a user-defined script
+to configure firewall access policies with regard to the client's
+high-level common name, rather than the low level client virtual
+addresses.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--route-up <var>cmd</var></span></kbd></td>
+<td><p class="first">Run command <tt class="docutils literal">cmd</tt> after routes are added, subject to <tt class="docutils literal"><span class="pre">--route-delay</span></tt>.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p class="last">See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a> section below for additional
+parameters passed as environmental variables.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-pre-down <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Run command <tt class="docutils literal">cmd</tt> before routes are removed upon disconnection.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p class="last">See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a> section below for additional
+parameters passed as environmental variables.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--setenv <var>args</var></span></kbd></td>
+<td><p class="first">Set a custom environmental variable <code>name=value</code> to pass to script.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+setenv name value
+setenv FORWARD_COMPATIBLE 1
+setenv opt config_option
+</pre>
+<p>By setting <code>FORWARD_COMPATIBLE</code> to <code>1</code>, the config file
+syntax checking is relaxed so that unknown directives will trigger a
+warning but not a fatal error, on the assumption that a given unknown
+directive might be valid in future OpenVPN versions.</p>
+<p>This option should be used with caution, as there are good security
+reasons for having OpenVPN fail if it detects problems in a config file.
+Having said that, there are valid reasons for wanting new software
+features to gracefully degrade when encountered by older software
+versions.</p>
+<p>It is also possible to tag a single directive so as not to trigger a
+fatal error if the directive isn't recognized. To do this, prepend the
+following before the directive: <tt class="docutils literal">setenv opt</tt></p>
+<p>Versions prior to OpenVPN 2.3.3 will always ignore options set with the
+<tt class="docutils literal">setenv opt</tt> directive.</p>
+<p class="last">See also <tt class="docutils literal"><span class="pre">--ignore-unknown-option</span></tt></p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--setenv-safe <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set a custom environmental variable <code>OPENVPN_name</code> to <code>value</code>
+to pass to scripts.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+setenv-safe name value
+</pre>
+<p class="last">This directive is designed to be pushed by the server to clients, and
+the prepending of <code>OPENVPN_</code> to the environmental variable is a
+safety precaution to prevent a <code>LD_PRELOAD</code> style attack from a
+malicious or compromised server.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tls-verify <var>cmd</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Run command <tt class="docutils literal">cmd</tt> to verify the X509 name of a pending TLS connection
+that has otherwise passed all other tests of certification (except for
+revocation via <tt class="docutils literal"><span class="pre">--crl-verify</span></tt> directive; the revocation test occurs
+after the <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> test).</p>
+<p><tt class="docutils literal">cmd</tt> should return <code>0</code> to allow the TLS handshake to proceed,
+or <code>1</code> to fail.</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>When <tt class="docutils literal">cmd</tt> is executed two arguments are appended after any arguments
+specified in <tt class="docutils literal">cmd</tt>, as follows:</p>
+<pre class="literal-block">
+cmd certificate_depth subject
+</pre>
+<p>These arguments are, respectively, the current certificate depth and the
+X509 subject distinguished name (dn) of the peer.</p>
+<p>This feature is useful if the peer you want to trust has a certificate
+which was signed by a certificate authority who also signed many other
+certificates, where you don't necessarily want to trust all of them, but
+rather be selective about which peer certificate you will accept. This
+feature allows you to write a script which will test the X509 name on a
+certificate and decide whether or not it should be accepted. For a
+simple perl script which will test the common name field on the
+certificate, see the file <tt class="docutils literal"><span class="pre">verify-cn</span></tt> in the OpenVPN distribution.</p>
+<p class="last">See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a> section below for additional
+parameters passed as environmental variables.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--up <var>cmd</var></span></kbd></td>
+<td><p class="first">Run command <tt class="docutils literal">cmd</tt> after successful TUN/TAP device open (pre <tt class="docutils literal"><span class="pre">--user</span></tt>
+UID change).</p>
+<p><tt class="docutils literal">cmd</tt> consists of a path to a script (or executable program), optionally
+followed by arguments. The path and arguments may be single- or
+double-quoted and/or escaped using a backslash, and should be separated
+by one or more spaces.</p>
+<p>The up command is useful for specifying route commands which route IP
+traffic destined for private subnets which exist at the other end of the
+VPN connection into the tunnel.</p>
+<p>For <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> execute as:</p>
+<pre class="literal-block">
+cmd tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_remote_ip [init | restart]
+</pre>
+<p>For <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> execute as:</p>
+<pre class="literal-block">
+cmd tap_dev tap_mtu link_mtu ifconfig_local_ip ifconfig_netmask [init | restart]
+</pre>
+<p>See the <a class="reference internal" href="#environmental-variables">Environmental Variables</a> section below for additional
+parameters passed as environmental variables.</p>
+<p>Note that if <tt class="docutils literal">cmd</tt> includes arguments, all OpenVPN-generated arguments
+will be appended to them to build an argument list with which the
+executable will be called.</p>
+<p>Typically, <tt class="docutils literal">cmd</tt> will run a script to add routes to the tunnel.</p>
+<p>Normally the up script is called after the TUN/TAP device is opened. In
+this context, the last command line parameter passed to the script will
+be <em>init.</em> If the <tt class="docutils literal"><span class="pre">--up-restart</span></tt> option is also used, the up script
+will be called for restarts as well. A restart is considered to be a
+partial reinitialization of OpenVPN where the TUN/TAP instance is
+preserved (the <tt class="docutils literal"><span class="pre">--persist-tun</span></tt> option will enable such preservation).
+A restart can be generated by a SIGUSR1 signal, a <tt class="docutils literal"><span class="pre">--ping-restart</span></tt>
+timeout, or a connection reset when the TCP protocol is enabled with the
+<tt class="docutils literal"><span class="pre">--proto</span></tt> option. If a restart occurs, and <tt class="docutils literal"><span class="pre">--up-restart</span></tt> has been
+specified, the up script will be called with <em>restart</em> as the last
+parameter.</p>
+<dl class="docutils">
+<dt><em>NOTE:</em></dt>
+<dd>On restart, OpenVPN will not pass the full set of environment
+variables to the script. Namely, everything related to routing and
+gateways will not be passed, as nothing needs to be done anyway - all
+the routing setup is already in place. Additionally, the up-restart
+script will run with the downgraded UID/GID settings (if configured).</dd>
+</dl>
+<p>The following standalone example shows how the <tt class="docutils literal"><span class="pre">--up</span></tt> script can be
+called in both an initialization and restart context. (<em>NOTE:</em> for
+security reasons, don't run the following example unless UDP port 9999
+is blocked by your firewall. Also, the example will run indefinitely, so
+you should abort with control-c).</p>
+<pre class="literal-block">
+openvpn --dev tun --port 9999 --verb 4 --ping-restart 10 \
+ --up 'echo up' --down 'echo down' --persist-tun \
+ --up-restart
+</pre>
+<p>Note that OpenVPN also provides the <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> option to
+automatically ifconfig the TUN device, eliminating the need to define an
+<tt class="docutils literal"><span class="pre">--up</span></tt> script, unless you also want to configure routes in the
+<tt class="docutils literal"><span class="pre">--up</span></tt> script.</p>
+<p>If <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> is also specified, OpenVPN will pass the ifconfig
+local and remote endpoints on the command line to the <tt class="docutils literal"><span class="pre">--up</span></tt> script so
+that they can be used to configure routes such as:</p>
+<pre class="last literal-block">
+route add -net 10.0.0.0 netmask 255.255.255.0 gw $5
+</pre>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--up-delay</span></kbd></td>
+<td><p class="first">Delay TUN/TAP open and possible <tt class="docutils literal"><span class="pre">--up</span></tt> script execution until after
+TCP/UDP connection establishment with peer.</p>
+<p>In <tt class="docutils literal"><span class="pre">--proto</span> udp</tt> mode, this option normally requires the use of
+<tt class="docutils literal"><span class="pre">--ping</span></tt> to allow connection initiation to be sensed in the absence of
+tunnel data, since UDP is a &quot;connectionless&quot; protocol.</p>
+<p class="last">On Windows, this option will delay the TAP-Win32 media state
+transitioning to &quot;connected&quot; until connection establishment, i.e. the
+receipt of the first authenticated packet from the peer.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--up-restart</span></kbd></td>
+<td>Enable the <tt class="docutils literal"><span class="pre">--up</span></tt> and <tt class="docutils literal"><span class="pre">--down</span></tt> scripts to be called for restarts as
+well as initial program start. This option is described more fully above
+in the <tt class="docutils literal"><span class="pre">--up</span></tt> option documentation.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="string-types-and-remapping">
+<h2>String Types and Remapping</h2>
+<p>In certain cases, OpenVPN will perform remapping of characters in
+strings. Essentially, any characters outside the set of permitted
+characters for each string type will be converted to underbar ('_').</p>
+<dl class="docutils">
+<dt><em>Q: Why is string remapping necessary?</em></dt>
+<dd>It's an important security feature to prevent the malicious
+coding of strings from untrusted sources to be passed as parameters to
+scripts, saved in the environment, used as a common name, translated to
+a filename, etc.</dd>
+<dt><em>Q: Can string remapping be disabled?</em></dt>
+<dd>Yes, by using the <tt class="docutils literal"><span class="pre">--no-name-remapping</span></tt> option, however this
+should be considered an advanced option.</dd>
+</dl>
+<p>Here is a brief rundown of OpenVPN's current string types and the
+permitted character class for each string:</p>
+<dl class="docutils">
+<dt><em>X509 Names</em></dt>
+<dd>Alphanumeric, underbar ('_'), dash ('-'), dot ('.'), at
+('&#64;'), colon (':'), slash ('/'), and equal ('='). Alphanumeric is
+defined as a character which will cause the C library isalnum() function
+to return true.</dd>
+<dt><em>Common Names</em></dt>
+<dd>Alphanumeric, underbar ('_'), dash ('-'), dot ('.'), and at ('&#64;').</dd>
+<dt><em>--auth-user-pass username</em></dt>
+<dd>Same as Common Name, with one exception:
+starting with OpenVPN 2.0.1, the username is passed to the
+<code>OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY</code> plugin in its raw form,
+without string remapping.</dd>
+<dt><em>--auth-user-pass password</em></dt>
+<dd>Any &quot;printable&quot; character except CR or LF. Printable is defined to be
+a character which will cause the C library isprint() function to
+return true.</dd>
+<dt><em>--client-config-dir filename as derived from common name or`username</em></dt>
+<dd>Alphanumeric, underbar ('_'), dash ('-'), and dot ('.') except for &quot;.&quot;
+or &quot;..&quot; as standalone strings. As of v2.0.1-rc6, the at ('&#64;') character
+has been added as well for compatibility with the common name character
+class.</dd>
+<dt><em>Environmental variable names</em></dt>
+<dd>Alphanumeric or underbar ('_').</dd>
+<dt><em>Environmental variable values</em></dt>
+<dd>Any printable character.</dd>
+</dl>
+<p>For all cases, characters in a string which are not members of the legal
+character class for that string type will be remapped to underbar
+('_').</p>
+</div>
+<div class="section" id="environmental-variables">
+<h2>Environmental Variables</h2>
+<p>Once set, a variable is persisted indefinitely until it is reset by a
+new value or a restart,</p>
+<p>As of OpenVPN 2.0-beta12, in server mode, environmental variables set by
+OpenVPN are scoped according to the client objects they are associated
+with, so there should not be any issues with scripts having access to
+stale, previously set variables which refer to different client
+instances.</p>
+<dl class="docutils">
+<dt><code>bytes_received</code></dt>
+<dd>Total number of bytes received from client during VPN session. Set prior
+to execution of the <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> script.</dd>
+<dt><code>bytes_sent</code></dt>
+<dd>Total number of bytes sent to client during VPN session. Set prior to
+execution of the <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> script.</dd>
+<dt><code>client_connect_config_file</code></dt>
+<dd>The path to the configuration file that should be written to by the
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> script (optional, if per-session configuration
+is desired). This is the same file name as passed via command line
+argument on the call to the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</dd>
+<dt><code>client_connect_deferred_file</code></dt>
+<dd><p class="first">This file can be optionally written to in order to to communicate a
+status code of the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script or plgin. Only the
+first character in the file is relevant. It must be either <code>1</code>
+to indicate normal script execution, <code>0</code> indicates an error (in
+the same way that a non zero exit status does) or <code>2</code> to indicate
+that the script deferred returning the config file.</p>
+<p>For deferred (background) handling, the script or plugin MUST write
+<code>2</code> to the file to indicate the deferral and then return with
+exit code <code>0</code> to signal <tt class="docutils literal">deferred handler started OK</tt>.</p>
+<p>A background process or similar must then take care of writing the
+configuration to the file indicated by the
+<code>client_connect_config_file</code> environment variable and when
+finished, write the a <code>1</code> to this file (or <code>0</code> in case of
+an error).</p>
+<p class="last">The absence of any character in the file when the script finishes
+executing is interpreted the same as <code>1</code>. This allows scripts
+that are not written to support the defer mechanism to be used
+unmodified.</p>
+</dd>
+<dt><code>common_name</code></dt>
+<dd>The X509 common name of an authenticated client. Set prior to execution
+of <tt class="docutils literal"><span class="pre">--client-connect</span></tt>, <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> and
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> scripts.</dd>
+<dt><code>config</code></dt>
+<dd>Name of first <tt class="docutils literal"><span class="pre">--config</span></tt> file. Set on program initiation and reset on
+SIGHUP.</dd>
+<dt><code>daemon</code></dt>
+<dd>Set to &quot;1&quot; if the <tt class="docutils literal"><span class="pre">--daemon</span></tt> directive is specified, or &quot;0&quot; otherwise.
+Set on program initiation and reset on SIGHUP.</dd>
+<dt><code>daemon_log_redirect</code></dt>
+<dd>Set to &quot;1&quot; if the <tt class="docutils literal"><span class="pre">--log</span></tt> or <tt class="docutils literal"><span class="pre">--log-append</span></tt> directives are
+specified, or &quot;0&quot; otherwise. Set on program initiation and reset on
+SIGHUP.</dd>
+<dt><code>dev</code></dt>
+<dd>The actual name of the TUN/TAP device, including a unit number if it
+exists. Set prior to <tt class="docutils literal"><span class="pre">--up</span></tt> or <tt class="docutils literal"><span class="pre">--down</span></tt> script execution.</dd>
+<dt><code>dev_idx</code></dt>
+<dd>On Windows, the device index of the TUN/TAP adapter (to be used in
+netsh.exe calls which sometimes just do not work right with interface
+names). Set prior to <tt class="docutils literal"><span class="pre">--up</span></tt> or <tt class="docutils literal"><span class="pre">--down</span></tt> script execution.</dd>
+<dt><code>foreign_option_{n}</code></dt>
+<dd>An option pushed via <tt class="docutils literal"><span class="pre">--push</span></tt> to a client which does not natively
+support it, such as <tt class="docutils literal"><span class="pre">--dhcp-option</span></tt> on a non-Windows system, will be
+recorded to this environmental variable sequence prior to <tt class="docutils literal"><span class="pre">--up</span></tt>
+script execution.</dd>
+<dt><code>ifconfig_broadcast</code></dt>
+<dd>The broadcast address for the virtual ethernet segment which is derived
+from the <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> option when <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> is used. Set prior to
+OpenVPN calling the <code>ifconfig</code> or <code>netsh</code> (windows version
+of ifconfig) commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script
+execution.</dd>
+<dt><code>ifconfig_ipv6_local</code></dt>
+<dd>The local VPN endpoint IPv6 address specified in the
+<tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt> option (first parameter). Set prior to OpenVPN
+calling the <code>ifconfig</code> or code:<cite>netsh</cite> (windows version of
+ifconfig) commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script
+execution.</dd>
+<dt><code>ifconfig_ipv6_netbits</code></dt>
+<dd>The prefix length of the IPv6 network on the VPN interface. Derived
+from the /nnn parameter of the IPv6 address in the <tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt>
+option (first parameter). Set prior to OpenVPN calling the
+<code>ifconfig</code> or <code>netsh</code> (windows version of ifconfig)
+commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script execution.</dd>
+<dt><code>ifconfig_ipv6_remote</code></dt>
+<dd>The remote VPN endpoint IPv6 address specified in the
+<tt class="docutils literal"><span class="pre">--ifconfig-ipv6</span></tt> option (second parameter). Set prior to OpenVPN
+calling the <code>ifconfig</code> or <code>netsh</code> (windows version of
+ifconfig) commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script
+execution.</dd>
+<dt><code>ifconfig_local</code></dt>
+<dd>The local VPN endpoint IP address specified in the <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>
+option (first parameter). Set prior to OpenVPN calling the
+<code>ifconfig</code> or <code>netsh</code> (windows version of ifconfig)
+commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script execution.</dd>
+<dt><code>ifconfig_remote</code></dt>
+<dd>The remote VPN endpoint IP address specified in the <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>
+option (second parameter) when <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> is used. Set prior to
+OpenVPN calling the <code>ifconfig</code> or <code>netsh</code> (windows version
+of ifconfig) commands which normally occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script
+execution.</dd>
+<dt><code>ifconfig_netmask</code></dt>
+<dd>The subnet mask of the virtual ethernet segment that is specified as
+the second parameter to <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> when <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> is being
+used. Set prior to OpenVPN calling the <code>ifconfig</code> or
+<code>netsh</code> (windows version of ifconfig) commands which normally
+occurs prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script execution.</dd>
+<dt><code>ifconfig_pool_local_ip</code></dt>
+<dd>The local virtual IP address for the TUN/TAP tunnel taken from an
+<tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt> directive if specified, or otherwise from the
+ifconfig pool (controlled by the <tt class="docutils literal"><span class="pre">--ifconfig-pool</span></tt> config file
+directive). Only set for <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> tunnels. This option is set on
+the server prior to execution of the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> and
+<tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> scripts.</dd>
+<dt><code>ifconfig_pool_netmask</code></dt>
+<dd>The virtual IP netmask for the TUN/TAP tunnel taken from an
+<tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt> directive if specified, or otherwise from the
+ifconfig pool (controlled by the <tt class="docutils literal"><span class="pre">--ifconfig-pool</span></tt> config file
+directive). Only set for <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> tunnels. This option is set on
+the server prior to execution of the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> and
+<tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> scripts.</dd>
+<dt><code>ifconfig_pool_remote_ip</code></dt>
+<dd>The remote virtual IP address for the TUN/TAP tunnel taken from an
+<tt class="docutils literal"><span class="pre">--ifconfig-push</span></tt> directive if specified, or otherwise from the
+ifconfig pool (controlled by the <tt class="docutils literal"><span class="pre">--ifconfig-pool</span></tt> config file
+directive). This option is set on the server prior to execution of the
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> and <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> scripts.</dd>
+<dt><code>link_mtu</code></dt>
+<dd>The maximum packet size (not including the IP header) of tunnel data in
+UDP tunnel transport mode. Set prior to <tt class="docutils literal"><span class="pre">--up</span></tt> or <tt class="docutils literal"><span class="pre">--down</span></tt> script
+execution.</dd>
+<dt><code>local</code></dt>
+<dd>The <tt class="docutils literal"><span class="pre">--local</span></tt> parameter. Set on program initiation and reset on
+SIGHUP.</dd>
+<dt><code>local_port</code></dt>
+<dd>The local port number or name, specified by <tt class="docutils literal"><span class="pre">--port</span></tt> or <tt class="docutils literal"><span class="pre">--lport</span></tt>.
+Set on program initiation and reset on SIGHUP.</dd>
+<dt><code>password</code></dt>
+<dd>The password provided by a connecting client. Set prior to
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script execution only when the <tt class="docutils literal"><span class="pre">via-env</span></tt>
+modifier is specified, and deleted from the environment after the script
+returns.</dd>
+<dt><code>proto</code></dt>
+<dd>The <tt class="docutils literal"><span class="pre">--proto</span></tt> parameter. Set on program initiation and reset on
+SIGHUP.</dd>
+<dt><code>remote_{n}</code></dt>
+<dd>The <tt class="docutils literal"><span class="pre">--remote</span></tt> parameter. Set on program initiation and reset on
+SIGHUP.</dd>
+<dt><code>remote_port_{n}</code></dt>
+<dd>The remote port number, specified by <tt class="docutils literal"><span class="pre">--port</span></tt> or <tt class="docutils literal"><span class="pre">--rport</span></tt>. Set on
+program initiation and reset on SIGHUP.</dd>
+<dt><code>route_net_gateway</code></dt>
+<dd>The pre-existing default IP gateway in the system routing table. Set
+prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script execution.</dd>
+<dt><code>route_vpn_gateway</code></dt>
+<dd>The default gateway used by <tt class="docutils literal"><span class="pre">--route</span></tt> options, as specified in either
+the <tt class="docutils literal"><span class="pre">--route-gateway</span></tt> option or the second parameter to
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> when <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> is specified. Set prior to <tt class="docutils literal"><span class="pre">--up</span></tt>
+script execution.</dd>
+<dt><code>route_{parm}_{n}</code></dt>
+<dd><p class="first">A set of variables which define each route to be added, and are set
+prior to <tt class="docutils literal"><span class="pre">--up</span></tt> script execution.</p>
+<p><tt class="docutils literal">parm</tt> will be one of <code>network</code>, <code>netmask&quot;</code>,
+<code>gateway</code>, or <code>metric</code>.</p>
+<p><tt class="docutils literal">n</tt> is the OpenVPN route number, starting from 1.</p>
+<p class="last">If the network or gateway are resolvable DNS names, their IP address
+translations will be recorded rather than their names as denoted on the
+command line or configuration file.</p>
+</dd>
+<dt><code>route_ipv6_{parm}_{n}</code></dt>
+<dd><p class="first">A set of variables which define each IPv6 route to be added, and are
+set prior to <strong>--up</strong> script execution.</p>
+<p><tt class="docutils literal">parm</tt> will be one of <code>network</code>, <code>gateway</code> or
+<code>metric</code>. <tt class="docutils literal">route_ipv6_network_{n}</tt> contains <code>netmask</code>
+as <code>/nnn</code>, unlike IPv4 where it is passed in a separate environment
+variable.</p>
+<p><tt class="docutils literal">n</tt> is the OpenVPN route number, starting from 1.</p>
+<p class="last">If the network or gateway are resolvable DNS names, their IP address
+translations will be recorded rather than their names as denoted on the
+command line or configuration file.</p>
+</dd>
+<dt><code>peer_cert</code></dt>
+<dd>Temporary file name containing the client certificate upon connection.
+Useful in conjunction with <tt class="docutils literal"><span class="pre">--tls-verify</span></tt>.</dd>
+<dt><code>script_context</code></dt>
+<dd>Set to &quot;init&quot; or &quot;restart&quot; prior to up/down script execution. For more
+information, see documentation for <tt class="docutils literal"><span class="pre">--up</span></tt>.</dd>
+<dt><code>script_type</code></dt>
+<dd>Prior to execution of any script, this variable is set to the type of
+script being run. It can be one of the following: <code>up</code>,
+<code>down</code>, <code>ipchange</code>, <code>route-up</code>, <code>tls-verify</code>,
+<code>auth-user-pass-verify</code>, <code>client-connect</code>,
+<code>client-disconnect</code> or <code>learn-address</code>. Set prior to
+execution of any script.</dd>
+<dt><code>signal</code></dt>
+<dd>The reason for exit or restart. Can be one of <code>sigusr1</code>,
+<code>sighup</code>, <code>sigterm</code>, <code>sigint</code>, <code>inactive</code>
+(controlled by <tt class="docutils literal"><span class="pre">--inactive</span></tt> option), <code>ping-exit</code> (controlled
+by <tt class="docutils literal"><span class="pre">--ping-exit</span></tt> option), <code>ping-restart</code> (controlled by
+<tt class="docutils literal"><span class="pre">--ping-restart</span></tt> option), <code>connection-reset</code> (triggered on TCP
+connection reset), <code>error</code> or <code>unknown</code> (unknown signal).
+This variable is set just prior to down script execution.</dd>
+<dt><code>time_ascii</code></dt>
+<dd>Client connection timestamp, formatted as a human-readable time string.
+Set prior to execution of the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</dd>
+<dt><code>time_duration</code></dt>
+<dd>The duration (in seconds) of the client session which is now
+disconnecting. Set prior to execution of the <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt>
+script.</dd>
+<dt><code>time_unix</code></dt>
+<dd>Client connection timestamp, formatted as a unix integer date/time
+value. Set prior to execution of the <tt class="docutils literal"><span class="pre">--client-connect</span></tt> script.</dd>
+<dt><code>tls_digest_{n}</code> / <code>tls_digest_sha256_{n}</code></dt>
+<dd>Contains the certificate SHA1 / SHA256 fingerprint, where <tt class="docutils literal">n</tt> is the
+verification level. Only set for TLS connections. Set prior to execution
+of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> script.</dd>
+<dt><code>tls_id_{n}</code></dt>
+<dd>A series of certificate fields from the remote peer, where <tt class="docutils literal">n</tt> is the
+verification level. Only set for TLS connections. Set prior to execution
+of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> script.</dd>
+<dt><code>tls_serial_{n}</code></dt>
+<dd>The serial number of the certificate from the remote peer, where <tt class="docutils literal">n</tt>
+is the verification level. Only set for TLS connections. Set prior to
+execution of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> script. This is in the form of a decimal
+string like &quot;933971680&quot;, which is suitable for doing serial-based OCSP
+queries (with OpenSSL, do not prepend &quot;0x&quot; to the string) If something
+goes wrong while reading the value from the certificate it will be an
+empty string, so your code should check that. See the
+<code>contrib/OCSP_check/OCSP_check.sh</code> script for an example.</dd>
+<dt><code>tls_serial_hex_{n}</code></dt>
+<dd>Like <code>tls_serial_{n}</code>, but in hex form (e.g.
+<code>12:34:56:78:9A</code>).</dd>
+<dt><code>tun_mtu</code></dt>
+<dd>The MTU of the TUN/TAP device. Set prior to <tt class="docutils literal"><span class="pre">--up</span></tt> or <tt class="docutils literal"><span class="pre">--down</span></tt>
+script execution.</dd>
+<dt><code>trusted_ip</code> / <code>trusted_ip6</code>)</dt>
+<dd>Actual IP address of connecting client or peer which has been
+authenticated. Set prior to execution of <tt class="docutils literal"><span class="pre">--ipchange</span></tt>,
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> and <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> scripts. If using ipv6
+endpoints (udp6, tcp6), <code>trusted_ip6</code> will be set instead.</dd>
+<dt><code>trusted_port</code></dt>
+<dd>Actual port number of connecting client or peer which has been
+authenticated. Set prior to execution of <tt class="docutils literal"><span class="pre">--ipchange</span></tt>,
+<tt class="docutils literal"><span class="pre">--client-connect</span></tt> and <tt class="docutils literal"><span class="pre">--client-disconnect</span></tt> scripts.</dd>
+<dt><code>untrusted_ip</code> / <code>untrusted_ip6</code></dt>
+<dd>Actual IP address of connecting client or peer which has not been
+authenticated yet. Sometimes used to <em>nmap</em> the connecting host in a
+<tt class="docutils literal"><span class="pre">--tls-verify</span></tt> script to ensure it is firewalled properly. Set prior
+to execution of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> and <tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt>
+scripts. If using ipv6 endpoints (udp6, tcp6), <code>untrusted_ip6</code>
+will be set instead.</dd>
+<dt><code>untrusted_port</code></dt>
+<dd>Actual port number of connecting client or peer which has not been
+authenticated yet. Set prior to execution of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> and
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> scripts.</dd>
+<dt><code>username</code></dt>
+<dd>The username provided by a connecting client. Set prior to
+<tt class="docutils literal"><span class="pre">--auth-user-pass-verify</span></tt> script execution only when the
+<code>via-env</code> modifier is specified.</dd>
+<dt><code>X509_{n}_{subject_field}</code></dt>
+<dd><p class="first">An X509 subject field from the remote peer certificate, where <tt class="docutils literal">n</tt> is
+the verification level. Only set for TLS connections. Set prior to
+execution of <tt class="docutils literal"><span class="pre">--tls-verify</span></tt> script. This variable is similar to
+<code>tls_id_{n}</code> except the component X509 subject fields are broken
+out, and no string remapping occurs on these field values (except for
+remapping of control characters to &quot;<code>_</code>&quot;). For example, the
+following variables would be set on the OpenVPN server using the sample
+client certificate in sample-keys (client.crt). Note that the
+verification level is 0 for the client certificate and 1 for the CA
+certificate.</p>
+<pre class="last literal-block">
+X509_0_emailAddress=me&#64;myhost.mydomain
+X509_0_CN=Test-Client
+X509_0_O=OpenVPN-TEST
+X509_0_ST=NA
+X509_0_C=KG
+X509_1_emailAddress=me&#64;myhost.mydomain
+X509_1_O=OpenVPN-TEST
+X509_1_L=BISHKEK
+X509_1_ST=NA
+X509_1_C=KG
+</pre>
+</dd>
+</dl>
+</div>
+<div class="section" id="management-interface-options">
+<h2>Management Interface Options</h2>
+<p>OpenVPN provides a feature rich socket based management interface for both
+server and client mode operations.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Enable a management server on a <tt class="docutils literal"><span class="pre">socket-name</span></tt> Unix socket on those
+platforms supporting it, or on a designated TCP port.</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+management socket-name unix #
+management socket-name unix pw-file # (recommended)
+management IP port # (INSECURE)
+management IP port pw-file #
+</pre>
+<p><tt class="docutils literal"><span class="pre">pw-file</span></tt>, if specified, is a password file where the password must
+be on first line. Instead of a filename it can use the keyword stdin
+which will prompt the user for a password to use when OpenVPN is
+starting.</p>
+<p>For unix sockets, the default behaviour is to create a unix domain
+socket that may be connected to by any process. Use the
+<tt class="docutils literal"><span class="pre">--management-client-user</span></tt> and <tt class="docutils literal"><span class="pre">--management-client-group</span></tt>
+directives to restrict access.</p>
+<p>The management interface provides a special mode where the TCP
+management link can operate over the tunnel itself. To enable this mode,
+set IP to <tt class="docutils literal">tunnel</tt>. Tunnel mode will cause the management interface to
+listen for a TCP connection on the local VPN address of the TUN/TAP
+interface.</p>
+<p><strong>*BEWARE*</strong> of enabling the management interface over TCP. In these cases
+you should <em>ALWAYS</em> make use of <tt class="docutils literal"><span class="pre">pw-file</span></tt> to password protect the
+management interface. Any user who can connect to this TCP <tt class="docutils literal">IP:port</tt>
+will be able to manage and control (and interfere with) the OpenVPN
+process. It is also strongly recommended to set IP to 127.0.0.1
+(localhost) to restrict accessibility of the management server to local
+clients.</p>
+<p>While the management port is designed for programmatic control of
+OpenVPN by other applications, it is possible to telnet to the port,
+using a telnet client in &quot;raw&quot; mode. Once connected, type <code>help</code>
+for a list of commands.</p>
+<p class="last">For detailed documentation on the management interface, see the
+<em>management-notes.txt</em> file in the management folder of the OpenVPN
+source distribution.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-client</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Management interface will connect as a TCP/unix domain client to
+<tt class="docutils literal">IP:port</tt> specified by <tt class="docutils literal"><span class="pre">--management</span></tt> rather than listen as a TCP
+server or on a unix domain socket.</p>
+<p class="last">If the client connection fails to connect or is disconnected, a SIGTERM
+signal will be generated causing OpenVPN to quit.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-client-auth</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Gives management interface client the responsibility to authenticate
+clients after their client certificate has been verified. See
+<code>management-notes.txt</code> in OpenVPN distribution for detailed notes.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-client-group <var>g</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When the management interface is listening on a unix domain socket, only
+allow connections from group <tt class="docutils literal">g</tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-client-pf</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Management interface clients must specify a packet filter file for each
+connecting client. See <code>management-notes.txt</code> in OpenVPN
+distribution for detailed notes.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-client-user <var>u</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>When the management interface is listening on a unix domain socket, only
+allow connections from user <tt class="docutils literal">u</tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-external-cert <var>certificate-hint</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Allows usage for external certificate instead of <tt class="docutils literal"><span class="pre">--cert</span></tt> option
+(client-only). <tt class="docutils literal"><span class="pre">certificate-hint</span></tt> is an arbitrary string which is
+passed to a management interface client as an argument of
+<em>NEED-CERTIFICATE</em> notification. Requires <tt class="docutils literal"><span class="pre">--management-external-key</span></tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-external-key <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Allows usage for external private key file instead of <tt class="docutils literal"><span class="pre">--key</span></tt> option
+(client-only).</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+management-external-key
+management-external-key nopadding
+management-external-key pkcs1
+management-external-key nopadding pkcs1
+</pre>
+<p class="last">The optional parameters <code>nopadding</code> and <code>pkcs1</code> signal
+support for different padding algorithms. See
+<code>doc/mangement-notes.txt</code> for a complete description of this
+feature.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-forget-disconnect</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Make OpenVPN forget passwords when management session disconnects.</p>
+<p class="last">This directive does not affect the <tt class="docutils literal"><span class="pre">--http-proxy</span></tt> username/password.
+It is always cached.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-hold</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Start OpenVPN in a hibernating state, until a client of the management
+interface explicitly starts it with the <code>hold release</code> command.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-log-cache <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Cache the most recent <tt class="docutils literal">n</tt> lines of log file history for usage by the
+management channel.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-query-passwords</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Query management channel for private key password and
+<tt class="docutils literal"><span class="pre">--auth-user-pass</span></tt> username/password. Only query the management
+channel for inputs which ordinarily would have been queried from the
+console.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-query-proxy</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Query management channel for proxy server information for a specific
+<tt class="docutils literal"><span class="pre">--remote</span></tt> (client-only).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-query-remote</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Allow management interface to override <tt class="docutils literal"><span class="pre">--remote</span></tt> directives
+(client-only).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-signal</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Send SIGUSR1 signal to OpenVPN if management session disconnects. This
+is useful when you wish to disconnect an OpenVPN session on user logoff.
+For <tt class="docutils literal"><span class="pre">--management-client</span></tt> this option is not needed since a disconnect
+will always generate a <code>SIGTERM</code>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--management-up-down</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Report tunnel up/down events to management interface.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="plug-in-interface-options">
+<h2>Plug-in Interface Options</h2>
+<p>OpenVPN can be extended by loading external plug-in modules at runtime. These
+plug-ins must be prebuilt and adhere to the OpenVPN Plug-In API.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group">
+<kbd><span class="option">--plugin <var>args</var></span></kbd></td>
+<td><p class="first">Loads an OpenVPN plug-in module.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+plugin module-name
+plugin module-name &quot;arguments&quot;
+</pre>
+<p>The <tt class="docutils literal"><span class="pre">module-name</span></tt> needs to be the first
+argument, indicating the plug-in to load. The second argument is an
+optional init string which will be passed directly to the plug-in.
+If the init consists of multiple arguments it must be enclosed in
+double-quotes (&quot;). Multiple plugin modules may be loaded into one
+OpenVPN process.</p>
+<p>The <tt class="docutils literal"><span class="pre">module-name</span></tt> argument can be just a filename or a filename
+with a relative or absolute path. The format of the filename and path
+defines if the plug-in will be loaded from a default plug-in directory
+or outside this directory.</p>
+<pre class="literal-block">
+--plugin path Effective directory used
+===================== =============================
+ myplug.so DEFAULT_DIR/myplug.so
+ subdir/myplug.so DEFAULT_DIR/subdir/myplug.so
+ ./subdir/myplug.so CWD/subdir/myplug.so
+ /usr/lib/my/plug.so /usr/lib/my/plug.so
+</pre>
+<p><tt class="docutils literal">DEFAULT_DIR</tt> is replaced by the default plug-in directory, which is
+configured at the build time of OpenVPN. <tt class="docutils literal">CWD</tt> is the current directory
+where OpenVPN was started or the directory OpenVPN have switched into
+via the <tt class="docutils literal"><span class="pre">--cd</span></tt> option before the <tt class="docutils literal"><span class="pre">--plugin</span></tt> option.</p>
+<p>For more information and examples on how to build OpenVPN plug-in
+modules, see the README file in the <tt class="docutils literal">plugin</tt> folder of the OpenVPN
+source distribution.</p>
+<p>If you are using an RPM install of OpenVPN, see
+<code>/usr/share/openvpn/plugin</code>. The documentation is in <tt class="docutils literal">doc</tt> and
+the actual plugin modules are in <tt class="docutils literal">lib</tt>.</p>
+<p class="last">Multiple plugin modules can be cascaded, and modules can be used in
+tandem with scripts. The modules will be called by OpenVPN in the order
+that they are declared in the config file. If both a plugin and script
+are configured for the same callback, the script will be called last. If
+the return code of the module/script controls an authentication function
+(such as tls-verify, auth-user-pass-verify, or client-connect), then
+every module and script must return success (<code>0</code>) in order for the
+connection to be authenticated.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="windows-specific-options">
+<h2>Windows-Specific Options</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--allow-nonadmin <var>TAP-adapter</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>(Standalone) Set <tt class="docutils literal"><span class="pre">TAP-adapter</span></tt> to allow access from non-administrative
+accounts. If <tt class="docutils literal"><span class="pre">TAP-adapter</span></tt> is omitted, all TAP adapters on the system
+will be configured to allow non-admin access. The non-admin access
+setting will only persist for the length of time that the TAP-Win32
+device object and driver remain loaded, and will need to be re-enabled
+after a reboot, or if the driver is unloaded and reloaded. This
+directive can only be used by an administrator.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--block-outside-dns</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Block DNS servers on other network adapters to prevent DNS leaks. This
+option prevents any application from accessing TCP or UDP port 53 except
+one inside the tunnel. It uses Windows Filtering Platform (WFP) and
+works on Windows Vista or later.</p>
+<p class="last">This option is considered unknown on non-Windows platforms and
+unsupported on Windows XP, resulting in fatal error. You may want to use
+<tt class="docutils literal"><span class="pre">--setenv</span> opt</tt> or <tt class="docutils literal"><span class="pre">--ignore-unknown-option</span></tt> (not suitable for
+Windows XP) to ignore said error. Note that pushing unknown options from
+server does not trigger fatal errors.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--cryptoapicert <var>select-string</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first"><em>(Windows/OpenSSL Only)</em> Load the certificate and private key from the
+Windows Certificate System Store.</p>
+<p>Use this option instead of <tt class="docutils literal"><span class="pre">--cert</span></tt> and <tt class="docutils literal"><span class="pre">--key</span></tt>.</p>
+<p>This makes it possible to use any smart card, supported by Windows, but
+also any kind of certificate, residing in the Cert Store, where you have
+access to the private key. This option has been tested with a couple of
+different smart cards (GemSAFE, Cryptoflex, and Swedish Post Office eID)
+on the client side, and also an imported PKCS12 software certificate on
+the server side.</p>
+<p>To select a certificate, based on a substring search in the
+certificate's subject:</p>
+<pre class="literal-block">
+cryptoapicert &quot;SUBJ:Peter Runestig&quot;
+</pre>
+<p>To select a certificate, based on certificate's thumbprint:</p>
+<pre class="literal-block">
+cryptoapicert &quot;THUMB:f6 49 24 41 01 b4 ...&quot;
+</pre>
+<p class="last">The thumbprint hex string can easily be copy-and-pasted from the Windows
+Certificate Store GUI.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--dhcp-release</span></kbd></td>
+<td>Ask Windows to release the TAP adapter lease on shutdown. This option
+has no effect now, as it is enabled by default starting with
+OpenVPN 2.4.1.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--dhcp-renew</span></kbd></td>
+<td>Ask Windows to renew the TAP adapter lease on startup. This option is
+normally unnecessary, as Windows automatically triggers a DHCP
+renegotiation on the TAP adapter when it comes up, however if you set
+the TAP-Win32 adapter Media Status property to &quot;Always Connected&quot;, you
+may need this flag.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ip-win32 <var>method</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">When using <tt class="docutils literal"><span class="pre">--ifconfig</span></tt> on Windows, set the TAP-Win32 adapter IP
+address and netmask using <tt class="docutils literal">method</tt>. Don't use this option unless you
+are also using <tt class="docutils literal"><span class="pre">--ifconfig</span></tt>.</p>
+<dl class="last docutils">
+<dt><code>manual</code></dt>
+<dd>Don't set the IP address or netmask automatically. Instead
+output a message to the console telling the user to configure the
+adapter manually and indicating the IP/netmask which OpenVPN
+expects the adapter to be set to.</dd>
+<dt><code>dynamic [offset] [lease-time]</code></dt>
+<dd><p class="first">Automatically set the IP address and netmask by replying to DHCP
+query messages generated by the kernel. This mode is probably the
+&quot;cleanest&quot; solution for setting the TCP/IP properties since it
+uses the well-known DHCP protocol. There are, however, two
+prerequisites for using this mode:</p>
+<ol class="arabic simple">
+<li>The TCP/IP properties for the TAP-Win32 adapter must be set
+to &quot;Obtain an IP address automatically&quot;, and</li>
+<li>OpenVPN needs to claim an IP address in the subnet for use
+as the virtual DHCP server address.</li>
+</ol>
+<p>By default in <tt class="docutils literal"><span class="pre">--dev</span> tap</tt> mode, OpenVPN will take the normally
+unused first address in the subnet. For example, if your subnet is
+<code>192.168.4.0 netmask 255.255.255.0</code>, then OpenVPN will take
+the IP address <code>192.168.4.0</code> to use as the virtual DHCP
+server address. In <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> mode, OpenVPN will cause the DHCP
+server to masquerade as if it were coming from the remote endpoint.</p>
+<p>The optional offset parameter is an integer which is &gt; <code>-256</code>
+and &lt; <code>256</code> and which defaults to -1. If offset is positive,
+the DHCP server will masquerade as the IP address at network
+address + offset. If offset is negative, the DHCP server will
+masquerade as the IP address at broadcast address + offset.</p>
+<p>The Windows <code>ipconfig /all</code> command can be used to show what
+Windows thinks the DHCP server address is. OpenVPN will &quot;claim&quot;
+this address, so make sure to use a free address. Having said that,
+different OpenVPN instantiations, including different ends of
+the same connection, can share the same virtual DHCP server
+address.</p>
+<p class="last">The <tt class="docutils literal"><span class="pre">lease-time</span></tt> parameter controls the lease time of the DHCP
+assignment given to the TAP-Win32 adapter, and is denoted in
+seconds. Normally a very long lease time is preferred because it
+prevents routes involving the TAP-Win32 adapter from being lost
+when the system goes to sleep. The default lease time is one year.</p>
+</dd>
+<dt><code>netsh</code></dt>
+<dd>Automatically set the IP address and netmask using the Windows
+command-line &quot;netsh&quot; command. This method appears to work correctly
+on Windows XP but not Windows 2000.</dd>
+<dt><code>ipapi</code></dt>
+<dd>Automatically set the IP address and netmask using the Windows IP
+Helper API. This approach does not have ideal semantics, though
+testing has indicated that it works okay in practice. If you use
+this option, it is best to leave the TCP/IP properties for the
+TAP-Win32 adapter in their default state, i.e. &quot;Obtain an IP
+address automatically.&quot;</dd>
+<dt><code>adaptive</code> (Default)</dt>
+<dd><p class="first">Try <code>dynamic</code> method initially and fail over to <code>netsh</code>
+if the DHCP negotiation with the TAP-Win32 adapter does not succeed
+in 20 seconds. Such failures have been known to occur when certain
+third-party firewall packages installed on the client machine block
+the DHCP negotiation used by the TAP-Win32 adapter. Note that if
+the <code>netsh</code> failover occurs, the TAP-Win32 adapter TCP/IP
+properties will be reset from DHCP to static, and this will cause
+future OpenVPN startups using the <code>adaptive</code> mode to use
+<code>netsh</code> immediately, rather than trying <code>dynamic</code> first.</p>
+<p class="last">To &quot;unstick&quot; the <code>adaptive</code> mode from using <code>netsh</code>,
+run OpenVPN at least once using the <code>dynamic</code> mode to restore
+the TAP-Win32 adapter TCP/IP properties to a DHCP configuration.</p>
+</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--pause-exit</span></kbd></td>
+<td>Put up a &quot;press any key to continue&quot; message on the console prior to
+OpenVPN program exit. This option is automatically used by the Windows
+explorer when OpenVPN is run on a configuration file using the
+right-click explorer menu.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--register-dns</span></kbd></td>
+<td>Run <code>ipconfig /flushdns</code> and <code>ipconfig /registerdns</code> on
+connection initiation. This is known to kick Windows into recognizing
+pushed DNS servers.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--route-method <var>m</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Which method <tt class="docutils literal">m</tt> to use for adding routes on Windows?</p>
+<dl class="last docutils">
+<dt><code>adaptive</code> (default)</dt>
+<dd>Try IP helper API first. If that fails, fall back to the route.exe
+shell command.</dd>
+<dt><code>ipapi</code></dt>
+<dd>Use IP helper API.</dd>
+<dt><code>exe</code></dt>
+<dd>Call the route.exe shell command.</dd>
+</dl>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--service <var>args</var></span></kbd></td>
+<td><p class="first">Should be used when OpenVPN is being automatically executed by another
+program in such a context that no interaction with the user via display
+or keyboard is possible.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+service exit-event [0|1]
+</pre>
+<p>In general, end-users should never need to explicitly use this option,
+as it is automatically added by the OpenVPN service wrapper when a given
+OpenVPN configuration is being run as a service.</p>
+<p><tt class="docutils literal"><span class="pre">exit-event</span></tt> is the name of a Windows global event object, and OpenVPN
+will continuously monitor the state of this event object and exit when
+it becomes signaled.</p>
+<p>The second parameter indicates the initial state of <tt class="docutils literal"><span class="pre">exit-event</span></tt> and
+normally defaults to 0.</p>
+<p>Multiple OpenVPN processes can be simultaneously executed with the same
+<tt class="docutils literal"><span class="pre">exit-event</span></tt> parameter. In any case, the controlling process can
+signal <tt class="docutils literal"><span class="pre">exit-event</span></tt>, causing all such OpenVPN processes to exit.</p>
+<p class="last">When executing an OpenVPN process using the <tt class="docutils literal"><span class="pre">--service</span></tt> directive,
+OpenVPN will probably not have a console window to output status/error
+messages, therefore it is useful to use <tt class="docutils literal"><span class="pre">--log</span></tt> or <tt class="docutils literal"><span class="pre">--log-append</span></tt> to
+write these messages to a file.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--show-adapters</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>(Standalone) Show available TAP-Win32 adapters which can be selected
+using the <tt class="docutils literal"><span class="pre">--dev-node</span></tt> option. On non-Windows systems, the
+<tt class="docutils literal">ifconfig</tt>(8) command provides similar functionality.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-net</span></kbd></td>
+<td>(Standalone) Show OpenVPN's view of the system routing table and network
+adapter list.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--show-net-up</span></kbd></td>
+<td>Output OpenVPN's view of the system routing table and network adapter
+list to the syslog or log file after the TUN/TAP adapter has been
+brought up and any routes have been added.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--show-valid-subnets</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">(Standalone) Show valid subnets for <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> emulation. Since the
+TAP-Win32 driver exports an ethernet interface to Windows, and since TUN
+devices are point-to-point in nature, it is necessary for the TAP-Win32
+driver to impose certain constraints on TUN endpoint address selection.</p>
+<p class="last">Namely, the point-to-point endpoints used in TUN device emulation must
+be the middle two addresses of a /30 subnet (netmask 255.255.255.252).</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--tap-sleep <var>n</var></span></kbd></td>
+<td><p class="first">Cause OpenVPN to sleep for <tt class="docutils literal">n</tt> seconds immediately after the TAP-Win32
+adapter state is set to &quot;connected&quot;.</p>
+<p class="last">This option is intended to be used to troubleshoot problems with the
+<tt class="docutils literal"><span class="pre">--ifconfig</span></tt> and <tt class="docutils literal"><span class="pre">--ip-win32</span></tt> options, and is used to give the
+TAP-Win32 adapter time to come up before Windows IP Helper API
+operations are applied to it.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--win-sys <var>path</var></span></kbd></td>
+<td><p class="first">Set the Windows system directory pathname to use when looking for system
+executables such as <tt class="docutils literal">route.exe</tt> and <tt class="docutils literal">netsh.exe</tt>. By default, if this
+directive is not specified, OpenVPN will use the SystemRoot environment
+variable.</p>
+<p class="last">This option has changed behaviour since OpenVPN 2.3. Earlier you had to
+define <tt class="docutils literal"><span class="pre">--win-sys</span> env</tt> to use the SystemRoot environment variable,
+otherwise it defaulted to <code>C:\\WINDOWS</code>. It is not needed to use
+the <tt class="docutils literal">env</tt> keyword any more, and it will just be ignored. A warning is
+logged when this is found in the configuration file.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--windows-driver <var>drv</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Specifies which tun driver to use. Values are <code>tap-windows6</code>
+(default) and <code>wintun</code>. This is a Windows-only option.
+<code>wintun</code>&quot; requires <tt class="docutils literal"><span class="pre">--dev</span> tun</tt> and the OpenVPN process to run
+elevated, or be invoked using the Interactive Service.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="standalone-debug-options">
+<h2>Standalone Debug Options</h2>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--show-gateway <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">(Standalone) Show current IPv4 and IPv6 default gateway and interface
+towards the gateway (if the protocol in question is enabled).</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+--show-gateway
+--show-gateway IPv6-target
+</pre>
+<p class="last">For IPv6 this queries the route towards ::/128, or the specified IPv6
+target address if passed as argument.
+For IPv4 on Linux, Windows, MacOS and BSD it looks for a 0.0.0.0/0 route.
+If there are more specific routes, the result will not always be matching
+the route of the IPv4 packets to the VPN gateway.</p>
+</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="advanced-expert-options">
+<h2>Advanced Expert Options</h2>
+<p>These are options only required when special tweaking is needed, often
+used when debugging or testing out special usage scenarios.</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--hash-size <var>args</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Set the size of the real address hash table to <tt class="docutils literal">r</tt> and the virtual
+address table to <tt class="docutils literal">v</tt>.</p>
+<p>Valid syntax:</p>
+<pre class="literal-block">
+hash-size r v
+</pre>
+<p class="last">By default, both tables are sized at 256 buckets.</p>
+</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--bcast-buffers <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Allocate <tt class="docutils literal">n</tt> buffers for broadcast datagrams (default <code>256</code>).</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--persist-local-ip</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Preserve initially resolved local IP address and port number across
+<tt class="docutils literal">SIGUSR1</tt> or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> restarts.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--persist-remote-ip</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Preserve most recently authenticated remote IP address and port number
+across <code>SIGUSR1</code> or <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> restarts.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--prng <var>args</var></span></kbd></td>
+<td><p class="first"><em>(Advanced)</em> Change the PRNG (Pseudo-random number generator) parameters</p>
+<p>Valid syntaxes:</p>
+<pre class="literal-block">
+prng alg
+prng alg nsl
+</pre>
+<p>Changes the PRNG to use digest algorithm <strong>alg</strong> (default <code>sha1</code>),
+and set <tt class="docutils literal">nsl</tt> (default <code>16</code>) to the size in bytes of the nonce
+secret length (between 16 and 64).</p>
+<p class="last">Set <tt class="docutils literal">alg</tt> to <code>none</code> to disable the PRNG and use the OpenSSL
+RAND_bytes function instead for all of OpenVPN's pseudo-random number
+needs.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--rcvbuf <var>size</var></span></kbd></td>
+<td>Set the TCP/UDP socket receive buffer size. Defaults to operating system
+default.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--shaper <var>n</var></span></kbd></td>
+<td><p class="first">Limit bandwidth of outgoing tunnel data to <tt class="docutils literal">n</tt> bytes per second on the
+TCP/UDP port. Note that this will only work if mode is set to
+<code>p2p</code>. If you want to limit the bandwidth in both directions, use
+this option on both peers.</p>
+<p>OpenVPN uses the following algorithm to implement traffic shaping: Given
+a shaper rate of <tt class="docutils literal">n</tt> bytes per second, after a datagram write of <tt class="docutils literal">b</tt>
+bytes is queued on the TCP/UDP port, wait a minimum of <tt class="docutils literal">(b / n)</tt>
+seconds before queuing the next write.</p>
+<p>It should be noted that OpenVPN supports multiple tunnels between the
+same two peers, allowing you to construct full-speed and reduced
+bandwidth tunnels at the same time, routing low-priority data such as
+off-site backups over the reduced bandwidth tunnel, and other data over
+the full-speed tunnel.</p>
+<p>Also note that for low bandwidth tunnels (under 1000 bytes per second),
+you should probably use lower MTU values as well (see above), otherwise
+the packet latency will grow so large as to trigger timeouts in the TLS
+layer and TCP connections running over the tunnel.</p>
+<p class="last">OpenVPN allows <tt class="docutils literal">n</tt> to be between 100 bytes/sec and 100 Mbytes/sec.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--sndbuf <var>size</var></span></kbd></td>
+<td>Set the TCP/UDP socket send buffer size. Defaults to operating system
+default.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--tcp-queue-limit <var>n</var></span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td><p class="first">Maximum number of output packets queued before TCP (default <code>64</code>).</p>
+<p class="last">When OpenVPN is tunneling data from a TUN/TAP device to a remote client
+over a TCP connection, it is possible that the TUN/TAP device might
+produce data at a faster rate than the TCP connection can support. When
+the number of output packets queued before sending to the TCP socket
+reaches this limit for a given client connection, OpenVPN will start to
+drop outgoing packets directed at this client.</p>
+</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--txqueuelen <var>n</var></span></kbd></td>
+<td><em>(Linux only)</em> Set the TX queue length on the TUN/TAP interface.
+Currently defaults to operating system default.</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+<div class="section" id="unsupported-options">
+<h1>UNSUPPORTED OPTIONS</h1>
+<p>Options listed in this section have been removed from OpenVPN and are no
+longer supported</p>
+<table class="docutils option-list" frame="void" rules="none">
+<col class="option" />
+<col class="description" />
+<tbody valign="top">
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--client-cert-not-required</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Removed in OpenVPN 2.5. This should be replaxed with
+<tt class="docutils literal"><span class="pre">--verify-client-cert</span> none</tt>.</td></tr>
+<tr><td class="option-group" colspan="2">
+<kbd><span class="option">--ifconfig-pool-linear</span></kbd></td>
+</tr>
+<tr><td>&nbsp;</td><td>Removed in OpenVPN 2.5. This should be replaced with <tt class="docutils literal"><span class="pre">--topology</span> p2p</tt>.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--key-method</span></kbd></td>
+<td>Removed in OpenVPN 2.5. This option should not be used, as using the old
+<tt class="docutils literal"><span class="pre">key-method</span></tt> weakens the VPN tunnel security. The old <tt class="docutils literal"><span class="pre">key-method</span></tt>
+was also only needed when the remote side was older than OpenVPN 2.0.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--no-iv</span></kbd></td>
+<td>Removed in OpenVPN 2.5. This option should not be used as it weakens the
+VPN tunnel security. This has been a NOOP option since OpenVPN 2.4.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--no-replay</span></kbd></td>
+<td>Removed in OpenVPN 2.5. This option should not be used as it weakens the
+VPN tunnel security.</td></tr>
+<tr><td class="option-group">
+<kbd><span class="option">--ns-cert-type</span></kbd></td>
+<td>Removed in OpenVPN 2.5. The <tt class="docutils literal">nsCertType</tt> field is no longer supported
+in recent SSL/TLS libraries. If your certificates does not include <em>key
+usage</em> and <em>extended key usage</em> fields, they must be upgraded and the
+<tt class="docutils literal"><span class="pre">--remote-cert-tls</span></tt> option should be used instead.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="connection-profiles">
+<h1>CONNECTION PROFILES</h1>
+<p>Client configuration files may contain multiple remote servers which
+it will attempt to connect against. But there are some configuration
+options which are related to specific <tt class="docutils literal"><span class="pre">--remote</span></tt> options. For these
+use cases, connection profiles are the solution.</p>
+<p>By enacpulating the <tt class="docutils literal"><span class="pre">--remote</span></tt> option and related options within
+<tt class="docutils literal">&lt;connection&gt;</tt> and <tt class="docutils literal">&lt;/connection&gt;</tt>, these options are handled as a
+group.</p>
+<p>An OpenVPN client will try each connection profile sequentially until it
+achieves a successful connection.</p>
+<p><tt class="docutils literal"><span class="pre">--remote-random</span></tt> can be used to initially &quot;scramble&quot; the connection
+list.</p>
+<p>Here is an example of connection profile usage:</p>
+<pre class="literal-block">
+client
+dev tun
+
+&lt;connection&gt;
+remote 198.19.34.56 1194 udp
+&lt;/connection&gt;
+
+&lt;connection&gt;
+remote 198.19.34.56 443 tcp
+&lt;/connection&gt;
+
+&lt;connection&gt;
+remote 198.19.34.56 443 tcp
+http-proxy 192.168.0.8 8080
+&lt;/connection&gt;
+
+&lt;connection&gt;
+remote 198.19.36.99 443 tcp
+http-proxy 192.168.0.8 8080
+&lt;/connection&gt;
+
+persist-key
+persist-tun
+pkcs12 client.p12
+remote-cert-tls server
+verb 3
+</pre>
+<p>First we try to connect to a server at 198.19.34.56:1194 using UDP. If
+that fails, we then try to connect to 198.19.34.56:443 using TCP. If
+that also fails, then try connecting through an HTTP proxy at
+192.168.0.8:8080 to 198.19.34.56:443 using TCP. Finally, try to connect
+through the same proxy to a server at 198.19.36.99:443 using TCP.</p>
+<p>The following OpenVPN options may be used inside of a <tt class="docutils literal">&lt;connection&gt;</tt>
+block:</p>
+<p><tt class="docutils literal">bind</tt>, <tt class="docutils literal"><span class="pre">connect-retry</span></tt>, <tt class="docutils literal"><span class="pre">connect-retry-max</span></tt>, <tt class="docutils literal"><span class="pre">connect-timeout</span></tt>,
+<tt class="docutils literal"><span class="pre">explicit-exit-notify</span></tt>, <tt class="docutils literal">float</tt>, <tt class="docutils literal">fragment</tt>, <tt class="docutils literal"><span class="pre">http-proxy</span></tt>,
+<tt class="docutils literal"><span class="pre">http-proxy-option</span></tt>, <tt class="docutils literal"><span class="pre">key-direction</span></tt>, <tt class="docutils literal"><span class="pre">link-mtu</span></tt>, <tt class="docutils literal">local</tt>,
+<tt class="docutils literal">lport</tt>, <tt class="docutils literal">mssfix</tt>, <tt class="docutils literal"><span class="pre">mtu-disc</span></tt>, <tt class="docutils literal">nobind</tt>, <tt class="docutils literal">port</tt>, <tt class="docutils literal">proto</tt>,
+<tt class="docutils literal">remote</tt>, <tt class="docutils literal">rport</tt>, <tt class="docutils literal"><span class="pre">socks-proxy</span></tt>, <tt class="docutils literal"><span class="pre">tls-auth</span></tt>, <tt class="docutils literal"><span class="pre">tls-crypt</span></tt>,
+<tt class="docutils literal"><span class="pre">tun-mtu</span> and</tt>, <tt class="docutils literal"><span class="pre">tun-mtu-extra</span></tt>.</p>
+<p>A defaulting mechanism exists for specifying options to apply to all
+<tt class="docutils literal">&lt;connection&gt;</tt> profiles. If any of the above options (with the
+exception of <tt class="docutils literal">remote</tt> ) appear outside of a <tt class="docutils literal">&lt;connection&gt;</tt> block,
+but in a configuration file which has one or more <tt class="docutils literal">&lt;connection&gt;</tt>
+blocks, the option setting will be used as a default for
+<tt class="docutils literal">&lt;connection&gt;</tt> blocks which follow it in the configuration file.</p>
+<p>For example, suppose the <tt class="docutils literal">nobind</tt> option were placed in the sample
+configuration file above, near the top of the file, before the first
+<tt class="docutils literal">&lt;connection&gt;</tt> block. The effect would be as if <tt class="docutils literal">nobind</tt> were
+declared in all <tt class="docutils literal">&lt;connection&gt;</tt> blocks below it.</p>
+</div>
+<div class="section" id="inline-file-support">
+<h1>INLINE FILE SUPPORT</h1>
+<p>OpenVPN allows including files in the main configuration for the <tt class="docutils literal"><span class="pre">--ca</span></tt>,
+<tt class="docutils literal"><span class="pre">--cert</span></tt>, <tt class="docutils literal"><span class="pre">--dh</span></tt>, <tt class="docutils literal"><span class="pre">--extra-certs</span></tt>, <tt class="docutils literal"><span class="pre">--key</span></tt>, <tt class="docutils literal"><span class="pre">--pkcs12</span></tt>,
+<tt class="docutils literal"><span class="pre">--secret</span></tt>, <tt class="docutils literal"><span class="pre">--crl-verify</span></tt>, <tt class="docutils literal"><span class="pre">--http-proxy-user-pass</span></tt>, <tt class="docutils literal"><span class="pre">--tls-auth</span></tt>,
+<tt class="docutils literal"><span class="pre">--auth-gen-token-secret</span></tt>, <tt class="docutils literal"><span class="pre">--tls-crypt</span></tt> and <tt class="docutils literal"><span class="pre">--tls-crypt-v2</span></tt>
+options.</p>
+<p>Each inline file started by the line <tt class="docutils literal">&lt;option&gt;</tt> and ended by the line
+<tt class="docutils literal">&lt;/option&gt;</tt></p>
+<p>Here is an example of an inline file usage</p>
+<pre class="literal-block">
+&lt;cert&gt;
+-----BEGIN CERTIFICATE-----
+[...]
+-----END CERTIFICATE-----
+&lt;/cert&gt;
+</pre>
+<p>When using the inline file feature with <tt class="docutils literal"><span class="pre">--pkcs12</span></tt> the inline file has
+to be base64 encoded. Encoding of a .p12 file into base64 can be done
+for example with OpenSSL by running <code>openssl base64 -in input.p12</code></p>
+</div>
+<div class="section" id="signals">
+<h1>SIGNALS</h1>
+<dl class="docutils">
+<dt><code>SIGHUP</code></dt>
+<dd>Cause OpenVPN to close all TUN/TAP and network connections, restart,
+re-read the configuration file (if any), and reopen TUN/TAP and network
+connections.</dd>
+<dt><code>SIGUSR1</code></dt>
+<dd><p class="first">Like <code>SIGHUP`</code>, except don't re-read configuration file, and
+possibly don't close and reopen TUN/TAP device, re-read key files,
+preserve local IP address/port, or preserve most recently authenticated
+remote IP address/port based on <tt class="docutils literal"><span class="pre">--persist-tun</span></tt>, <tt class="docutils literal"><span class="pre">--persist-key</span></tt>,
+<tt class="docutils literal"><span class="pre">--persist-local-ip</span></tt> and <tt class="docutils literal"><span class="pre">--persist-remote-ip</span></tt> options respectively
+(see above).</p>
+<p>This signal may also be internally generated by a timeout condition,
+governed by the <tt class="docutils literal"><span class="pre">--ping-restart</span></tt> option.</p>
+<p class="last">This signal, when combined with <tt class="docutils literal"><span class="pre">--persist-remote-ip</span></tt>, may be sent
+when the underlying parameters of the host's network interface change
+such as when the host is a DHCP client and is assigned a new IP address.
+See <tt class="docutils literal"><span class="pre">--ipchange</span></tt> for more information.</p>
+</dd>
+<dt><code>SIGUSR2</code></dt>
+<dd>Causes OpenVPN to display its current statistics (to the syslog file if
+<tt class="docutils literal"><span class="pre">--daemon</span></tt> is used, or stdout otherwise).</dd>
+<dt><code>SIGINT</code>, <code>SIGTERM</code></dt>
+<dd>Causes OpenVPN to exit gracefully.</dd>
+</dl>
+</div>
+<div class="section" id="faq">
+<h1>FAQ</h1>
+<p><a class="reference external" href="https://community.openvpn.net/openvpn/wiki/FAQ">https://community.openvpn.net/openvpn/wiki/FAQ</a></p>
+</div>
+<div class="section" id="howto">
+<h1>HOWTO</h1>
+<p>For a more comprehensive guide to setting up OpenVPN in a production
+setting, see the OpenVPN HOWTO at
+<a class="reference external" href="https://openvpn.net/community-resources/how-to/">https://openvpn.net/community-resources/how-to/</a></p>
+</div>
+<div class="section" id="protocol">
+<h1>PROTOCOL</h1>
+<p>For a description of OpenVPN's underlying protocol, see
+<a class="reference external" href="https://openvpn.net/community-resources/openvpn-protocol/">https://openvpn.net/community-resources/openvpn-protocol/</a></p>
+</div>
+<div class="section" id="web">
+<h1>WEB</h1>
+<p>OpenVPN's web site is at <a class="reference external" href="https://openvpn.net/">https://openvpn.net/</a></p>
+<p>Go here to download the latest version of OpenVPN, subscribe to the
+mailing lists, read the mailing list archives, or browse the SVN
+repository.</p>
+</div>
+<div class="section" id="bugs">
+<h1>BUGS</h1>
+<p>Report all bugs to the OpenVPN team <a class="reference external" href="mailto:info&#64;openvpn.net">info&#64;openvpn.net</a></p>
+</div>
+<div class="section" id="see-also">
+<h1>SEE ALSO</h1>
+<p><tt class="docutils literal"><span class="pre">openvpn-examples</span></tt>(5),
+<tt class="docutils literal">dhcpcd</tt>(8),
+<tt class="docutils literal">ifconfig</tt>(8),
+<tt class="docutils literal">openssl</tt>(1),
+<tt class="docutils literal">route</tt>(8),
+<tt class="docutils literal">scp</tt>(1)
+<tt class="docutils literal">ssh</tt>(1)</p>
+</div>
+<div class="section" id="notes">
+<h1>NOTES</h1>
+<p>This product includes software developed by the OpenSSL Project
+(<a class="reference external" href="https://www.openssl.org/">https://www.openssl.org/</a>)</p>
+<p>For more information on the TLS protocol, see
+<a class="reference external" href="http://www.ietf.org/rfc/rfc2246.txt">http://www.ietf.org/rfc/rfc2246.txt</a></p>
+<p>For more information on the LZO real-time compression library see
+<a class="reference external" href="https://www.oberhumer.com/opensource/lzo/">https://www.oberhumer.com/opensource/lzo/</a></p>
+</div>
+<div class="section" id="copyright">
+<h1>COPYRIGHT</h1>
+<p>Copyright (C) 2002-2020 OpenVPN Inc This program is free software; you
+can redistribute it and/or modify it under the terms of the GNU General
+Public License version 2 as published by the Free Software Foundation.</p>
+</div>
+<div class="section" id="authors">
+<h1>AUTHORS</h1>
+<p>James Yonan <a class="reference external" href="mailto:james&#64;openvpn.net">james&#64;openvpn.net</a></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/doc/openvpn.8.rst b/doc/openvpn.8.rst
new file mode 100644
index 0000000..9954674
--- /dev/null
+++ b/doc/openvpn.8.rst
@@ -0,0 +1,170 @@
+=========
+ openvpn
+=========
+-------------------------
+ Secure IP tunnel daemon
+-------------------------
+
+:Manual section: 8
+:Manual group: System Manager's Manual
+
+
+
+SYNOPSIS
+========
+| ``openvpn`` [ options ... ]
+| ``openvpn`` ``--help``
+
+
+
+INTRODUCTION
+============
+
+OpenVPN is an open source VPN daemon by James Yonan. Because OpenVPN
+tries to be a universal VPN tool offering a great deal of flexibility,
+there are a lot of options on this manual page. If you're new to
+OpenVPN, you might want to skip ahead to the examples section where you
+will see how to construct simple VPNs on the command line without even
+needing a configuration file.
+
+Also note that there's more documentation and examples on the OpenVPN
+web site: https://openvpn.net/
+
+And if you would like to see a shorter version of this manual, see the
+openvpn usage message which can be obtained by running **openvpn**
+without any parameters.
+
+
+
+DESCRIPTION
+===========
+
+OpenVPN is a robust and highly flexible VPN daemon. OpenVPN supports
+SSL/TLS security, ethernet bridging, TCP or UDP tunnel transport through
+proxies or NAT, support for dynamic IP addresses and DHCP, scalability
+to hundreds or thousands of users, and portability to most major OS
+platforms.
+
+OpenVPN is tightly bound to the OpenSSL library, and derives much of its
+crypto capabilities from it.
+
+OpenVPN supports conventional encryption using a pre-shared secret key
+**(Static Key mode)** or public key security **(SSL/TLS mode)** using
+client & server certificates. OpenVPN also supports non-encrypted
+TCP/UDP tunnels.
+
+OpenVPN is designed to work with the **TUN/TAP** virtual networking
+interface that exists on most platforms.
+
+Overall, OpenVPN aims to offer many of the key features of IPSec but
+with a relatively lightweight footprint.
+
+
+
+OPTIONS
+=======
+
+OpenVPN allows any option to be placed either on the command line or in
+a configuration file. Though all command line options are preceded by a
+double-leading-dash ("--"), this prefix can be removed when an option is
+placed in a configuration file.
+
+.. include:: man-sections/generic-options.rst
+.. include:: man-sections/log-options.rst
+.. include:: man-sections/protocol-options.rst
+.. include:: man-sections/client-options.rst
+.. include:: man-sections/server-options.rst
+.. include:: man-sections/encryption-options.rst
+.. include:: man-sections/cipher-negotiation.rst
+.. include:: man-sections/network-config.rst
+.. include:: man-sections/script-options.rst
+.. include:: man-sections/management-options.rst
+.. include:: man-sections/plugin-options.rst
+.. include:: man-sections/windows-options.rst
+.. include:: man-sections/advanced-options.rst
+.. include:: man-sections/unsupported-options.rst
+.. include:: man-sections/connection-profiles.rst
+.. include:: man-sections/inline-files.rst
+.. include:: man-sections/signals.rst
+
+
+FAQ
+===
+
+https://community.openvpn.net/openvpn/wiki/FAQ
+
+
+
+HOWTO
+=====
+
+For a more comprehensive guide to setting up OpenVPN in a production
+setting, see the OpenVPN HOWTO at
+https://openvpn.net/community-resources/how-to/
+
+
+
+PROTOCOL
+========
+
+For a description of OpenVPN's underlying protocol, see
+https://openvpn.net/community-resources/openvpn-protocol/
+
+
+
+WEB
+===
+
+OpenVPN's web site is at https://openvpn.net/
+
+Go here to download the latest version of OpenVPN, subscribe to the
+mailing lists, read the mailing list archives, or browse the SVN
+repository.
+
+
+
+BUGS
+====
+
+Report all bugs to the OpenVPN team info@openvpn.net
+
+
+
+SEE ALSO
+========
+
+``openvpn-examples``\(5),
+``dhcpcd``\(8),
+``ifconfig``\(8),
+``openssl``\(1),
+``route``\(8),
+``scp``\(1)
+``ssh``\(1)
+
+
+
+NOTES
+=====
+
+This product includes software developed by the OpenSSL Project
+(https://www.openssl.org/)
+
+For more information on the TLS protocol, see
+http://www.ietf.org/rfc/rfc2246.txt
+
+For more information on the LZO real-time compression library see
+https://www.oberhumer.com/opensource/lzo/
+
+
+
+COPYRIGHT
+=========
+
+Copyright (C) 2002-2020 OpenVPN Inc This program is free software; you
+can redistribute it and/or modify it under the terms of the GNU General
+Public License version 2 as published by the Free Software Foundation.
+
+AUTHORS
+=======
+
+James Yonan james@openvpn.net
diff --git a/include/Makefile.am b/include/Makefile.am
index 484e4e1..bd4f21d 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
diff --git a/include/Makefile.in b/include/Makefile.in
index 67fbbc8..ed52b3d 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -164,8 +164,8 @@ am__uninstall_files_from_dir = { \
}
am__installdirs = "$(DESTDIR)$(includedir)"
HEADERS = $(include_HEADERS)
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
- $(LISP)openvpn-plugin.h.in
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+ openvpn-plugin.h.in
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
@@ -198,7 +198,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -212,6 +213,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -239,7 +241,6 @@ 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@
@@ -290,6 +291,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -353,6 +356,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/include/openvpn-msg.h b/include/openvpn-msg.h
index 66177a2..83344ee 100644
--- a/include/openvpn-msg.h
+++ b/include/openvpn-msg.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2013-2018 Heiko Hund <heiko.hund@sophos.com>
+ * Copyright (C) 2013-2021 Heiko Hund <heiko.hund@sophos.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
@@ -39,6 +39,8 @@ typedef enum {
msg_del_block_dns,
msg_register_dns,
msg_enable_dhcp,
+ msg_register_ring_buffers,
+ msg_set_mtu
} message_type_t;
typedef struct {
@@ -117,4 +119,20 @@ typedef struct {
interface_t iface;
} enable_dhcp_message_t;
+typedef struct {
+ message_header_t header;
+ HANDLE device;
+ HANDLE send_ring_handle;
+ HANDLE receive_ring_handle;
+ HANDLE send_tail_moved;
+ HANDLE receive_tail_moved;
+} register_ring_buffers_message_t;
+
+typedef struct {
+ message_header_t header;
+ interface_t iface;
+ short family;
+ int mtu;
+} set_mtu_message_t;
+
#endif /* ifndef OPENVPN_MSG_H_ */
diff --git a/include/openvpn-plugin.h b/include/openvpn-plugin.h
index 03399f9..934248d 100644
--- a/include/openvpn-plugin.h
+++ b/include/openvpn-plugin.h
@@ -6,7 +6,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -27,7 +27,6 @@
#define OPENVPN_PLUGIN_VERSION 3
-#ifdef ENABLE_CRYPTO
#ifdef ENABLE_CRYPTO_MBEDTLS
#include <mbedtls/x509_crt.h>
#ifndef __OPENVPN_X509_CERT_T_DECLARED
@@ -41,7 +40,6 @@ typedef mbedtls_x509_crt openvpn_x509_cert_t;
typedef X509 openvpn_x509_cert_t;
#endif
#endif
-#endif
#include <stdarg.h>
#include <stddef.h>
@@ -54,8 +52,8 @@ extern "C" {
* This is will not be the complete version
*/
#define OPENVPN_VERSION_MAJOR 2
-#define OPENVPN_VERSION_MINOR 4
-#define OPENVPN_VERSION_PATCH ".9"
+#define OPENVPN_VERSION_MINOR 5
+#define OPENVPN_VERSION_PATCH ".4"
/*
* Plug-in types. These types correspond to the set of script callbacks
@@ -119,20 +117,22 @@ extern "C" {
* FUNC: openvpn_plugin_client_destructor_v1 (top-level "generic" client)
* FUNC: openvpn_plugin_close_v1
*/
-#define OPENVPN_PLUGIN_UP 0
-#define OPENVPN_PLUGIN_DOWN 1
-#define OPENVPN_PLUGIN_ROUTE_UP 2
-#define OPENVPN_PLUGIN_IPCHANGE 3
-#define OPENVPN_PLUGIN_TLS_VERIFY 4
-#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
-#define OPENVPN_PLUGIN_CLIENT_CONNECT 6
-#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7
-#define OPENVPN_PLUGIN_LEARN_ADDRESS 8
-#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9
-#define OPENVPN_PLUGIN_TLS_FINAL 10
-#define OPENVPN_PLUGIN_ENABLE_PF 11
-#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12
-#define OPENVPN_PLUGIN_N 13
+#define OPENVPN_PLUGIN_UP 0
+#define OPENVPN_PLUGIN_DOWN 1
+#define OPENVPN_PLUGIN_ROUTE_UP 2
+#define OPENVPN_PLUGIN_IPCHANGE 3
+#define OPENVPN_PLUGIN_TLS_VERIFY 4
+#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
+#define OPENVPN_PLUGIN_CLIENT_CONNECT 6
+#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7
+#define OPENVPN_PLUGIN_LEARN_ADDRESS 8
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9
+#define OPENVPN_PLUGIN_TLS_FINAL 10
+#define OPENVPN_PLUGIN_ENABLE_PF 11
+#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER 13
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 14
+#define OPENVPN_PLUGIN_N 15
/*
* Build a mask out of a set of plug-in types.
@@ -424,9 +424,9 @@ struct openvpn_plugin_args_open_return
* per_client_context : the per-client context pointer which was returned by
* openvpn_plugin_client_constructor_v1, if defined.
*
- * current_cert_depth : Certificate depth of the certificate being passed over (only if compiled with ENABLE_CRYPTO defined)
+ * current_cert_depth : Certificate depth of the certificate being passed over
*
- * *current_cert : X509 Certificate object received from the client (only if compiled with ENABLE_CRYPTO defined)
+ * *current_cert : X509 Certificate object received from the client
*
*/
struct openvpn_plugin_args_func_in
@@ -436,13 +436,8 @@ struct openvpn_plugin_args_func_in
const char **const envp;
openvpn_plugin_handle_t handle;
void *per_client_context;
-#ifdef ENABLE_CRYPTO
int current_cert_depth;
openvpn_x509_cert_t *current_cert;
-#else
- int __current_cert_depth_disabled; /* Unused, for compatibility purposes only */
- void *__current_cert_disabled; /* Unused, for compatibility purposes only */
-#endif
};
@@ -563,12 +558,21 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op
* OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
*
* In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
- * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY. This enables asynchronous
- * authentication where the plugin (or one of its agents) may indicate
- * authentication success/failure some number of seconds after the return
- * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
- * char to the file named by auth_control_file in the environmental variable
- * list (envp).
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, OPENVPN_PLUGIN_CLIENT_CONNECT and
+ * OPENVPN_PLUGIN_CLIENT_CONNECT_V2. This enables asynchronous
+ * authentication or client connect where the plugin (or one of its agents)
+ * may indicate authentication success/failure or client configuration some
+ * number of seconds after the return of the function handler.
+ * For OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY and OPENVPN_PLUGIN_CLIENT_CONNECT
+ * this is done by writing a single char to the file named by
+ * auth_control_file/client_connect_deferred_file
+ * in the environmental variable list (envp).
+ *
+ * In addition the OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER and
+ * OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 are called when OpenVPN tries to
+ * get the deferred result. For a V2 call implementing this function is
+ * required as information is not passed by files. For the normal version
+ * the call is optional.
*
* first char of auth_control_file:
* '0' -- indicates auth failure
@@ -667,12 +671,12 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
* ARGUMENTS
*
* version : fixed value, defines the API version of the OpenVPN plug-in API. The plug-in
- * should validate that this value is matching the OPENVPN_PLUGIN_VERSION value.
+ * should validate that this value is matching the OPENVPN_PLUGINv3_STRUCTVER
+ * value.
*
- * handle : the openvpn_plugin_handle_t value which was returned by
- * openvpn_plugin_open.
+ * arguments : Structure with all arguments available to the plug-in.
*
- * return_list : used to return data back to OpenVPN.
+ * retptr : used to return data back to OpenVPN.
*
* RETURN VALUE
*
@@ -733,8 +737,8 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
* A given client or subnet rule applies to both incoming and outgoing
* packets.
*
- * See plugin/defer/simple.c for an example on using asynchronous
- * authentication and client-specific packet filtering.
+ * See sample/sample-plugins/defer/simple.c for an example on using
+ * asynchronous authentication and client-specific packet filtering.
*/
OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v3)
(const int version,
diff --git a/include/openvpn-plugin.h.in b/include/openvpn-plugin.h.in
index a604f1c..7ee68bb 100644
--- a/include/openvpn-plugin.h.in
+++ b/include/openvpn-plugin.h.in
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -26,7 +26,6 @@
#define OPENVPN_PLUGIN_VERSION 3
-#ifdef ENABLE_CRYPTO
#ifdef ENABLE_CRYPTO_MBEDTLS
#include <mbedtls/x509_crt.h>
#ifndef __OPENVPN_X509_CERT_T_DECLARED
@@ -40,7 +39,6 @@ typedef mbedtls_x509_crt openvpn_x509_cert_t;
typedef X509 openvpn_x509_cert_t;
#endif
#endif
-#endif
#include <stdarg.h>
#include <stddef.h>
@@ -118,20 +116,22 @@ extern "C" {
* FUNC: openvpn_plugin_client_destructor_v1 (top-level "generic" client)
* FUNC: openvpn_plugin_close_v1
*/
-#define OPENVPN_PLUGIN_UP 0
-#define OPENVPN_PLUGIN_DOWN 1
-#define OPENVPN_PLUGIN_ROUTE_UP 2
-#define OPENVPN_PLUGIN_IPCHANGE 3
-#define OPENVPN_PLUGIN_TLS_VERIFY 4
-#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
-#define OPENVPN_PLUGIN_CLIENT_CONNECT 6
-#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7
-#define OPENVPN_PLUGIN_LEARN_ADDRESS 8
-#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9
-#define OPENVPN_PLUGIN_TLS_FINAL 10
-#define OPENVPN_PLUGIN_ENABLE_PF 11
-#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12
-#define OPENVPN_PLUGIN_N 13
+#define OPENVPN_PLUGIN_UP 0
+#define OPENVPN_PLUGIN_DOWN 1
+#define OPENVPN_PLUGIN_ROUTE_UP 2
+#define OPENVPN_PLUGIN_IPCHANGE 3
+#define OPENVPN_PLUGIN_TLS_VERIFY 4
+#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
+#define OPENVPN_PLUGIN_CLIENT_CONNECT 6
+#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7
+#define OPENVPN_PLUGIN_LEARN_ADDRESS 8
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9
+#define OPENVPN_PLUGIN_TLS_FINAL 10
+#define OPENVPN_PLUGIN_ENABLE_PF 11
+#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER 13
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 14
+#define OPENVPN_PLUGIN_N 15
/*
* Build a mask out of a set of plug-in types.
@@ -423,9 +423,9 @@ struct openvpn_plugin_args_open_return
* per_client_context : the per-client context pointer which was returned by
* openvpn_plugin_client_constructor_v1, if defined.
*
- * current_cert_depth : Certificate depth of the certificate being passed over (only if compiled with ENABLE_CRYPTO defined)
+ * current_cert_depth : Certificate depth of the certificate being passed over
*
- * *current_cert : X509 Certificate object received from the client (only if compiled with ENABLE_CRYPTO defined)
+ * *current_cert : X509 Certificate object received from the client
*
*/
struct openvpn_plugin_args_func_in
@@ -435,13 +435,8 @@ struct openvpn_plugin_args_func_in
const char **const envp;
openvpn_plugin_handle_t handle;
void *per_client_context;
-#ifdef ENABLE_CRYPTO
int current_cert_depth;
openvpn_x509_cert_t *current_cert;
-#else
- int __current_cert_depth_disabled; /* Unused, for compatibility purposes only */
- void *__current_cert_disabled; /* Unused, for compatibility purposes only */
-#endif
};
@@ -562,12 +557,21 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op
* OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
*
* In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
- * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY. This enables asynchronous
- * authentication where the plugin (or one of its agents) may indicate
- * authentication success/failure some number of seconds after the return
- * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
- * char to the file named by auth_control_file in the environmental variable
- * list (envp).
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, OPENVPN_PLUGIN_CLIENT_CONNECT and
+ * OPENVPN_PLUGIN_CLIENT_CONNECT_V2. This enables asynchronous
+ * authentication or client connect where the plugin (or one of its agents)
+ * may indicate authentication success/failure or client configuration some
+ * number of seconds after the return of the function handler.
+ * For OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY and OPENVPN_PLUGIN_CLIENT_CONNECT
+ * this is done by writing a single char to the file named by
+ * auth_control_file/client_connect_deferred_file
+ * in the environmental variable list (envp).
+ *
+ * In addition the OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER and
+ * OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 are called when OpenVPN tries to
+ * get the deferred result. For a V2 call implementing this function is
+ * required as information is not passed by files. For the normal version
+ * the call is optional.
*
* first char of auth_control_file:
* '0' -- indicates auth failure
@@ -666,12 +670,12 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
* ARGUMENTS
*
* version : fixed value, defines the API version of the OpenVPN plug-in API. The plug-in
- * should validate that this value is matching the OPENVPN_PLUGIN_VERSION value.
+ * should validate that this value is matching the OPENVPN_PLUGINv3_STRUCTVER
+ * value.
*
- * handle : the openvpn_plugin_handle_t value which was returned by
- * openvpn_plugin_open.
+ * arguments : Structure with all arguments available to the plug-in.
*
- * return_list : used to return data back to OpenVPN.
+ * retptr : used to return data back to OpenVPN.
*
* RETURN VALUE
*
@@ -732,8 +736,8 @@ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
* A given client or subnet rule applies to both incoming and outgoing
* packets.
*
- * See plugin/defer/simple.c for an example on using asynchronous
- * authentication and client-specific packet filtering.
+ * See sample/sample-plugins/defer/simple.c for an example on using
+ * asynchronous authentication and client-specific packet filtering.
*/
OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v3)
(const int version,
diff --git a/install-sh b/install-sh
index 8175c64..20d8b2e 100755
--- a/install-sh
+++ b/install-sh
@@ -451,7 +451,18 @@ do
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
- (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+ (umask $cp_umask &&
+ { test -z "$stripcmd" || {
+ # Create $dsttmp read-write so that cp doesn't create it read-only,
+ # which would cause strip to fail.
+ if test -z "$doit"; then
+ : >"$dsttmp" # No need to fork-exec 'touch'.
+ else
+ $doit touch "$dsttmp"
+ fi
+ }
+ } &&
+ $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
diff --git a/m4/pkg.m4 b/m4/pkg.m4
index 12d2a58..cca47a7 100644
--- a/m4/pkg.m4
+++ b/m4/pkg.m4
@@ -53,7 +53,7 @@ fi[]dnl
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
#
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
-# only at the first occurence in configure.ac, so if the first place
+# only at the first occurrence in configure.ac, so if the first place
# it's called might be skipped (such as if it is within an "if", you
# have to call PKG_CHECK_EXISTS manually
# --------------------------------------------------------------
diff --git a/missing b/missing
index 625aeb1..8d0eaad 100755
--- a/missing
+++ b/missing
@@ -3,7 +3,7 @@
scriptversion=2018-03-07.03; # UTC
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
diff --git a/msvc-build.bat b/msvc-build.bat
index fd6d558..8256c62 100644
--- a/msvc-build.bat
+++ b/msvc-build.bat
@@ -7,13 +7,15 @@ setlocal ENABLEDELAYEDEXPANSION
cd /d %0\..
call msvc-env.bat
-set PLATFORMS=Win32
-set CONFIGURATIONS=Release
+set PLATFORMS=x64
+set CONFIGURATIONS=Debug Release
if exist "%VCHOME%\vcvarsall.bat" (
call "%VCHOME%\vcvarsall.bat"
) else if exist "%VCHOME%\bin\vcvars32.bat" (
call "%VCHOME%\bin\vcvars32.bat"
+) else if exist "%VCHOME%\Auxiliary\Build\vcvars32.bat" (
+ call "%VCHOME%\Auxiliary\Build\vcvars32.bat"
) else (
echo Cannot detect visual studio
goto error
diff --git a/msvc-dev.bat b/msvc-dev.bat
index dbd7be0..74aee0b 100644
--- a/msvc-dev.bat
+++ b/msvc-dev.bat
@@ -8,6 +8,8 @@ if exist "%VSHOME%\Common7\IDE\VCExpress.exe" (
set IDE=%VSHOME%\Common7\IDE\VCExpress.exe
) else if exist "%VSHOME%\Common7\IDE\devenv.exe" (
set IDE=%VSHOME%\Common7\IDE\devenv.exe
+) else if exist "%VCHOME%\Auxiliary\Build\vcvars64.bat" (
+ call "%VCHOME%\Auxiliary\Build\vcvars64.bat"
) else (
echo "Cannot detect visual studio environment"
goto error
diff --git a/msvc-env.bat b/msvc-env.bat
index aabed75..cc9663d 100644
--- a/msvc-env.bat
+++ b/msvc-env.bat
@@ -4,8 +4,8 @@ rem Put your own settings at msvc-env-local.bat
if exist msvc-env-local.bat call msvc-env-local.bat
if "%ProgramFiles(x86)%"=="" set ProgramFiles(x86)=%ProgramFiles%
-if "%VSCOMNTOOLS%"=="" SET VSCOMNTOOLS=%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\Tools
-if "%VSCOMNTOOLS%"=="" SET VSCOMNTOOLS=%ProgramFiles(x86)%\Microsoft Visual Studio 9.0\Common7\Tools
+if "%VSCOMNTOOLS%"=="" set VSCOMNTOOLS=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\Common7\Tools
+if not exist "%VSCOMNTOOLS%" set VSCOMNTOOLS=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\Tools
if "%VSHOME%"=="" SET VSHOME=%VSCOMNTOOLS%\..\..
if "%VCHOME%"=="" SET VCHOME=%VSHOME%\VC
@@ -13,7 +13,7 @@ set SOURCEBASE=%cd%
set SOLUTION=openvpn.sln
set CPPFLAGS=%CPPFLAGS%;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS
set CPPFLAGS=%CPPFLAGS%;NTDDI_VERSION=NTDDI_VISTA;_WIN32_WINNT=_WIN32_WINNT_VISTA
-set CPPFLAGS=%CPPFLAGS%;_USE_32BIT_TIME_T
+set CPPFLAGS=%CPPFLAGS%;
set CPPFLAGS=%CPPFLAGS%;%EXTRA_CPPFLAGS%
if exist config-msvc-local.h set CPPFLAGS="%CPPFLAGS%;HAVE_CONFIG_MSVC_LOCAL_H"
diff --git a/openvpn.sln b/openvpn.sln
index 90c01b8..3485bc9 100644
--- a/openvpn.sln
+++ b/openvpn.sln
@@ -1,6 +1,8 @@

-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual C++ Express 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31205.134
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openvpnserv", "src\openvpnserv\openvpnserv.vcxproj", "{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openvpn", "src\openvpn\openvpn.vcxproj", "{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}"
@@ -9,30 +11,97 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msvc-generate", "build\msvc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compat", "src\compat\compat.vcxproj", "{4B2E2719-E661-45D7-9203-F6F456B22F19}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tapctl", "src\tapctl\tapctl.vcxproj", "{A06436E7-D576-490D-8BA0-0751D920334A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openvpnmsica", "src\openvpnmsica\openvpnmsica.vcxproj", "{D41AA9D6-B818-476E-992E-0E16EB86BEE2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|ARM64 = Release|ARM64
Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|ARM64.Build.0 = Debug|ARM64
{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|Win32.ActiveCfg = Debug|Win32
{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|Win32.Build.0 = Debug|Win32
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|x64.ActiveCfg = Debug|x64
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Debug|x64.Build.0 = Debug|x64
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|ARM64.ActiveCfg = Release|ARM64
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|ARM64.Build.0 = Release|ARM64
{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|Win32.ActiveCfg = Release|Win32
{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|Win32.Build.0 = Release|Win32
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|x64.ActiveCfg = Release|x64
+ {9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}.Release|x64.Build.0 = Release|x64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|ARM64.Build.0 = Debug|ARM64
{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|Win32.ActiveCfg = Debug|Win32
{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|Win32.Build.0 = Debug|Win32
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|x64.ActiveCfg = Debug|x64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Debug|x64.Build.0 = Debug|x64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|ARM64.ActiveCfg = Release|ARM64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|ARM64.Build.0 = Release|ARM64
{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|Win32.ActiveCfg = Release|Win32
{29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|Win32.Build.0 = Release|Win32
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|x64.ActiveCfg = Release|x64
+ {29DF226E-4D4E-440F-ADAF-5829CFD4CA94}.Release|x64.Build.0 = Release|x64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|ARM64.Build.0 = Debug|ARM64
{8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|Win32.ActiveCfg = Debug|Win32
{8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|Win32.Build.0 = Debug|Win32
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|x64.ActiveCfg = Debug|x64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Debug|x64.Build.0 = Debug|x64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|ARM64.ActiveCfg = Release|ARM64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|ARM64.Build.0 = Release|ARM64
{8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|Win32.ActiveCfg = Release|Win32
{8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|Win32.Build.0 = Release|Win32
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|x64.ActiveCfg = Release|x64
+ {8598C2C8-34C4-47A1-99B0-7C295A890615}.Release|x64.Build.0 = Release|x64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|ARM64.Build.0 = Debug|ARM64
{4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|Win32.ActiveCfg = Debug|Win32
{4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|Win32.Build.0 = Debug|Win32
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|x64.ActiveCfg = Debug|x64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Debug|x64.Build.0 = Debug|x64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|ARM64.ActiveCfg = Release|ARM64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|ARM64.Build.0 = Release|ARM64
{4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|Win32.ActiveCfg = Release|Win32
{4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|Win32.Build.0 = Release|Win32
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|x64.ActiveCfg = Release|x64
+ {4B2E2719-E661-45D7-9203-F6F456B22F19}.Release|x64.Build.0 = Release|x64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|ARM64.Build.0 = Debug|ARM64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|Win32.Build.0 = Debug|Win32
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|x64.ActiveCfg = Debug|x64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Debug|x64.Build.0 = Debug|x64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|ARM64.Build.0 = Release|ARM64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|Win32.ActiveCfg = Release|Win32
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|Win32.Build.0 = Release|Win32
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|x64.ActiveCfg = Release|x64
+ {A06436E7-D576-490D-8BA0-0751D920334A}.Release|x64.Build.0 = Release|x64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|ARM64.Build.0 = Debug|ARM64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|Win32.Build.0 = Debug|Win32
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|x64.ActiveCfg = Debug|x64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Debug|x64.Build.0 = Debug|x64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|ARM64.ActiveCfg = Release|ARM64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|ARM64.Build.0 = Release|ARM64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|Win32.ActiveCfg = Release|Win32
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|Win32.Build.0 = Release|Win32
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|x64.ActiveCfg = Release|x64
+ {D41AA9D6-B818-476E-992E-0E16EB86BEE2}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EB3CE5D3-415C-46F0-96AB-E1CDA287AB6D}
+ EndGlobalSection
EndGlobal
diff --git a/sample/Makefile.am b/sample/Makefile.am
index 3be698e..06ba0ff 100644
--- a/sample/Makefile.am
+++ b/sample/Makefile.am
@@ -5,13 +5,16 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
+DISTCLEANFILES = \
+ $(builddir)/sample-plugins/Makefile
+
EXTRA_DIST = \
sample-plugins \
sample-config-files \
diff --git a/sample/Makefile.in b/sample/Makefile.in
index d851a5f..3c9335f 100644
--- a/sample/Makefile.in
+++ b/sample/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -179,7 +179,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -193,6 +194,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -220,7 +222,6 @@ 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@
@@ -271,6 +272,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -334,6 +337,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -348,6 +352,9 @@ top_srcdir = @top_srcdir@
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
+DISTCLEANFILES = \
+ $(builddir)/sample-plugins/Makefile
+
EXTRA_DIST = \
sample-plugins \
sample-config-files \
@@ -492,6 +499,7 @@ 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)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
diff --git a/sample/sample-config-files/client.conf b/sample/sample-config-files/client.conf
index 5fd4a94..47ca409 100644
--- a/sample/sample-config-files/client.conf
+++ b/sample/sample-config-files/client.conf
@@ -90,7 +90,7 @@ cert client.crt
key client.key
# Verify server certificate by checking that the
-# certicate has the correct key usage set.
+# certificate has the correct key usage set.
# This is an important precaution to protect against
# a potential attack discussed here:
# http://openvpn.net/howto.html#mitm
@@ -112,7 +112,7 @@ tls-auth ta.key 1
# then you must also specify it here.
# Note that v2.4 client/server will automatically
# negotiate AES-256-GCM in TLS mode.
-# See also the ncp-cipher option in the manpage
+# See also the data-ciphers option in the manpage
cipher AES-256-CBC
# Enable compression on the VPN link.
diff --git a/sample/sample-config-files/loopback-client b/sample/sample-config-files/loopback-client
index 7117307..8ac3d1d 100644
--- a/sample/sample-config-files/loopback-client
+++ b/sample/sample-config-files/loopback-client
@@ -8,6 +8,9 @@
#
# ./openvpn --config sample-config-files/loopback-client (In one window)
# ./openvpn --config sample-config-files/loopback-server (Simultaneously in another window)
+#
+# this config file has the crypto material (cert, key, ..) "inlined",
+# while the "server" config has it as external reference - test both paths
rport 16000
lport 16001
@@ -18,9 +21,206 @@ verb 3
reneg-sec 10
tls-client
remote-cert-tls server
-ca sample-keys/ca.crt
-key sample-keys/client.key
-cert sample-keys/client.crt
-tls-auth sample-keys/ta.key 1
+#ca sample-keys/ca.crt
+<ca>
+-----BEGIN CERTIFICATE-----
+MIIGKDCCBBCgAwIBAgIJAKFO3vqQ8q6BMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNV
+BAYTAktHMQswCQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMM
+T3BlblZQTi1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4w
+HhcNMTQxMDIyMjE1OTUyWhcNMjQxMDE5MjE1OTUyWjBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsJVPCqt3vtoDW2U0DII1QIh2Qs0dqh88
+8nivxAIm2LTq93e9fJhsq3P/UVYAYSeCIrekXypR0EQgSgcNTvGBMe20BoHO5yvb
+GjKPmjfLj6XRotCOGy8EDl/hLgRY9efiA8wsVfuvF2q/FblyJQPR/gPiDtTmUiqF
+qXa7AJmMrqFsnWppOuGd7Qc6aTsae4TF1e/gUTCTraa7NeHowDaKhdyFmEEnCYR5
+CeUsx2JlFWAH8PCrxBpHYbmGyvS0kH3+rQkaSM/Pzc2bS4ayHaOYRK5XsGq8XiNG
+KTTLnSaCdPeHsI+3xMHmEh+u5Og2DFGgvyD22gde6W2ezvEKCUDrzR7bsnYqqyUy
+n7LxnkPXGyvR52T06G8KzLKQRmDlPIXhzKMO07qkHmIonXTdF7YI1azwHpAtN4dS
+rUe1bvjiTSoEsQPfOAyvD0RMK/CBfgEZUzAB50e/IlbZ84c0DJfUMOm4xCyft1HF
+YpYeyCf5dxoIjweCPOoP426+aTXM7kqq0ieIr6YxnKV6OGGLKEY+VNZh1DS7enqV
+HP5i8eimyuUYPoQhbK9xtDGMgghnc6Hn8BldPMcvz98HdTEH4rBfA3yNuCxLSNow
+4jJuLjNXh2QeiUtWtkXja7ec+P7VqKTduJoRaX7cs+8E3ImigiRnvmK+npk7Nt1y
+YE9hBRhSoLsCAwEAAaOB2DCB1TAdBgNVHQ4EFgQUK0DlyX319JY46S/jL9lAZMmO
+BZswgZgGA1UdIwSBkDCBjYAUK0DlyX319JY46S/jL9lAZMmOBZuhaqRoMGYxCzAJ
+BgNVBAYTAktHMQswCQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UE
+ChMMT3BlblZQTi1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21h
+aW6CCQChTt76kPKugTAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG
+9w0BAQsFAAOCAgEABc77f4C4P8fIS+V8qCJmVNSDU44UZBc+D+J6ZTgW8JeOHUIj
+Bh++XDg3gwat7pIWQ8AU5R7h+fpBI9n3dadyIsMHGwSogHY9Gw7di2RVtSFajEth
+rvrq0JbzpwoYedMh84sJ2qI/DGKW9/Is9+O52fR+3z3dY3gNRDPQ5675BQ5CQW9I
+AJgLOqzD8Q0qrXYi7HaEqzNx6p7RDTuhFgvTd+vS5d5+28Z5fm2umnq+GKHF8W5P
+ylp2Js119FTVO7brusAMKPe5emc7tC2ov8OFFemQvfHR41PLryap2VD81IOgmt/J
+kX/j/y5KGux5HZ3lxXqdJbKcAq4NKYQT0mCkRD4l6szaCEJ+k0SiM9DdTcBDefhR
+9q+pCOyMh7d8QjQ1075mF7T+PGkZQUW1DUjEfrZhICnKgq+iEoUmM0Ee5WtRqcnu
+5BTGQ2mSfc6rV+Vr+eYXqcg7Nxb3vFXYSTod1UhefonVqwdmyJ2sC79zp36Tbo2+
+65NW2WJK7KzPUyOJU0U9bcu0utvDOvGWmG+aHbymJgcoFzvZmlXqMXn97pSFn4jV
+y3SLRgJXOw1QLXL2Y5abcuoBVr4gCOxxk2vBeVxOMRXNqSWZOFIF1bu/PxuDA+Sa
+hEi44aHbPXt9opdssz/hdGfd8Wo7vEJrbg7c6zR6C/Akav1Rzy9oohIdgOw=
+-----END CERTIFICATE-----
+</ca>
+#key sample-keys/client.key
+<key>
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDsZY/pEsIaW+ZW
+KgipgjotRHijADuwn+cnEECT7/HMPqCqBKKAGxOp5v6B1nCQqNjU3jDYNQDSvmLw
+SNr8FY3Exm0LmfErgwAK0yojC+XN+TXfQ2EVcq2VmPZzIUFeoN1HJ6DVmtRBqBwd
+VyBxF4/3KJ4+B87s1Q5CTx50R45HndIUKCcsFBD10Za1k3SE7/kE3o1Kb993q+rR
+WNNE/loEAf8Gepf3/eNXSOHw30ATn2YjWuNVVD1UOe4A+RLx0t90LrrX8I3G3RhY
+HJMiC3X6qNbgtS8tudT+uU+G4nVIFmD7P8m0MEIp+zuzK7lZgWpG80WDv/3VGv83
+DG9b/WHxAgMBAAECggEBAIOdaCpUD02trOh8LqZxowJhBOl7z7/ex0uweMPk67LT
+i5AdVHwOlzwZJ8oSIknoOBEMRBWcLQEojt1JMuL2/R95emzjIKshHHzqZKNulFvB
+TIUpdnwChTKtH0mqUkLlPU3Ienty4IpNlpmfUKimfbkWHERdBJBHbtDsTABhdo3X
+9pCF/yRKqJS2Fy/Mkl3gv1y/NB1OL4Jhl7vQbf+kmgfQN2qdOVe2BOKQ8NlPUDmE
+/1XNIDaE3s6uvUaoFfwowzsCCwN2/8QrRMMKkjvV+lEVtNmQdYxj5Xj5IwS0vkK0
+6icsngW87cpZxxc1zsRWcSTloy5ohub4FgKhlolmigECgYEA+cBlxzLvaMzMlBQY
+kCac9KQMvVL+DIFHlZA5i5L/9pRVp4JJwj3GUoehFJoFhsxnKr8HZyLwBKlCmUVm
+VxnshRWiAU18emUmeAtSGawlAS3QXhikVZDdd/L20YusLT+DXV81wlKR97/r9+17
+klQOLkSdPm9wcMDOWMNHX8bUg8kCgYEA8k+hQv6+TR/+Beao2IIctFtw/EauaJiJ
+wW5ql1cpCLPMAOQUvjs0Km3zqctfBF8mUjdkcyJ4uhL9FZtfywY22EtRIXOJ/8VR
+we65mVo6RLR8YVM54sihanuFOnlyF9LIBWB+9pUfh1/Y7DSebh7W73uxhAxQhi3Y
+QwfIQIFd8OkCgYBalH4VXhLYhpaYCiXSej6ot6rrK2N6c5Tb2MAWMA1nh+r84tMP
+gMoh+pDgYPAqMI4mQbxUmqZEeoLuBe6VHpDav7rPECRaW781AJ4ZM4cEQ3Jz/inz
+4qOAMn10CF081/Ez9ykPPlU0bsYNWHNd4eB2xWnmUBKOwk7UgJatVPaUiQKBgQCI
+f18CVGpzG9CHFnaK8FCnMNOm6VIaTcNcGY0mD81nv5Dt943P054BQMsAHTY7SjZW
+HioRyZtkhonXAB2oSqnekh7zzxgv4sG5k3ct8evdBCcE1FNJc2eqikZ0uDETRoOy
+s7cRxNNr+QxDkyikM+80HOPU1PMPgwfOSrX90GJQ8QKBgEBKohGMV/sNa4t14Iau
+qO8aagoqh/68K9GFXljsl3/iCSa964HIEREtW09Qz1w3dotEgp2w8bsDa+OwWrLy
+0SY7T5jRViM3cDWRlUBLrGGiL0FiwsfqiRiji60y19erJgrgyGVIb1kIgIBRkgFM
+2MMweASzTmZcri4PA/5C0HYb
+-----END PRIVATE KEY-----
+</key>
+#cert sample-keys/client.crt
+<cert>
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Oct 22 21:59:53 2014 GMT
+ Not After : Oct 19 21:59:53 2024 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ec:65:8f:e9:12:c2:1a:5b:e6:56:2a:08:a9:82:
+ 3a:2d:44:78:a3:00:3b:b0:9f:e7:27:10:40:93:ef:
+ f1:cc:3e:a0:aa:04:a2:80:1b:13:a9:e6:fe:81:d6:
+ 70:90:a8:d8:d4:de:30:d8:35:00:d2:be:62:f0:48:
+ da:fc:15:8d:c4:c6:6d:0b:99:f1:2b:83:00:0a:d3:
+ 2a:23:0b:e5:cd:f9:35:df:43:61:15:72:ad:95:98:
+ f6:73:21:41:5e:a0:dd:47:27:a0:d5:9a:d4:41:a8:
+ 1c:1d:57:20:71:17:8f:f7:28:9e:3e:07:ce:ec:d5:
+ 0e:42:4f:1e:74:47:8e:47:9d:d2:14:28:27:2c:14:
+ 10:f5:d1:96:b5:93:74:84:ef:f9:04:de:8d:4a:6f:
+ df:77:ab:ea:d1:58:d3:44:fe:5a:04:01:ff:06:7a:
+ 97:f7:fd:e3:57:48:e1:f0:df:40:13:9f:66:23:5a:
+ e3:55:54:3d:54:39:ee:00:f9:12:f1:d2:df:74:2e:
+ ba:d7:f0:8d:c6:dd:18:58:1c:93:22:0b:75:fa:a8:
+ d6:e0:b5:2f:2d:b9:d4:fe:b9:4f:86:e2:75:48:16:
+ 60:fb:3f:c9:b4:30:42:29:fb:3b:b3:2b:b9:59:81:
+ 6a:46:f3:45:83:bf:fd:d5:1a:ff:37:0c:6f:5b:fd:
+ 61:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ D2:B4:36:0F:B1:FC:DD:A5:EA:2A:F7:C7:23:89:FA:E3:FA:7A:44:1D
+ X509v3 Authority Key Identifier:
+ keyid:2B:40:E5:C9:7D:F5:F4:96:38:E9:2F:E3:2F:D9:40:64:C9:8E:05:9B
+ DirName:/C=KG/ST=NA/L=BISHKEK/O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ serial:A1:4E:DE:FA:90:F2:AE:81
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 7f:e0:fe:84:a7:ec:df:62:a5:cd:3c:c1:e6:42:b1:31:12:f0:
+ b9:da:a7:9e:3f:bd:96:52:b6:fc:55:74:64:3e:e4:ff:7e:aa:
+ f7:3e:06:18:5f:73:85:f8:c8:e0:67:1b:4d:97:ca:05:d0:37:
+ 07:33:64:9b:e6:78:77:14:9a:55:bb:2a:ac:c3:7f:c9:15:08:
+ 83:5c:c8:c2:61:d3:71:4c:05:0b:2b:cb:a3:87:6d:a0:32:ed:
+ b0:b3:27:97:4a:55:8d:01:2a:30:56:68:ab:f2:da:5c:10:73:
+ c9:aa:0a:9c:4b:4c:a0:5b:51:6e:0a:7e:6c:53:80:b0:00:e1:
+ 1e:9a:4c:0a:37:9e:20:89:bc:c5:e5:79:58:b7:45:ff:d3:c4:
+ a1:fd:d9:78:3d:45:16:74:df:82:44:1d:1d:81:50:5a:b9:32:
+ 4c:e2:4f:3f:0e:3a:65:5a:64:83:3b:29:31:c4:99:88:bc:c5:
+ 84:39:f2:19:12:e1:66:d0:ea:fb:75:b1:d2:27:be:91:59:a3:
+ 2b:09:d5:5c:bf:46:8e:d6:67:d6:0b:ec:da:ab:f0:80:19:87:
+ 64:07:a9:77:b1:5e:0c:e2:c5:1d:6a:ac:5d:23:f3:30:75:36:
+ 4e:ca:c3:4e:b0:4d:8c:2c:ce:52:61:63:de:d5:f5:ef:ef:0a:
+ 6b:23:25:26:3c:3a:f2:c3:c2:16:19:3f:a9:32:ba:68:f9:c9:
+ 12:3c:3e:c6:1f:ff:9b:4e:f4:90:b0:63:f5:d1:33:00:30:5a:
+ e8:24:fa:35:44:9b:6a:80:f3:a6:cc:7b:3c:73:5f:50:c4:30:
+ 71:d8:74:90:27:0a:01:4e:a5:5e:b1:f8:da:c2:61:81:11:ae:
+ 29:a3:8f:fa:7e:4c:4e:62:b1:00:de:92:e3:8f:6a:2e:da:d9:
+ 38:5d:6b:7c:0d:e4:01:aa:c8:c6:6d:8b:cd:c0:c8:6e:e4:57:
+ 21:8a:f6:46:30:d9:ad:51:a1:87:96:a6:53:c9:1e:c6:bb:c3:
+ eb:55:fe:8c:d6:5c:d5:c6:f3:ca:b0:60:d2:d4:2a:1f:88:94:
+ d3:4c:1a:da:0c:94:fe:c1:5d:0d:2a:db:99:29:5d:f6:dd:16:
+ c4:c8:4d:74:9e:80:d9:d0:aa:ed:7b:e3:30:e4:47:d8:f5:15:
+ c1:71:b8:c6:fd:ee:fc:9e:b2:5f:b5:b7:92:ed:ff:ca:37:f6:
+ c7:82:b4:54:13:9b:83:cd:87:8b:7e:64:f6:2e:54:3a:22:b1:
+ c5:c1:f4:a5:25:53:9a:4d:a8:0f:e7:35:4b:89:df:19:83:66:
+ 64:d9:db:d1:61:2b:24:1b:1d:44:44:fb:49:30:87:b7:49:23:
+ 08:02:8a:e0:25:f3:f4:43
+-----BEGIN CERTIFICATE-----
+MIIFFDCCAvygAwIBAgIBAjANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTE0MTAy
+MjIxNTk1M1oXDTI0MTAxOTIxNTk1M1owajELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxFDASBgNVBAMTC1Rlc3QtQ2xpZW50
+MSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDsZY/pEsIaW+ZWKgipgjotRHijADuwn+cnEECT
+7/HMPqCqBKKAGxOp5v6B1nCQqNjU3jDYNQDSvmLwSNr8FY3Exm0LmfErgwAK0yoj
+C+XN+TXfQ2EVcq2VmPZzIUFeoN1HJ6DVmtRBqBwdVyBxF4/3KJ4+B87s1Q5CTx50
+R45HndIUKCcsFBD10Za1k3SE7/kE3o1Kb993q+rRWNNE/loEAf8Gepf3/eNXSOHw
+30ATn2YjWuNVVD1UOe4A+RLx0t90LrrX8I3G3RhYHJMiC3X6qNbgtS8tudT+uU+G
+4nVIFmD7P8m0MEIp+zuzK7lZgWpG80WDv/3VGv83DG9b/WHxAgMBAAGjgcgwgcUw
+CQYDVR0TBAIwADAdBgNVHQ4EFgQU0rQ2D7H83aXqKvfHI4n64/p6RB0wgZgGA1Ud
+IwSBkDCBjYAUK0DlyX319JY46S/jL9lAZMmOBZuhaqRoMGYxCzAJBgNVBAYTAktH
+MQswCQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQ
+Ti1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CCQChTt76
+kPKugTANBgkqhkiG9w0BAQsFAAOCAgEAf+D+hKfs32KlzTzB5kKxMRLwudqnnj+9
+llK2/FV0ZD7k/36q9z4GGF9zhfjI4GcbTZfKBdA3BzNkm+Z4dxSaVbsqrMN/yRUI
+g1zIwmHTcUwFCyvLo4dtoDLtsLMnl0pVjQEqMFZoq/LaXBBzyaoKnEtMoFtRbgp+
+bFOAsADhHppMCjeeIIm8xeV5WLdF/9PEof3ZeD1FFnTfgkQdHYFQWrkyTOJPPw46
+ZVpkgzspMcSZiLzFhDnyGRLhZtDq+3Wx0ie+kVmjKwnVXL9GjtZn1gvs2qvwgBmH
+ZAepd7FeDOLFHWqsXSPzMHU2TsrDTrBNjCzOUmFj3tX17+8KayMlJjw68sPCFhk/
+qTK6aPnJEjw+xh//m070kLBj9dEzADBa6CT6NUSbaoDzpsx7PHNfUMQwcdh0kCcK
+AU6lXrH42sJhgRGuKaOP+n5MTmKxAN6S449qLtrZOF1rfA3kAarIxm2LzcDIbuRX
+IYr2RjDZrVGhh5amU8kexrvD61X+jNZc1cbzyrBg0tQqH4iU00wa2gyU/sFdDSrb
+mSld9t0WxMhNdJ6A2dCq7XvjMORH2PUVwXG4xv3u/J6yX7W3ku3/yjf2x4K0VBOb
+g82Hi35k9i5UOiKxxcH0pSVTmk2oD+c1S4nfGYNmZNnb0WErJBsdRET7STCHt0kj
+CAKK4CXz9EM=
+-----END CERTIFICATE-----
+</cert>
+#tls-auth sample-keys/ta.key 1
+key-direction 1
+<tls-auth>
+#
+# 2048 bit OpenVPN static key
+#
+-----BEGIN OpenVPN Static key V1-----
+a863b1cbdb911ff4ef3360ce135157e7
+241a465f5045f51cf9a92ebc24da34fd
+5fc48456778c977e374d55a8a7298aef
+40d0ab0c60b5e09838510526b73473a0
+8da46a8c352572dd86d4a871700a915b
+6aaa58a9dac560db2dfdd7ef15a202e1
+fca6913d7ee79c678c5798fbf7bd920c
+caa7a64720908da7254598b052d07f55
+5e31dc5721932cffbdd8965d04107415
+46c86823da18b66aab347e4522cc05ff
+634968889209c96b1024909cd4ce574c
+f829aa9c17d5df4a66043182ee23635d
+8cabf5a7ba02345ad94a3aa25a63d55c
+e13f4ad235a0825e3fe17f9419baff1c
+e73ad1dd652f1e48c7102fe8ee181e54
+10a160ae255f63fd01db1f29e6efcb8e
+-----END OpenVPN Static key V1-----
+</tls-auth>
+cipher AES-256-GCM
ping 1
inactive 120 10000000
diff --git a/sample/sample-config-files/loopback-server b/sample/sample-config-files/loopback-server
index 8e1f39c..58daeb5 100644
--- a/sample/sample-config-files/loopback-server
+++ b/sample/sample-config-files/loopback-server
@@ -22,5 +22,6 @@ ca sample-keys/ca.crt
key sample-keys/server.key
cert sample-keys/server.crt
tls-auth sample-keys/ta.key 0
+cipher AES-256-GCM
ping 1
inactive 120 10000000
diff --git a/sample/sample-config-files/server.conf b/sample/sample-config-files/server.conf
index 1dd477b..e702063 100644
--- a/sample/sample-config-files/server.conf
+++ b/sample/sample-config-files/server.conf
@@ -235,7 +235,7 @@ keepalive 10 120
# to help block DoS attacks and UDP port flooding.
#
# Generate with:
-# openvpn --genkey --secret ta.key
+# openvpn --genkey tls-auth ta.key
#
# The server and each client must have
# a copy of this key.
diff --git a/sample/sample-config-files/static-home.conf b/sample/sample-config-files/static-home.conf
deleted file mode 100644
index ed0c672..0000000
--- a/sample/sample-config-files/static-home.conf
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Sample OpenVPN configuration file for
-# home using a pre-shared static key.
-#
-# '#' or ';' may be used to delimit comments.
-
-# Use a dynamic tun device.
-# For Linux 2.2 or non-Linux OSes,
-# you may want to use an explicit
-# unit number such as "tun1".
-# OpenVPN also supports virtual
-# ethernet "tap" devices.
-dev tun
-
-# Our OpenVPN peer is the office gateway.
-remote 1.2.3.4
-
-# 10.1.0.2 is our local VPN endpoint (home).
-# 10.1.0.1 is our remote VPN endpoint (office).
-ifconfig 10.1.0.2 10.1.0.1
-
-# Our up script will establish routes
-# once the VPN is alive.
-up ./home.up
-
-# Our pre-shared static key
-secret static.key
-
-# Cipher to use
-cipher AES-256-CBC
-
-# OpenVPN 2.0 uses UDP port 1194 by default
-# (official port assignment by iana.org 11/04).
-# OpenVPN 1.x uses UDP port 5000 by default.
-# Each OpenVPN tunnel must use
-# a different port number.
-# lport or rport can be used
-# to denote different ports
-# for local and remote.
-; port 1194
-
-# Downgrade UID and GID to
-# "nobody" after initialization
-# for extra security.
-; user nobody
-; group nobody
-
-# If you built OpenVPN with
-# LZO compression, uncomment
-# out the following line.
-; comp-lzo
-
-# Send a UDP ping to remote once
-# every 15 seconds to keep
-# stateful firewall connection
-# alive. Uncomment this
-# out if you are using a stateful
-# firewall.
-; ping 15
-
-# Uncomment this section for a more reliable detection when a system
-# loses its connection. For example, dial-ups or laptops that
-# travel to other locations.
-; ping 15
-; ping-restart 45
-; ping-timer-rem
-; persist-tun
-; persist-key
-
-# Verbosity level.
-# 0 -- quiet except for fatal errors.
-# 1 -- mostly quiet, but display non-fatal network errors.
-# 3 -- medium output, good for normal operation.
-# 9 -- verbose, good for troubleshooting
-verb 3
diff --git a/sample/sample-config-files/static-office.conf b/sample/sample-config-files/static-office.conf
deleted file mode 100644
index 609ddd0..0000000
--- a/sample/sample-config-files/static-office.conf
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Sample OpenVPN configuration file for
-# office using a pre-shared static key.
-#
-# '#' or ';' may be used to delimit comments.
-
-# Use a dynamic tun device.
-# For Linux 2.2 or non-Linux OSes,
-# you may want to use an explicit
-# unit number such as "tun1".
-# OpenVPN also supports virtual
-# ethernet "tap" devices.
-dev tun
-
-# 10.1.0.1 is our local VPN endpoint (office).
-# 10.1.0.2 is our remote VPN endpoint (home).
-ifconfig 10.1.0.1 10.1.0.2
-
-# Our up script will establish routes
-# once the VPN is alive.
-up ./office.up
-
-# Our pre-shared static key
-secret static.key
-
-# Cipher to use
-cipher AES-256-CBC
-
-# OpenVPN 2.0 uses UDP port 1194 by default
-# (official port assignment by iana.org 11/04).
-# OpenVPN 1.x uses UDP port 5000 by default.
-# Each OpenVPN tunnel must use
-# a different port number.
-# lport or rport can be used
-# to denote different ports
-# for local and remote.
-; port 1194
-
-# Downgrade UID and GID to
-# "nobody" after initialization
-# for extra security.
-; user nobody
-; group nobody
-
-# If you built OpenVPN with
-# LZO compression, uncomment
-# out the following line.
-; comp-lzo
-
-# Send a UDP ping to remote once
-# every 15 seconds to keep
-# stateful firewall connection
-# alive. Uncomment this
-# out if you are using a stateful
-# firewall.
-; ping 15
-
-# Uncomment this section for a more reliable detection when a system
-# loses its connection. For example, dial-ups or laptops that
-# travel to other locations.
-; ping 15
-; ping-restart 45
-; ping-timer-rem
-; persist-tun
-; persist-key
-
-# Verbosity level.
-# 0 -- quiet except for fatal errors.
-# 1 -- mostly quiet, but display non-fatal network errors.
-# 3 -- medium output, good for normal operation.
-# 9 -- verbose, good for troubleshooting
-verb 3
diff --git a/sample/sample-config-files/tls-home.conf b/sample/sample-config-files/tls-home.conf
index daa4ea1..3a9297c 100644
--- a/sample/sample-config-files/tls-home.conf
+++ b/sample/sample-config-files/tls-home.conf
@@ -4,12 +4,9 @@
#
# '#' or ';' may be used to delimit comments.
-# Use a dynamic tun device.
-# For Linux 2.2 or non-Linux OSes,
-# you may want to use an explicit
-# unit number such as "tun1".
-# OpenVPN also supports virtual
-# ethernet "tap" devices.
+# Use a dynamic tun device. For non-Linux OSes, you may want to use an
+# explicit unit number such as "tun1".
+# OpenVPN also supports virtual ethernet "tap" devices.
dev tun
# Our OpenVPN peer is the office gateway.
@@ -37,6 +34,9 @@ cert home.crt
# Our private key
key home.key
+# Our data channel cipher (must match peer config)
+cipher AES-256-GCM
+
# OpenVPN 2.0 uses UDP port 1194 by default
# (official port assignment by iana.org 11/04).
# OpenVPN 1.x uses UDP port 5000 by default.
diff --git a/sample/sample-config-files/tls-office.conf b/sample/sample-config-files/tls-office.conf
index d196144..8105221 100644
--- a/sample/sample-config-files/tls-office.conf
+++ b/sample/sample-config-files/tls-office.conf
@@ -37,6 +37,9 @@ cert office.crt
# Our private key
key office.key
+# Our data channel cipher (must match peer config)
+cipher AES-256-GCM
+
# OpenVPN 2.0 uses UDP port 1194 by default
# (official port assignment by iana.org 11/04).
# OpenVPN 1.x uses UDP port 5000 by default.
diff --git a/sample/sample-keys/gen-sample-keys.sh b/sample/sample-keys/gen-sample-keys.sh
index 920513a..82f0880 100755
--- a/sample/sample-keys/gen-sample-keys.sh
+++ b/sample/sample-keys/gen-sample-keys.sh
@@ -3,7 +3,7 @@
# Run this script to set up a test CA, and test key-certificate pair for a
# server, and various clients.
#
-# Copyright (C) 2014 Steffan Karger <steffan@karger.me>
+# Copyright (C) 2014-2021 Steffan Karger <steffan@karger.me>
set -eu
command -v openssl >/dev/null 2>&1 || { echo >&2 "Unable to find openssl. Please make sure openssl is installed and in your path."; exit 1; }
@@ -15,7 +15,7 @@ then
fi
# Generate static key for tls-auth (or static key mode)
-$(dirname ${0})/../../src/openvpn/openvpn --genkey --secret ta.key
+$(dirname ${0})/../../src/openvpn/openvpn --genkey tls-auth ta.key
# Create required directories and files
mkdir -p sample-ca
diff --git a/sample/sample-keys/openssl.cnf b/sample/sample-keys/openssl.cnf
index aabfd48..02bf8ac 100644
--- a/sample/sample-keys/openssl.cnf
+++ b/sample/sample-keys/openssl.cnf
@@ -19,7 +19,7 @@ crl = $dir/crl.pem # The current CRL
private_key = $dir/ca.key # The private key
RANDFILE = $dir/.rand # private random number file
-x509_extensions = basic_exts # The extentions to add to the cert
+x509_extensions = basic_exts # The extensions to add to the cert
# This allows a V2 CRL. Ancient browsers don't like it, but anything Easy-RSA
# is designed for will. In return, we get the Issuer attached to CRLs.
@@ -54,7 +54,7 @@ default_bits = 2048
default_keyfile = privkey.pem
default_md = sha256
distinguished_name = cn_only
-x509_extensions = easyrsa_ca # The extentions to add to the self signed cert
+x509_extensions = easyrsa_ca # The extensions to add to the self signed cert
# A placeholder to handle the $EXTRA_EXTS feature:
#%EXTRA_EXTS% # Do NOT remove or change this line as $EXTRA_EXTS support requires it
diff --git a/sample/sample-plugins/Makefile b/sample/sample-plugins/Makefile
new file mode 100644
index 0000000..8646832
--- /dev/null
+++ b/sample/sample-plugins/Makefile
@@ -0,0 +1,585 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# sample/sample-plugins/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994-2020 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.
+
+
+
+#
+# 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-2021 OpenVPN Inc <sales@openvpn.net>
+#
+
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020-2021 OpenVPN Inc <sales@openvpn.net>
+#
+
+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)/openvpn
+pkgincludedir = $(includedir)/openvpn
+pkglibdir = $(libdir)/openvpn
+pkglibexecdir = $(libexecdir)/openvpn
+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 = x86_64-pc-linux-gnu
+host_triplet = x86_64-pc-linux-gnu
+subdir = sample/sample-plugins
+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_V_P = $(am__v_P_$(V))
+am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+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)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.plugins \
+ README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/missing aclocal-1.16
+AMTAR = $${TAR-tar}
+AM_DEFAULT_VERBOSITY = 1
+AR = ar
+AS = as
+AUTOCONF = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/missing autoconf
+AUTOHEADER = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/missing autoheader
+AUTOMAKE = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/missing automake-1.16
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -Wall -Wno-unused-parameter -Wno-unused-function -Wno-stringop-truncation -g -O2 -std=c99
+CMOCKA_CFLAGS =
+CMOCKA_LIBS =
+CPP = gcc -E
+CPPFLAGS =
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DLLTOOL = false
+DL_LIBS = -ldl
+DSYMUTIL =
+DUMPBIN =
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EGREP = /usr/bin/grep -E
+ENABLE_UNITTESTS =
+EXEEXT =
+FGREP = /usr/bin/grep -F
+GIT = git
+GREP = /usr/bin/grep
+IFCONFIG = /usr/sbin/ifconfig
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+IPROUTE = /usr/sbin/ip
+LD = /usr/bin/ld -m elf_x86_64
+LDFLAGS =
+LIBOBJS =
+LIBPAM_CFLAGS =
+LIBPAM_LIBS = -lpam
+LIBS =
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO =
+LN_S = ln -s
+LTLIBOBJS =
+LT_SYS_LIBRARY_PATH =
+LZ4_CFLAGS =
+LZ4_LIBS = -llz4
+LZO_CFLAGS =
+LZO_LIBS = -llzo2
+MAKEINFO = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/missing makeinfo
+MANIFEST_TOOL = :
+MBEDTLS_CFLAGS =
+MBEDTLS_LIBS =
+MKDIR_P = /usr/bin/mkdir -p
+NETSTAT = netstat
+NM = /usr/bin/nm -B
+NMEDIT =
+OBJDUMP = objdump
+OBJEXT = o
+OPENSSL_CFLAGS =
+OPENSSL_LIBS = -lssl -lcrypto
+OPENVPN_VERSION_MAJOR = 2
+OPENVPN_VERSION_MINOR = 5
+OPENVPN_VERSION_PATCH = .4
+OPTIONAL_CRYPTO_CFLAGS =
+OPTIONAL_CRYPTO_LIBS = -lssl -lcrypto
+OPTIONAL_DL_LIBS = -ldl
+OPTIONAL_INOTIFY_CFLAGS =
+OPTIONAL_INOTIFY_LIBS =
+OPTIONAL_LZ4_CFLAGS =
+OPTIONAL_LZ4_LIBS = -llz4
+OPTIONAL_LZO_CFLAGS =
+OPTIONAL_LZO_LIBS = -llzo2
+OPTIONAL_PKCS11_HELPER_CFLAGS =
+OPTIONAL_PKCS11_HELPER_LIBS =
+OPTIONAL_SELINUX_LIBS =
+OPTIONAL_SYSTEMD_LIBS =
+OTOOL =
+OTOOL64 =
+P11KIT_CFLAGS =
+P11KIT_LIBS =
+PACKAGE = openvpn
+PACKAGE_BUGREPORT = openvpn-users@lists.sourceforge.net
+PACKAGE_NAME = OpenVPN
+PACKAGE_STRING = OpenVPN 2.5.4
+PACKAGE_TARNAME = openvpn
+PACKAGE_URL =
+PACKAGE_VERSION = 2.5.4
+PATH_SEPARATOR = :
+PKCS11_HELPER_CFLAGS =
+PKCS11_HELPER_LIBS = -lpthread -ldl -lcrypto -lpkcs11-helper
+PKG_CONFIG = /usr/bin/pkg-config
+PKG_CONFIG_LIBDIR =
+PKG_CONFIG_PATH =
+PLUGINDIR =
+PLUGIN_AUTH_PAM_CFLAGS =
+PLUGIN_AUTH_PAM_LIBS = -lpam
+RANLIB = ranlib
+RC =
+ROUTE = /usr/sbin/route
+RST2HTML = rst2html
+RST2MAN = rst2man
+SED = /usr/bin/sed
+SELINUX_LIBS = -lselinux
+SET_MAKE =
+SHELL = /bin/sh
+SOCKETS_LIBS = -lresolv
+STRIP = strip
+SYSTEMD_ASK_PASSWORD = /usr/bin/systemd-ask-password
+SYSTEMD_UNIT_DIR =
+TAP_CFLAGS =
+TAP_WIN_COMPONENT_ID = tap0901
+TAP_WIN_MIN_MAJOR = 9
+TAP_WIN_MIN_MINOR = 9
+TEST_CFLAGS = -I$(top_srcdir)/include
+TEST_LDFLAGS = -lssl -lcrypto -llzo2
+TMPFILES_DIR =
+VERSION = 2.5.4
+abs_builddir = /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/sample/sample-plugins
+abs_srcdir = /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/sample/sample-plugins
+abs_top_builddir = /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn
+abs_top_srcdir = /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn
+ac_ct_AR = ar
+ac_ct_CC = gcc
+ac_ct_DUMPBIN =
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = $${TAR-tar} chof - "$$tardir"
+am__untar = $${TAR-tar} xf -
+bindir = ${exec_prefix}/bin
+build = x86_64-pc-linux-gnu
+build_alias =
+build_cpu = x86_64
+build_os = linux-gnu
+build_vendor = pc
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = x86_64-pc-linux-gnu
+host_alias =
+host_cpu = x86_64
+host_os = linux-gnu
+host_vendor = pc
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /home/samuli/opt/openvpninc/openvpn-release-scripts/release/openvpn/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+libsystemd_CFLAGS =
+libsystemd_LIBS =
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+mandir = ${datarootdir}/man
+mkdir_p = $(MKDIR_P)
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+plugindir = ${libdir}/openvpn/plugins
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+runstatedir = ${localstatedir}/run
+sampledir = $(docdir)/sample
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+systemdunitdir =
+target_alias =
+tmpfilesdir =
+top_build_prefix = ../../
+top_builddir = ../..
+top_srcdir = ../..
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) \
+ -I$(top_srcdir)/include -I$(top_builddir)/include
+
+
+#
+# Plug-ins to build - listed entries should not carry any extensions
+#
+PLUGINS = \
+ defer/simple \
+ keying-material-exporter-demo/keyingmaterialexporter \
+ log/log log/log_v3 \
+ simple/base64 \
+ simple/simple \
+ client-connect/sample-client-connect
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(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 sample/sample-plugins/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign sample/sample-plugins/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;
+$(srcdir)/Makefile.plugins $(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):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+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-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+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-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 Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am dist-hook distclean distclean-generic \
+ distclean-libtool 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-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# All the plugins to build - rewritten with .so extension
+all : $(foreach var, $(PLUGINS), $(var).so)
+
+# Do not automatically remove object files
+# This is a special Make setting, to avoid adding an implicit
+# 'rm' command on object files - due to the .c.o/%.so rules below
+.PRECIOUS: %.o
+
+# Compile step
+.c.o :
+ test -d `dirname $@` || $(MKDIR_P) `dirname $@`; \
+ $(CC) -c -o $@ $(CFLAGS) $(AM_CPPFLAGS) -fPIC $<
+
+# Link step
+%.so : %.o
+ $(CC) $(LDFLAGS) -shared -fPIC -o $@ $<
+
+# Clean up all build object and shared object files
+clean :
+ rm -f $(foreach var, $(PLUGINS), $(var).o) \
+ $(foreach var, $(PLUGINS), $(var).so)
+
+# We don't want automake to pull in libtool for building these
+# sample-plugins. Even though this breaks the conceptual ideas
+# around autoconf/automake/libtools ... these sample plug-ins
+# are just sample code, not to be installed or distributed outside
+# of the source tarball. Not even built by default, by design.
+#
+# We only add this as a simple and convenient way to build all
+# these plug-ins with the same build parameters as the rest
+# of the OpenVPN code.
+#
+# All the plugins which will be built are processed in this
+# separate Makefile, which disconnects everything just enough
+# to achieve our goal.
+
+dist-hook :
+ make -f Makefile.plugins clean
+
+# 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/sample/sample-plugins/Makefile.am b/sample/sample-plugins/Makefile.am
new file mode 100644
index 0000000..9539d05
--- /dev/null
+++ b/sample/sample-plugins/Makefile.am
@@ -0,0 +1,34 @@
+#
+# 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-2021 OpenVPN Inc <sales@openvpn.net>
+#
+
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) \
+ -I$(top_srcdir)/include -I$(top_builddir)/include
+
+# We don't want automake to pull in libtool for building these
+# sample-plugins. Even though this breaks the conceptual ideas
+# around autoconf/automake/libtools ... these sample plug-ins
+# are just sample code, not to be installed or distributed outside
+# of the source tarball. Not even built by default, by design.
+#
+# We only add this as a simple and convenient way to build all
+# these plug-ins with the same build parameters as the rest
+# of the OpenVPN code.
+#
+# All the plugins which will be built are processed in this
+# separate Makefile, which disconnects everything just enough
+# to achieve our goal.
+include Makefile.plugins
+
+
+dist-hook :
+ make -f Makefile.plugins clean
diff --git a/sample/sample-plugins/Makefile.in b/sample/sample-plugins/Makefile.in
new file mode 100644
index 0000000..8be23e0
--- /dev/null
+++ b/sample/sample-plugins/Makefile.in
@@ -0,0 +1,585 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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-2021 OpenVPN Inc <sales@openvpn.net>
+#
+
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020-2021 OpenVPN Inc <sales@openvpn.net>
+#
+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@
+subdir = sample/sample-plugins
+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_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 =
+SOURCES =
+DIST_SOURCES =
+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)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.plugins \
+ README
+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@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+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@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
+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@
+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@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
+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@
+runstatedir = @runstatedir@
+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@
+MAINTAINERCLEANFILES = \
+ $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) \
+ -I$(top_srcdir)/include -I$(top_builddir)/include
+
+
+#
+# Plug-ins to build - listed entries should not carry any extensions
+#
+PLUGINS = \
+ defer/simple \
+ keying-material-exporter-demo/keyingmaterialexporter \
+ log/log log/log_v3 \
+ simple/base64 \
+ simple/simple \
+ client-connect/sample-client-connect
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(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 sample/sample-plugins/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign sample/sample-plugins/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;
+$(srcdir)/Makefile.plugins $(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):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+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-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+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-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 Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am dist-hook distclean distclean-generic \
+ distclean-libtool 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-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# All the plugins to build - rewritten with .so extension
+all : $(foreach var, $(PLUGINS), $(var).so)
+
+# Do not automatically remove object files
+# This is a special Make setting, to avoid adding an implicit
+# 'rm' command on object files - due to the .c.o/%.so rules below
+.PRECIOUS: %.o
+
+# Compile step
+.c.o :
+ test -d `dirname $@` || $(MKDIR_P) `dirname $@`; \
+ $(CC) -c -o $@ $(CFLAGS) $(AM_CPPFLAGS) -fPIC $<
+
+# Link step
+%.so : %.o
+ $(CC) $(LDFLAGS) -shared -fPIC -o $@ $<
+
+# Clean up all build object and shared object files
+clean :
+ rm -f $(foreach var, $(PLUGINS), $(var).o) \
+ $(foreach var, $(PLUGINS), $(var).so)
+
+# We don't want automake to pull in libtool for building these
+# sample-plugins. Even though this breaks the conceptual ideas
+# around autoconf/automake/libtools ... these sample plug-ins
+# are just sample code, not to be installed or distributed outside
+# of the source tarball. Not even built by default, by design.
+#
+# We only add this as a simple and convenient way to build all
+# these plug-ins with the same build parameters as the rest
+# of the OpenVPN code.
+#
+# All the plugins which will be built are processed in this
+# separate Makefile, which disconnects everything just enough
+# to achieve our goal.
+
+dist-hook :
+ make -f Makefile.plugins clean
+
+# 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/sample/sample-plugins/Makefile.plugins b/sample/sample-plugins/Makefile.plugins
new file mode 100644
index 0000000..44c9cea
--- /dev/null
+++ b/sample/sample-plugins/Makefile.plugins
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020-2021 OpenVPN Inc <sales@openvpn.net>
+#
+
+#
+# Plug-ins to build - listed entries should not carry any extensions
+#
+PLUGINS = \
+ defer/simple \
+ keying-material-exporter-demo/keyingmaterialexporter \
+ log/log log/log_v3 \
+ simple/base64 \
+ simple/simple \
+ client-connect/sample-client-connect
+
+# All the plugins to build - rewritten with .so extension
+all : $(foreach var, $(PLUGINS), $(var).so)
+
+# Do not automatically remove object files
+# This is a special Make setting, to avoid adding an implicit
+# 'rm' command on object files - due to the .c.o/%.so rules below
+.PRECIOUS: %.o
+
+# Compile step
+.c.o :
+ test -d `dirname $@` || $(MKDIR_P) `dirname $@`; \
+ $(CC) -c -o $@ $(CFLAGS) $(AM_CPPFLAGS) -fPIC $<
+
+# Link step
+%.so : %.o
+ $(CC) $(LDFLAGS) -shared -fPIC -o $@ $<
+
+# Clean up all build object and shared object files
+clean :
+ rm -f $(foreach var, $(PLUGINS), $(var).o) \
+ $(foreach var, $(PLUGINS), $(var).so)
diff --git a/sample/sample-plugins/README b/sample/sample-plugins/README
new file mode 100644
index 0000000..cf1b355
--- /dev/null
+++ b/sample/sample-plugins/README
@@ -0,0 +1,43 @@
+OpenVPN plug-in examples.
+
+Examples provided:
+
+* authentication and logging
+simple/simple.c -- using the --auth-user-pass-verify callback, verify
+ that the username/password is "foo"/"bar".
+defer/simple.c -- using the --auth-user-pass-verify callback,
+ test deferred authentication.
+log/log.c -- Extended variant of simple/simple.c which adds more
+ logging of what is happening inside the plug-in
+log/log_v3.c -- A variant of log/log.c, which makes use of the
+ OpenVPN plug-in v3 API. This will also log even more
+ information related to certificates in use.
+
+* client-connect (and logging)
+client-connect/sample-client-connect -- demonstrate how to use the
+ CLIENT_CONNECT and CLIENT_CONNECT_V2 hooks to achieve
+ "per client configuration / logging / ..." actions,
+ both in synchronous and async/deferred mode
+
+* cryptography related
+simple/base64.c -- Example using the OpenVPN exported base64 encode/decode
+ functions
+keying-material-exporter-demo/keyingmaterialexporter.c
+ -- Example based on TLS Keying Material Exporters over HTTP [RFC-5705]
+ (openvpn/doc/keying-material-exporter.txt). For more details, see
+ keying-material-exporter-demo/README
+
+
+To build on *BSD/Linux platforms (requires GNU Make):
+
+ gmake (builds a default set of plug-ins)
+ gmake simple/simple.so
+
+To build on Windows platform (MinGW):
+
+ cd simple; ./winbuild simple.so
+
+To use in OpenVPN, add to config file:
+
+ plugin simple.so (Linux/BSD/etc.)
+ plugin simple.dll
diff --git a/sample/sample-plugins/client-connect/README b/sample/sample-plugins/client-connect/README
new file mode 100644
index 0000000..cb3e0f3
--- /dev/null
+++ b/sample/sample-plugins/client-connect/README
@@ -0,0 +1,38 @@
+OpenVPN plugin examples.
+
+Examples provided:
+
+sample-client-connect.c
+
+ - hook to all plugin hooks that openvpn offers
+ - log which hook got called
+ - on CLIENT_CONNECT or CLIENT_CONNECT_V2 set some config variables
+ (controlled by "setenv plugin_cc_config ..." and "plugin_cc2_config"
+ in openvpn's config)
+
+ - if the environment variable UV_WANT_CC_FAIL is set, fail
+ - if the environment variable UV_WANT_CC_DISABLE is set, reject ("disable")
+ - if the environment variable UV_WANT_CC_ASYNC is set, go to
+ asynchronous/deferred mode on CLIENT_CONNECT, and sleep for
+ ${UV_WANT_CC_ASYNC} seconds
+
+ - if the environment variable UV_WANT_CC2_FAIL is set, fail CC2
+ - if the environment variable UV_WANT_CC2_DISABLE is set, reject ("disable")
+ - if the environment variable UV_WANT_CC2_ASYNC is set, go to
+ asynchronous/deferred mode on CLIENT_CONNECT_V2, and sleep for
+ ${UV_WANT_CC2_ASYNC} seconds
+
+ (this can be client-controlled with --setenv UV_WANT_CC_ASYNC nnn
+ etc. --> for easy testing server code paths)
+
+To build for unixy platforms (not very sophisticated right now, needs gmake):
+
+ .../sample-plugins$ gmake client-connect/sample-client-connect.so
+
+(This plugin has not been tested on Windows, and might not even work due
+to its use of fork() and wait(). Let us know if it does or needs patches)
+
+
+To use in OpenVPN, add to config file:
+
+ plugin sample-client-connect.so (Linux/BSD/etc.)
diff --git a/sample/sample-plugins/client-connect/sample-client-connect.c b/sample/sample-plugins/client-connect/sample-client-connect.c
new file mode 100644
index 0000000..cc85aeb
--- /dev/null
+++ b/sample/sample-plugins/client-connect/sample-client-connect.c
@@ -0,0 +1,612 @@
+/*
+ * 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-2021 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.
+ */
+
+/*
+ * This file implements a simple OpenVPN plugin module which
+ * will log the calls made, and send back some config statements
+ * when called on the CLIENT_CONNECT and CLIENT_CONNECT_V2 hooks.
+ *
+ * it can be asked to fail or go to async/deferred mode by setting
+ * environment variables (UV_WANT_CC_FAIL, UV_WANT_CC_ASYNC,
+ * UV_WANT_CC2_ASYNC) - mostly used as a testing vehicle for the
+ * server side code to handle these cases
+ *
+ * See the README file for build instructions and env control variables.
+ */
+
+/* strdup() might need special defines to be visible in <string.h> */
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "openvpn-plugin.h"
+
+/* Pointers to functions exported from openvpn */
+static plugin_log_t plugin_log = NULL;
+static plugin_secure_memzero_t plugin_secure_memzero = NULL;
+static plugin_base64_decode_t plugin_base64_decode = NULL;
+
+/* module name for plugin_log() */
+static char *MODULE = "sample-cc";
+
+/*
+ * Our context, where we keep our state.
+ */
+
+struct plugin_context {
+ int verb; /* logging verbosity */
+};
+
+/* this is used for the CLIENT_CONNECT_V2 async/deferred handler
+ *
+ * the "CLIENT_CONNECT_V2" handler puts per-client information into
+ * this, and the "CLIENT_CONNECT_DEFER_V2" handler looks at it to see
+ * if it's time yet to succeed/fail
+ */
+struct plugin_per_client_context {
+ time_t sleep_until; /* wakeup time (time() + sleep) */
+ bool want_fail;
+ bool want_disable;
+ const char *client_config;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env(const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen(name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp(envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ {
+ return cp + 1;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static int
+atoi_null0(const char *str)
+{
+ if (str)
+ {
+ return atoi(str);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* use v3 functions so we can use openvpn's logging and base64 etc. */
+OPENVPN_EXPORT int
+openvpn_plugin_open_v3(const int v3structver,
+ struct openvpn_plugin_args_open_in const *args,
+ struct openvpn_plugin_args_open_return *ret)
+{
+ /* const char **argv = args->argv; */ /* command line arguments (unused) */
+ const char **envp = args->envp; /* environment variables */
+
+ /* Check API compatibility -- struct version 5 or higher needed */
+ if (v3structver < 5)
+ {
+ fprintf(stderr, "sample-client-connect: this plugin is incompatible with the running version of OpenVPN\n");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /*
+ * Allocate our context
+ */
+ struct plugin_context *context = calloc(1, sizeof(struct plugin_context));
+ if (!context)
+ {
+ goto error;
+ }
+
+ /*
+ * Intercept just about everything...
+ */
+ ret->type_mask =
+ OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_UP)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_DOWN)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ROUTE_UP)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_IPCHANGE)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS)
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL);
+
+ /* Save global pointers to functions exported from openvpn */
+ plugin_log = args->callbacks->plugin_log;
+ plugin_secure_memzero = args->callbacks->plugin_secure_memzero;
+ plugin_base64_decode = args->callbacks->plugin_base64_decode;
+
+ /*
+ * Get verbosity level from environment
+ */
+ context->verb = atoi_null0(get_env("verb", envp));
+
+ ret->handle = (openvpn_plugin_handle_t *) context;
+ plugin_log(PLOG_NOTE, MODULE, "initialization succeeded");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+
+error:
+ if (context)
+ {
+ free(context);
+ }
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+}
+
+
+/* there are two possible interfaces for an openvpn plugin how
+ * to be called on "client connect", which primarily differ in the
+ * way config options are handed back to the client instance
+ * (see openvpn/multi.c, multi_client_connect_call_plugin_{v1,v2}())
+ *
+ * OPENVPN_PLUGIN_CLIENT_CONNECT
+ * openvpn creates a temp file and passes the name to the plugin
+ * (via argv[1] variable, argv[0] is the name of the plugin)
+ * the plugin can write config statements to that file, and openvpn
+ * reads it in like a "ccd/$cn" per-client config file
+ *
+ * OPENVPN_PLUGIN_CLIENT_CONNECT_V2
+ * the caller passes in a pointer to an "openvpn_plugin_string_list"
+ * (openvpn-plugin.h), which is a linked list of (name,value) pairs
+ *
+ * we fill in one node with name="config" and value="our config"
+ *
+ * both "l" and "l->name" and "l->value" are malloc()ed by the plugin
+ * and free()ed by the caller (openvpn_plugin_string_list_free())
+ */
+
+/* helper function to write actual "here are your options" file,
+ * called from sync and sync handler
+ */
+int
+write_cc_options_file(const char *name, const char **envp)
+{
+ if (!name)
+ {
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ }
+
+ FILE *fp = fopen(name,"w");
+ if (!fp)
+ {
+ plugin_log(PLOG_ERR, MODULE, "fopen('%s') failed", name);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* config to-be-sent can come from "setenv plugin_cc_config" in openvpn */
+ const char *p = get_env("plugin_cc_config", envp);
+ if (p)
+ {
+ fprintf(fp, "%s\n", p);
+ }
+
+ /* some generic config snippets so we know it worked */
+ fprintf(fp, "push \"echo sample-cc plugin 1 called\"\n");
+
+ /* if the caller wants, reject client by means of "disable" option */
+ if (get_env("UV_WANT_CC_DISABLE", envp))
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC_DISABLE, reject");
+ fprintf(fp, "disable\n");
+ }
+ fclose(fp);
+
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+int
+cc_handle_deferred_v1(int seconds, const char *name, const char **envp)
+{
+ const char *ccd_file = get_env("client_connect_deferred_file", envp);
+ if (!ccd_file)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC_ASYNC=%d, but "
+ "'client_connect_deferred_file' not set -> fail", seconds);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* the CLIENT_CONNECT (v1) API is a bit tricky to work with, because
+ * completition can be signalled both by the "deferred_file" and by
+ * the new ...CLIENT_CONNECT_DEFER API - which is optional.
+ *
+ * For OpenVPN to be able to differenciate, we must create the file
+ * right away if we want to use that for signalling.
+ */
+ int fd = open(ccd_file, O_WRONLY);
+ if (fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "open('%s') failed", ccd_file);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ if (write(fd, "2", 1) != 1)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "write to '%s' failed", ccd_file );
+ close(fd);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ close(fd);
+
+ /* we do not want to complicate our lives with having to wait()
+ * for child processes (so they are not zombiefied) *and* we MUST NOT
+ * fiddle with signal handlers (= shared with openvpn main), so
+ * we use double-fork() trick.
+ */
+
+ /* fork, sleep, succeed/fail according to env vars */
+ pid_t p1 = fork();
+ if (p1 < 0) /* Fork failed */
+ {
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ if (p1 > 0) /* parent process */
+ {
+ waitpid(p1, NULL, 0);
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+
+ /* first gen child process, fork() again and exit() right away */
+ pid_t p2 = fork();
+ if (p2 < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
+ exit(1);
+ }
+ if (p2 > 0) /* new parent: exit right away */
+ {
+ exit(0);
+ }
+
+ /* (grand-)child process
+ * - never call "return" now (would mess up openvpn)
+ * - return status is communicated by file
+ * - then exit()
+ */
+
+ /* do mighty complicated work that will really take time here... */
+ plugin_log(PLOG_NOTE, MODULE, "in async/deferred handler, sleep(%d)", seconds);
+ sleep(seconds);
+
+ /* write config options to openvpn */
+ int ret = write_cc_options_file(name, envp);
+
+ /* by setting "UV_WANT_CC_FAIL" we can be triggered to fail */
+ const char *p = get_env("UV_WANT_CC_FAIL", envp);
+ if (p)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC_FAIL=%s -> fail", p);
+ ret = OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* now signal success/failure state to openvpn */
+ fd = open(ccd_file, O_WRONLY);
+ if (fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "open('%s') failed", ccd_file);
+ exit(1);
+ }
+
+ plugin_log(PLOG_NOTE, MODULE, "cc_handle_deferred_v1: done, signalling %s",
+ (ret == OPENVPN_PLUGIN_FUNC_SUCCESS) ? "success" : "fail" );
+
+ if (write(fd, (ret == OPENVPN_PLUGIN_FUNC_SUCCESS) ? "1" : "0", 1) != 1)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "write to '%s' failed", ccd_file );
+ }
+ close(fd);
+
+ exit(0);
+}
+
+int
+openvpn_plugin_client_connect(struct plugin_context *context,
+ const char **argv,
+ const char **envp)
+{
+ /* log environment variables handed to us by OpenVPN, but
+ * only if "setenv verb" is 3 or higher (arbitrary number)
+ */
+ if (context->verb>=3)
+ {
+ for (int i = 0; argv[i]; i++)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "per-client argv: %s", argv[i]);
+ }
+ for (int i = 0; envp[i]; i++)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "per-client env: %s", envp[i]);
+ }
+ }
+
+ /* by setting "UV_WANT_CC_ASYNC" we go to async/deferred mode */
+ const char *p = get_env("UV_WANT_CC_ASYNC", envp);
+ if (p)
+ {
+ /* the return value will usually be OPENVPN_PLUGIN_FUNC_DEFERRED
+ * ("I will do my job in the background, check the status file!")
+ * but depending on env setup it might be "..._ERRROR"
+ */
+ return cc_handle_deferred_v1(atoi(p), argv[1], envp);
+ }
+
+ /* -- this is synchronous mode (openvpn waits for us) -- */
+
+ /* by setting "UV_WANT_CC_FAIL" we can be triggered to fail */
+ p = get_env("UV_WANT_CC_FAIL", envp);
+ if (p)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC_FAIL=%s -> fail", p);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* does the caller want options? give them some */
+ int ret = write_cc_options_file(argv[1], envp);
+
+ return ret;
+}
+
+int
+openvpn_plugin_client_connect_v2(struct plugin_context *context,
+ struct plugin_per_client_context *pcc,
+ const char **envp,
+ struct openvpn_plugin_string_list **return_list)
+{
+ /* by setting "UV_WANT_CC2_ASYNC" we go to async/deferred mode */
+ const char *want_async = get_env("UV_WANT_CC2_ASYNC", envp);
+ const char *want_fail = get_env("UV_WANT_CC2_FAIL", envp);
+ const char *want_disable = get_env("UV_WANT_CC2_DISABLE", envp);
+
+ /* config to push towards client - can be controlled by OpenVPN
+ * config ("setenv plugin_cc2_config ...") - mostly useful in a
+ * regression test environment to push stuff like routes which are
+ * then verified by t_client ping tests
+ */
+ const char *client_config = get_env("plugin_cc2_config", envp);
+ if (!client_config)
+ {
+ /* pick something meaningless which can be verified in client log */
+ client_config = "push \"setenv CC2 MOOH\"\n";
+ }
+
+ if (want_async)
+ {
+ /* we do no really useful work here, so we just tell the
+ * "CLIENT_CONNECT_DEFER_V2" handler that it should sleep
+ * and then "do things" via the per-client-context
+ */
+ pcc->sleep_until = time(NULL) + atoi(want_async);
+ pcc->want_fail = (want_fail != NULL);
+ pcc->want_disable = (want_disable != NULL);
+ pcc->client_config = client_config;
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC2_ASYNC=%s -> set up deferred handler", want_async);
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+
+ /* by setting "UV_WANT_CC2_FAIL" we can be triggered to fail here */
+ if (want_fail)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC2_FAIL=%s -> fail", want_fail);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ struct openvpn_plugin_string_list *rl =
+ calloc(1, sizeof(struct openvpn_plugin_string_list));
+ if (!rl)
+ {
+ plugin_log(PLOG_ERR, MODULE, "malloc(return_list) failed");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ rl->name = strdup("config");
+ if (want_disable)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC2_DISABLE, reject");
+ rl->value = strdup("disable\n");
+ }
+ else
+ {
+ rl->value = strdup(client_config);
+ }
+
+ if (!rl->name || !rl->value)
+ {
+ plugin_log(PLOG_ERR, MODULE, "malloc(return_list->xx) failed");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ *return_list = rl;
+
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+int
+openvpn_plugin_client_connect_defer_v2(struct plugin_context *context,
+ struct plugin_per_client_context *pcc,
+ struct openvpn_plugin_string_list
+ **return_list)
+{
+ time_t time_left = pcc->sleep_until - time(NULL);
+ plugin_log(PLOG_NOTE, MODULE, "defer_v2: seconds left=%d",
+ (int) time_left);
+
+ /* not yet due? */
+ if (time_left > 0)
+ {
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+
+ /* client wants fail? */
+ if (pcc->want_fail)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC2_FAIL -> fail" );
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* fill in RL according to with-disable / without-disable */
+
+ /* TODO: unify this with non-deferred case */
+ struct openvpn_plugin_string_list *rl =
+ calloc(1, sizeof(struct openvpn_plugin_string_list));
+ if (!rl)
+ {
+ plugin_log(PLOG_ERR, MODULE, "malloc(return_list) failed");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ rl->name = strdup("config");
+ if (pcc->want_disable)
+ {
+ plugin_log(PLOG_NOTE, MODULE, "env has UV_WANT_CC2_DISABLE, reject");
+ rl->value = strdup("disable\n");
+ }
+ else
+ {
+ rl->value = strdup(pcc->client_config);
+ }
+
+ if (!rl->name || !rl->value)
+ {
+ plugin_log(PLOG_ERR, MODULE, "malloc(return_list->xx) failed");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ *return_list = rl;
+
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v2(openvpn_plugin_handle_t handle,
+ const int type,
+ const char *argv[],
+ const char *envp[],
+ void *per_client_context,
+ struct openvpn_plugin_string_list **return_list)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context;
+
+ /* for most functions, we just "don't do anything" but log the
+ * event received (so one can follow it in the log and understand
+ * the sequence of events). CONNECT and CONNECT_V2 are handled
+ */
+ switch (type)
+ {
+ case OPENVPN_PLUGIN_UP:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_UP");
+ break;
+
+ case OPENVPN_PLUGIN_DOWN:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_DOWN");
+ break;
+
+ case OPENVPN_PLUGIN_ROUTE_UP:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ROUTE_UP");
+ break;
+
+ case OPENVPN_PLUGIN_IPCHANGE:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_IPCHANGE");
+ break;
+
+ case OPENVPN_PLUGIN_TLS_VERIFY:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_VERIFY");
+ break;
+
+ case OPENVPN_PLUGIN_CLIENT_CONNECT:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT");
+ return openvpn_plugin_client_connect(context, argv, envp);
+
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT_V2");
+ return openvpn_plugin_client_connect_v2(context, pcc, envp,
+ return_list);
+
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2");
+ return openvpn_plugin_client_connect_defer_v2(context, pcc,
+ return_list);
+
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_DISCONNECT");
+ break;
+
+ case OPENVPN_PLUGIN_LEARN_ADDRESS:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_LEARN_ADDRESS");
+ break;
+
+ case OPENVPN_PLUGIN_TLS_FINAL:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_FINAL");
+ break;
+
+ default:
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_? type=%d\n", type);
+ }
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT void *
+openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
+{
+ printf("FUNC: openvpn_plugin_client_constructor_v1\n");
+ return calloc(1, sizeof(struct plugin_per_client_context));
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context)
+{
+ printf("FUNC: openvpn_plugin_client_destructor_v1\n");
+ free(per_client_context);
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ printf("FUNC: openvpn_plugin_close_v1\n");
+ free(context);
+}
diff --git a/sample/sample-plugins/defer/README b/sample/sample-plugins/defer/README
deleted file mode 100644
index d8990f8..0000000
--- a/sample/sample-plugins/defer/README
+++ /dev/null
@@ -1,16 +0,0 @@
-OpenVPN plugin examples.
-
-Examples provided:
-
-simple.c -- using the --auth-user-pass-verify callback,
- test deferred authentication.
-
-To build:
-
- ./build simple (Linux/BSD/etc.)
- ./winbuild simple (MinGW on Windows)
-
-To use in OpenVPN, add to config file:
-
- plugin simple.so (Linux/BSD/etc.)
- plugin simple.dll (MinGW on Windows)
diff --git a/sample/sample-plugins/defer/build b/sample/sample-plugins/defer/build
deleted file mode 100755
index ba41a39..0000000
--- a/sample/sample-plugins/defer/build
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-#
-# Build an OpenVPN plugin module on *nix. The argument should
-# be the base name of the C source file (without the .c).
-#
-
-# This directory is where we will look for openvpn-plugin.h
-CPPFLAGS="${CPPFLAGS:--I../../../include}"
-
-CC="${CC:-gcc}"
-CFLAGS="${CFLAGS:--O2 -Wall -g}"
-
-$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \
-$CC $CFLAGS -fPIC -shared ${LDFLAGS} -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/sample/sample-plugins/defer/simple.c b/sample/sample-plugins/defer/simple.c
index d18695b..ba2e03e 100644
--- a/sample/sample-plugins/defer/simple.c
+++ b/sample/sample-plugins/defer/simple.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -54,13 +54,30 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include "openvpn-plugin.h"
-/* bool definitions */
-#define bool int
-#define true 1
-#define false 0
+/* Pointers to functions exported from openvpn */
+static plugin_log_t plugin_log = NULL;
+
+/*
+ * Constants indicating minimum API and struct versions by the functions
+ * in this plugin. Consult openvpn-plugin.h, look for:
+ * OPENVPN_PLUGIN_VERSION and OPENVPN_PLUGINv3_STRUCTVER
+ *
+ * Strictly speaking, this sample code only requires plugin_log, a feature
+ * of structver version 1. However, '1' lines up with ancient versions
+ * of openvpn that are past end-of-support. As such, we are requiring
+ * structver '5' here to indicate a desire for modern openvpn, rather
+ * than a need for any particular feature found in structver beyond '1'.
+ */
+#define OPENVPN_PLUGIN_VERSION_MIN 3
+#define OPENVPN_PLUGIN_STRUCTVER_MIN 5
/*
* Our context, where we keep our state.
@@ -76,6 +93,9 @@ struct plugin_per_client_context {
bool generated_pf_file;
};
+/* module name for plugin_log() */
+static char *MODULE = "defer/simple";
+
/*
* Given an environmental variable name, search
* the envp array for its value, returning it
@@ -130,28 +150,52 @@ atoi_null0(const char *str)
}
}
-OPENVPN_EXPORT openvpn_plugin_handle_t
-openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *envp[])
+/* Require a minimum OpenVPN Plugin API */
+OPENVPN_EXPORT int
+openvpn_plugin_min_version_required_v1()
{
+ return OPENVPN_PLUGIN_VERSION_MIN;
+}
+
+/* use v3 functions so we can use openvpn's logging and base64 etc. */
+OPENVPN_EXPORT int
+openvpn_plugin_open_v3(const int v3structver,
+ struct openvpn_plugin_args_open_in const *args,
+ struct openvpn_plugin_args_open_return *ret)
+{
+ const char **envp = args->envp; /* environment variables */
struct plugin_context *context;
- printf("FUNC: openvpn_plugin_open_v1\n");
+ if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
+ {
+ fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* Save global pointers to functions exported from openvpn */
+ plugin_log = args->callbacks->plugin_log;
+
+ plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_open_v3");
/*
* Allocate our context
*/
context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
+ if (!context)
+ {
+ goto error;
+ }
context->test_deferred_auth = atoi_null0(get_env("test_deferred_auth", envp));
- printf("TEST_DEFERRED_AUTH %d\n", context->test_deferred_auth);
+ plugin_log(PLOG_NOTE, MODULE, "TEST_DEFERRED_AUTH %d", context->test_deferred_auth);
context->test_packet_filter = atoi_null0(get_env("test_packet_filter", envp));
- printf("TEST_PACKET_FILTER %d\n", context->test_packet_filter);
+ plugin_log(PLOG_NOTE, MODULE, "TEST_PACKET_FILTER %d", context->test_packet_filter);
/*
* Which callbacks to intercept.
*/
- *type_mask =
+ ret->type_mask =
OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_UP)
|OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_DOWN)
|OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ROUTE_UP)
@@ -161,157 +205,315 @@ openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *
|OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2)
|OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
|OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS)
- |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL)
- |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ENABLE_PF);
+ |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL);
- return (openvpn_plugin_handle_t) context;
+ /* ENABLE_PF should only be called if we're actually willing to do PF */
+ if (context->test_packet_filter)
+ {
+ ret->type_mask |= OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ENABLE_PF);
+ }
+
+ ret->handle = (openvpn_plugin_handle_t *) context;
+ plugin_log(PLOG_NOTE, MODULE, "initialization succeeded");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+
+error:
+ if (context)
+ {
+ free(context);
+ }
+ plugin_log(PLOG_NOTE, MODULE, "initialization failed");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
}
static int
-auth_user_pass_verify(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
+auth_user_pass_verify(struct plugin_context *context,
+ struct plugin_per_client_context *pcc,
+ const char *argv[], const char *envp[])
{
- if (context->test_deferred_auth)
+ if (!context->test_deferred_auth)
{
- /* get username/password from envp string array */
- const char *username = get_env("username", envp);
- const char *password = get_env("password", envp);
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ }
+
+ /* get username/password from envp string array */
+ const char *username = get_env("username", envp);
+ const char *password = get_env("password", envp);
- /* get auth_control_file filename from envp string array*/
- const char *auth_control_file = get_env("auth_control_file", envp);
+ /* get auth_control_file filename from envp string array*/
+ const char *auth_control_file = get_env("auth_control_file", envp);
- printf("DEFER u='%s' p='%s' acf='%s'\n",
+ plugin_log(PLOG_NOTE, MODULE, "DEFER u='%s' p='%s' acf='%s'",
np(username),
np(password),
np(auth_control_file));
- /* Authenticate asynchronously in n seconds */
- if (auth_control_file)
- {
- char buf[256];
- int auth = 2;
- sscanf(username, "%d", &auth);
- snprintf(buf, sizeof(buf), "( sleep %d ; echo AUTH %s %d ; echo %d >%s ) &",
- context->test_deferred_auth,
- auth_control_file,
- auth,
- pcc->n_calls < auth,
- auth_control_file);
- printf("%s\n", buf);
- system(buf);
- pcc->n_calls++;
- return OPENVPN_PLUGIN_FUNC_DEFERRED;
- }
- else
- {
- return OPENVPN_PLUGIN_FUNC_ERROR;
- }
+ /* Authenticate asynchronously in n seconds */
+ if (!auth_control_file)
+ {
+ return OPENVPN_PLUGIN_FUNC_ERROR;
}
- else
+
+ /* we do not want to complicate our lives with having to wait()
+ * for child processes (so they are not zombiefied) *and* we MUST NOT
+ * fiddle with signal handlers (= shared with openvpn main), so
+ * we use double-fork() trick.
+ */
+
+ /* fork, sleep, succeed (no "real" auth done = always succeed) */
+ pid_t p1 = fork();
+ if (p1 < 0) /* Fork failed */
{
- return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ if (p1 > 0) /* parent process */
+ {
+ waitpid(p1, NULL, 0);
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+
+ /* first gen child process, fork() again and exit() right away */
+ pid_t p2 = fork();
+ if (p2 < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
+ exit(1);
+ }
+
+ if (p2 != 0) /* new parent: exit right away */
+ {
+ exit(0);
+ }
+
+ /* (grand-)child process
+ * - never call "return" now (would mess up openvpn)
+ * - return status is communicated by file
+ * - then exit()
+ */
+
+ /* do mighty complicated work that will really take time here... */
+ plugin_log(PLOG_NOTE, MODULE, "in async/deferred handler, sleep(%d)", context->test_deferred_auth);
+ sleep(context->test_deferred_auth);
+
+ /* now signal success state to openvpn */
+ int fd = open(auth_control_file, O_WRONLY);
+ if (fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "open('%s') failed", auth_control_file);
+ exit(1);
}
+
+ plugin_log(PLOG_NOTE, MODULE, "auth_user_pass_verify: done" );
+
+ if (write(fd, "1", 1) != 1)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "write to '%s' failed", auth_control_file );
+ }
+ close(fd);
+
+ exit(0);
}
static int
tls_final(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
{
- if (context->test_packet_filter)
+ if (!context->test_packet_filter) /* no PF testing, nothing to do */
{
- if (!pcc->generated_pf_file)
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ }
+
+ if (pcc->generated_pf_file) /* we already have created a file */
+ {
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ const char *pff = get_env("pf_file", envp);
+ const char *cn = get_env("username", envp);
+ if (!pff || !cn) /* required vars missing */
+ {
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ pcc->generated_pf_file = true;
+
+ /* the PF API is, basically
+ * - OpenVPN sends a filename (pf_file) to the plugin
+ * - OpenVPN main loop will check every second if that file shows up
+ * - when it does, it will be read & used for the pf config
+ * the pre-created file needs to be removed in ...ENABLE_PF
+ * to make deferred PF setup work
+ *
+ * the regular PF hook does not know the client username or CN, so
+ * this is deferred to the TLS_FINAL hook which knows these things
+ */
+
+ /* do the double fork dance (see above for more verbose comments)
+ */
+ pid_t p1 = fork();
+ if (p1 < 0) /* Fork failed */
+ {
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ if (p1 > 0) /* parent process */
+ {
+ waitpid(p1, NULL, 0);
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; /* no _DEFERRED here! */
+ }
+
+ /* first gen child process, fork() again and exit() right away */
+ pid_t p2 = fork();
+ if (p2 < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
+ exit(1);
+ }
+
+ if (p2 != 0) /* new parent: exit right away */
+ {
+ exit(0);
+ }
+
+ /* (grand-)child process
+ * - never call "return" now (would mess up openvpn)
+ * - return status is communicated by file
+ * - then exit()
+ */
+
+ /* at this point, the plugin can take its time, because OpenVPN will
+ * no longer block waiting for the call to finish
+ *
+ * in this example, we build a PF file by copying over a file
+ * named "<username>.pf" to the OpenVPN-provided pf file name
+ *
+ * a real example could do a LDAP lookup, a REST call, ...
+ */
+ plugin_log(PLOG_NOTE, MODULE, "in async/deferred tls_final handler, sleep(%d)", context->test_packet_filter);
+ sleep(context->test_packet_filter);
+
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s.pf", cn );
+
+ /* there is a small race condition here - OpenVPN could detect our
+ * file while we have only written half of it. So "perfect" code
+ * needs to create this with a temp file name, and then rename() it
+ * after it has been written. But I am lazy.
+ */
+
+ int w_fd = open( pff, O_WRONLY|O_CREAT, 0600 );
+ if (w_fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "can't write to '%s'", pff);
+ exit(0);
+ }
+
+ int r_fd = open( buf, O_RDONLY );
+ if (r_fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "can't read '%s', creating empty pf file", buf);
+ close(w_fd);
+ exit(0);
+ }
+
+ char data[1024];
+
+ int r;
+ do
+ {
+ r = read(r_fd, data, sizeof(data));
+ if (r < 0)
{
- const char *pff = get_env("pf_file", envp);
- const char *cn = get_env("username", envp);
- if (pff && cn)
- {
- char buf[256];
- snprintf(buf, sizeof(buf), "( sleep %d ; echo PF %s/%s ; cp \"%s.pf\" \"%s\" ) &",
- context->test_packet_filter, cn, pff, cn, pff);
- printf("%s\n", buf);
- system(buf);
- pcc->generated_pf_file = true;
- return OPENVPN_PLUGIN_FUNC_SUCCESS;
- }
- else
- {
- return OPENVPN_PLUGIN_FUNC_ERROR;
- }
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "error reading '%s'", buf);
+ close(r_fd);
+ close(w_fd);
+ exit(0);
}
- else
+ int w = write(w_fd, data, r);
+ if (w < 0 || w != r)
{
- return OPENVPN_PLUGIN_FUNC_ERROR;
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "error writing %d bytes to '%s'", r, pff);
+ close(r_fd);
+ close(w_fd);
+ exit(0);
}
}
- else
- {
- return OPENVPN_PLUGIN_FUNC_SUCCESS;
- }
+ while(r > 0);
+
+ plugin_log(PLOG_NOTE, MODULE, "copied PF config from '%s' to '%s', job done", buf, pff);
+ exit(0);
}
OPENVPN_EXPORT int
-openvpn_plugin_func_v2(openvpn_plugin_handle_t handle,
- const int type,
- const char *argv[],
- const char *envp[],
- void *per_client_context,
- struct openvpn_plugin_string_list **return_list)
+openvpn_plugin_func_v3(const int v3structver,
+ struct openvpn_plugin_args_func_in const *args,
+ struct openvpn_plugin_args_func_return *ret)
{
- struct plugin_context *context = (struct plugin_context *) handle;
- struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context;
- switch (type)
+ if (v3structver < OPENVPN_PLUGIN_STRUCTVER_MIN)
+ {
+ fprintf(stderr, "%s: this plugin is incompatible with the running version of OpenVPN\n", MODULE);
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ const char **argv = args->argv;
+ const char **envp = args->envp;
+ struct plugin_context *context = (struct plugin_context *) args->handle;
+ struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) args->per_client_context;
+ switch (args->type)
{
case OPENVPN_PLUGIN_UP:
- printf("OPENVPN_PLUGIN_UP\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_UP");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_DOWN:
- printf("OPENVPN_PLUGIN_DOWN\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_DOWN");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_ROUTE_UP:
- printf("OPENVPN_PLUGIN_ROUTE_UP\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ROUTE_UP");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_IPCHANGE:
- printf("OPENVPN_PLUGIN_IPCHANGE\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_IPCHANGE");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_TLS_VERIFY:
- printf("OPENVPN_PLUGIN_TLS_VERIFY\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_VERIFY");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
- printf("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY");
return auth_user_pass_verify(context, pcc, argv, envp);
case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
- printf("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_CONNECT_V2");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
- printf("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_CLIENT_DISCONNECT");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_LEARN_ADDRESS:
- printf("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_LEARN_ADDRESS");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_TLS_FINAL:
- printf("OPENVPN_PLUGIN_TLS_FINAL\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_TLS_FINAL");
return tls_final(context, pcc, argv, envp);
case OPENVPN_PLUGIN_ENABLE_PF:
- printf("OPENVPN_PLUGIN_ENABLE_PF\n");
- if (context->test_packet_filter)
- {
- return OPENVPN_PLUGIN_FUNC_SUCCESS;
- }
- else
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_ENABLE_PF");
+
+ /* OpenVPN pre-creates the file, which gets in the way of
+ * deferred pf setup - so remove it here, and re-create
+ * it in the background handler (in tls_final()) when ready
+ */
+ const char *pff = get_env("pf_file", envp);
+ if (pff)
{
- return OPENVPN_PLUGIN_FUNC_ERROR;
+ (void) unlink(pff);
}
+ return OPENVPN_PLUGIN_FUNC_SUCCESS; /* must succeed */
default:
- printf("OPENVPN_PLUGIN_?\n");
+ plugin_log(PLOG_NOTE, MODULE, "OPENVPN_PLUGIN_?");
return OPENVPN_PLUGIN_FUNC_ERROR;
}
}
@@ -319,14 +521,14 @@ openvpn_plugin_func_v2(openvpn_plugin_handle_t handle,
OPENVPN_EXPORT void *
openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle)
{
- printf("FUNC: openvpn_plugin_client_constructor_v1\n");
+ plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_constructor_v1");
return calloc(1, sizeof(struct plugin_per_client_context));
}
OPENVPN_EXPORT void
openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context)
{
- printf("FUNC: openvpn_plugin_client_destructor_v1\n");
+ plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_client_destructor_v1");
free(per_client_context);
}
@@ -334,6 +536,6 @@ OPENVPN_EXPORT void
openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
{
struct plugin_context *context = (struct plugin_context *) handle;
- printf("FUNC: openvpn_plugin_close_v1\n");
+ plugin_log(PLOG_NOTE, MODULE, "FUNC: openvpn_plugin_close_v1");
free(context);
}
diff --git a/sample/sample-plugins/keying-material-exporter-demo/build b/sample/sample-plugins/keying-material-exporter-demo/build
deleted file mode 100755
index bbb05f7..0000000
--- a/sample/sample-plugins/keying-material-exporter-demo/build
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-#
-# Build an OpenVPN plugin module on *nix. The argument should
-# be the base name of the C source file (without the .c).
-#
-
-# This directory is where we will look for openvpn-plugin.h
-CPPFLAGS="${CPPFLAGS:--I../../..}"
-
-CC="${CC:-gcc}"
-CFLAGS="${CFLAGS:--O2 -Wall -g}"
-
-$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \
-$CC $CFLAGS -fPIC -shared $LDFLAGS -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/sample/sample-plugins/keying-material-exporter-demo/keyingmaterialexporter.c b/sample/sample-plugins/keying-material-exporter-demo/keyingmaterialexporter.c
index 5d3ca14..787fc54 100644
--- a/sample/sample-plugins/keying-material-exporter-demo/keyingmaterialexporter.c
+++ b/sample/sample-plugins/keying-material-exporter-demo/keyingmaterialexporter.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -27,8 +27,6 @@
* See the README file for build instructions.
*/
-#define ENABLE_CRYPTO
-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -94,6 +92,12 @@ openvpn_plugin_open_v3(const int version,
{
struct plugin *plugin = calloc(1, sizeof(*plugin));
+ if (plugin == NULL)
+ {
+ printf("PLUGIN: allocating memory for context failed\n");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
plugin->type = get_env("remote_1", args->envp) ? CLIENT : SERVER;
plugin->log = args->callbacks->plugin_log;
@@ -232,7 +236,8 @@ tls_final(struct openvpn_plugin_args_func_in const *args,
snprintf(sess->key, sizeof(sess->key) - 1, "%s", key);
ovpn_note("app session key: %s", sess->key);
- switch (plugin->type) {
+ switch (plugin->type)
+ {
case SERVER:
server_store(args);
break;
@@ -251,7 +256,8 @@ openvpn_plugin_func_v3(const int version,
struct openvpn_plugin_args_func_in const *args,
struct openvpn_plugin_args_func_return *rv)
{
- switch (args->type) {
+ switch (args->type)
+ {
case OPENVPN_PLUGIN_TLS_VERIFY:
return tls_verify(args);
diff --git a/sample/sample-plugins/log/build b/sample/sample-plugins/log/build
deleted file mode 100755
index c07ec40..0000000
--- a/sample/sample-plugins/log/build
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-#
-# Build an OpenVPN plugin module on *nix. The argument should
-# be the base name of the C source file (without the .c).
-#
-
-# This directory is where we will look for openvpn-plugin.h
-CPPFLAGS="${CPPFLAGS:--I../../../include}"
-
-CC="${CC:-gcc}"
-CFLAGS="${CFLAGS:--O2 -Wall -g}"
-
-$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \
-$CC $CFLAGS -fPIC -shared $LDFLAGS -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/sample/sample-plugins/log/log.c b/sample/sample-plugins/log/log.c
index ecf62c0..661ec5d 100644
--- a/sample/sample-plugins/log/log.c
+++ b/sample/sample-plugins/log/log.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -78,6 +78,11 @@ openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *
* Allocate our context
*/
context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
+ if (context == NULL)
+ {
+ printf("PLUGIN: allocating memory for context failed\n");
+ return NULL;
+ }
/*
* Set the username/password we will require.
@@ -156,11 +161,15 @@ show(const int type, const char *argv[], const char *envp[])
printf("ARGV\n");
for (i = 0; argv[i] != NULL; ++i)
+ {
printf("%d '%s'\n", (int)i, argv[i]);
+ }
printf("ENVP\n");
for (i = 0; envp[i] != NULL; ++i)
+ {
printf("%d '%s'\n", (int)i, envp[i]);
+ }
}
OPENVPN_EXPORT int
diff --git a/sample/sample-plugins/log/log_v3.c b/sample/sample-plugins/log/log_v3.c
index c972951..7ae77a8 100644
--- a/sample/sample-plugins/log/log_v3.c
+++ b/sample/sample-plugins/log/log_v3.c
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
- * Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 David Sommerseth <dazo@eurephia.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -35,8 +35,6 @@
#include <string.h>
#include <stdlib.h>
-#define ENABLE_CRYPTO
-
#include "openvpn-plugin.h"
/*
@@ -115,6 +113,11 @@ openvpn_plugin_open_v3(const int v3structver,
/* Allocate our context */
context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
+ if (context == NULL)
+ {
+ printf("PLUGIN: allocating memory for context failed\n");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
/* Set the username/password we will require. */
context->username = "foo";
@@ -179,11 +182,15 @@ show(const int type, const char *argv[], const char *envp[])
printf("ARGV\n");
for (i = 0; argv[i] != NULL; ++i)
+ {
printf("%d '%s'\n", (int)i, argv[i]);
+ }
printf("ENVP\n");
for (i = 0; envp[i] != NULL; ++i)
+ {
printf("%d '%s'\n", (int)i, envp[i]);
+ }
}
static void
@@ -196,7 +203,7 @@ x509_print_info(X509 *x509crt)
X509_NAME *x509_name;
X509_NAME_ENTRY *ent;
const char *objbuf;
- unsigned char *buf;
+ unsigned char *buf = NULL;
x509_name = X509_get_subject_name(x509crt);
n = X509_NAME_entry_count(x509_name);
diff --git a/sample/sample-plugins/simple/README b/sample/sample-plugins/simple/README
deleted file mode 100644
index 4400cd3..0000000
--- a/sample/sample-plugins/simple/README
+++ /dev/null
@@ -1,16 +0,0 @@
-OpenVPN plugin examples.
-
-Examples provided:
-
-simple.c -- using the --auth-user-pass-verify callback, verify
- that the username/password is "foo"/"bar".
-
-To build:
-
- ./build simple (Linux/BSD/etc.)
- ./winbuild simple (MinGW on Windows)
-
-To use in OpenVPN, add to config file:
-
- plugin simple.so (Linux/BSD/etc.)
- plugin simple.dll (MinGW on Windows)
diff --git a/sample/sample-plugins/simple/base64.c b/sample/sample-plugins/simple/base64.c
index bd95e79..363a123 100644
--- a/sample/sample-plugins/simple/base64.c
+++ b/sample/sample-plugins/simple/base64.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2017 David Sommerseth <davids@openvpn.net>
+ * Copyright (C) 2017-2021 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
diff --git a/sample/sample-plugins/simple/build b/sample/sample-plugins/simple/build
deleted file mode 100755
index bbb05f7..0000000
--- a/sample/sample-plugins/simple/build
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-#
-# Build an OpenVPN plugin module on *nix. The argument should
-# be the base name of the C source file (without the .c).
-#
-
-# This directory is where we will look for openvpn-plugin.h
-CPPFLAGS="${CPPFLAGS:--I../../..}"
-
-CC="${CC:-gcc}"
-CFLAGS="${CFLAGS:--O2 -Wall -g}"
-
-$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \
-$CC $CFLAGS -fPIC -shared $LDFLAGS -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/sample/sample-plugins/simple/simple.c b/sample/sample-plugins/simple/simple.c
index 950c547..0f26dd2 100644
--- a/sample/sample-plugins/simple/simple.c
+++ b/sample/sample-plugins/simple/simple.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -80,6 +80,11 @@ openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *
* Allocate our context
*/
context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context));
+ if (context == NULL)
+ {
+ printf("PLUGIN: allocating memory for context failed\n");
+ return NULL;
+ }
/*
* Set the username/password we will require.
diff --git a/sample/sample-windows/sample.ovpn b/sample/sample-windows/sample.ovpn
index 5accd57..51e3274 100755
--- a/sample/sample-windows/sample.ovpn
+++ b/sample/sample-windows/sample.ovpn
@@ -68,7 +68,7 @@ ifconfig 10.3.0.1 255.255.255.0
#
# You can also generate key.txt manually
# with the following command:
-# openvpn --genkey --secret key.txt
+# openvpn --genkey secret key.txt
#
# key must match on both ends of the connection,
# so you should generate it on one machine and
diff --git a/src/Makefile.am b/src/Makefile.am
index c7f6302..5d7935b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,11 +5,11 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
-SUBDIRS = compat openvpn openvpnserv plugins
+SUBDIRS = compat openvpn openvpnmsica openvpnserv plugins tapctl
diff --git a/src/Makefile.in b/src/Makefile.in
index 841c1d8..e12c2f4 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
VPATH = @srcdir@
@@ -209,7 +209,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -223,6 +224,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -250,7 +252,6 @@ 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@
@@ -301,6 +302,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -364,6 +367,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -378,7 +382,7 @@ top_srcdir = @top_srcdir@
MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
-SUBDIRS = compat openvpn openvpnserv plugins
+SUBDIRS = compat openvpn openvpnmsica openvpnserv plugins tapctl
all: all-recursive
.SUFFIXES:
diff --git a/src/compat/Debug.props b/src/compat/Debug.props
new file mode 100644
index 0000000..31bb9d9
--- /dev/null
+++ b/src/compat/Debug.props
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets">
+ <Import Project="PropertySheet.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_PropertySheetDisplayName>compat-Debug</_PropertySheetDisplayName>
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
index b4c3a4a..c985679 100644
--- a/src/compat/Makefile.am
+++ b/src/compat/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -14,7 +14,10 @@ MAINTAINERCLEANFILES = \
EXTRA_DIST = \
compat.vcxproj \
- compat.vcxproj.filters
+ compat.vcxproj.filters \
+ PropertySheet.props \
+ Debug.props \
+ Release.props
noinst_LTLIBRARIES = libcompat.la
@@ -27,4 +30,5 @@ libcompat_la_SOURCES = \
compat-inet_ntop.c \
compat-inet_pton.c \
compat-lz4.c compat-lz4.h \
+ compat-strsep.c \
compat-versionhelpers.h
diff --git a/src/compat/Makefile.in b/src/compat/Makefile.in
index 8e45c84..83fc656 100644
--- a/src/compat/Makefile.in
+++ b/src/compat/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -120,7 +120,7 @@ LTLIBRARIES = $(noinst_LTLIBRARIES)
libcompat_la_LIBADD =
am_libcompat_la_OBJECTS = compat-dirname.lo compat-basename.lo \
compat-gettimeofday.lo compat-daemon.lo compat-inet_ntop.lo \
- compat-inet_pton.lo compat-lz4.lo
+ compat-inet_pton.lo compat-lz4.lo compat-strsep.lo
libcompat_la_OBJECTS = $(am_libcompat_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -145,7 +145,8 @@ am__depfiles_remade = ./$(DEPDIR)/compat-basename.Plo \
./$(DEPDIR)/compat-daemon.Plo ./$(DEPDIR)/compat-dirname.Plo \
./$(DEPDIR)/compat-gettimeofday.Plo \
./$(DEPDIR)/compat-inet_ntop.Plo \
- ./$(DEPDIR)/compat-inet_pton.Plo ./$(DEPDIR)/compat-lz4.Plo
+ ./$(DEPDIR)/compat-inet_pton.Plo ./$(DEPDIR)/compat-lz4.Plo \
+ ./$(DEPDIR)/compat-strsep.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -205,7 +206,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -219,6 +221,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -246,7 +249,6 @@ 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@
@@ -297,6 +299,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -360,6 +364,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -376,7 +381,10 @@ MAINTAINERCLEANFILES = \
EXTRA_DIST = \
compat.vcxproj \
- compat.vcxproj.filters
+ compat.vcxproj.filters \
+ PropertySheet.props \
+ Debug.props \
+ Release.props
noinst_LTLIBRARIES = libcompat.la
libcompat_la_SOURCES = \
@@ -388,6 +396,7 @@ libcompat_la_SOURCES = \
compat-inet_ntop.c \
compat-inet_pton.c \
compat-lz4.c compat-lz4.h \
+ compat-strsep.c \
compat-versionhelpers.h
all: all-am
@@ -451,6 +460,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-inet_ntop.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-inet_pton.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-lz4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compat-strsep.Plo@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@@ -618,6 +628,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/compat-inet_ntop.Plo
-rm -f ./$(DEPDIR)/compat-inet_pton.Plo
-rm -f ./$(DEPDIR)/compat-lz4.Plo
+ -rm -f ./$(DEPDIR)/compat-strsep.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -670,6 +681,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/compat-inet_ntop.Plo
-rm -f ./$(DEPDIR)/compat-inet_pton.Plo
-rm -f ./$(DEPDIR)/compat-lz4.Plo
+ -rm -f ./$(DEPDIR)/compat-strsep.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/src/compat/PropertySheet.props b/src/compat/PropertySheet.props
new file mode 100644
index 0000000..4f94b97
--- /dev/null
+++ b/src/compat/PropertySheet.props
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets" />
+ <PropertyGroup Label="UserMacros">
+ <SOURCEBASE>$(SolutionDir)</SOURCEBASE>
+ <OPENVPN_DEPROOT>$(SOURCEBASE)\..\openvpn-build\msvc\image$(PlatformArchitecture)</OPENVPN_DEPROOT>
+ <OPENSSL_HOME>$(OPENVPN_DEPROOT)</OPENSSL_HOME>
+ <TAP_WINDOWS_HOME>$(OPENVPN_DEPROOT)</TAP_WINDOWS_HOME>
+ <LZO_HOME>$(OPENVPN_DEPROOT)</LZO_HOME>
+ <PKCS11H_HOME>$(OPENVPN_DEPROOT)</PKCS11H_HOME>
+ </PropertyGroup>
+ <PropertyGroup>
+ <OutDir>$(SolutionDir)$(Platform)-Output\$(Configuration)\</OutDir>
+ <_PropertySheetDisplayName>compat</_PropertySheetDisplayName>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(SOURCEBASE);$(SOURCEBASE)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <BuildMacro Include="SOURCEBASE">
+ <Value>$(SOURCEBASE)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="OPENVPN_DEPROOT">
+ <Value>$(OPENVPN_DEPROOT)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="OPENSSL_HOME">
+ <Value>$(OPENSSL_HOME)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="TAP_WINDOWS_HOME">
+ <Value>$(TAP_WINDOWS_HOME)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="LZO_HOME">
+ <Value>$(LZO_HOME)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ <BuildMacro Include="PKCS11H_HOME">
+ <Value>$(PKCS11H_HOME)</Value>
+ <EnvironmentVariable>true</EnvironmentVariable>
+ </BuildMacro>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/compat/Release.props b/src/compat/Release.props
new file mode 100644
index 0000000..63828b7
--- /dev/null
+++ b/src/compat/Release.props
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets">
+ <Import Project="PropertySheet.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_PropertySheetDisplayName>compat-Release</_PropertySheetDisplayName>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/compat/compat-gettimeofday.c b/src/compat/compat-gettimeofday.c
index 7cae641..8ce586b 100644
--- a/src/compat/compat-gettimeofday.c
+++ b/src/compat/compat-gettimeofday.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/compat/compat-lz4.c b/src/compat/compat-lz4.c
index 723157d..26a3980 100644
--- a/src/compat/compat-lz4.c
+++ b/src/compat/compat-lz4.c
@@ -1,5 +1,5 @@
/* This file has been backported by dev-tools/lz4-rebaser.sh
- * from upstream lz4 commit 7bb64ff2b69a9f8367de (v1.7.5)
+ * from upstream lz4 commit fdf2ef5809ca875c4545 (v1.9.2)
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -10,7 +10,7 @@
#ifdef NEED_COMPAT_LZ4
/*
LZ4 - Fast LZ compression algorithm
- Copyright (C) 2011-2016, Yann Collet.
+ Copyright (C) 2011-present, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@@ -42,17 +42,16 @@
- LZ4 source repository : https://github.com/lz4/lz4
*/
-
/*-************************************
* Tuning parameters
**************************************/
/*
- * HEAPMODE :
+ * LZ4_HEAPMODE :
* Select how default compression functions will allocate memory for their hash table,
* in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
*/
-#ifndef HEAPMODE
-# define HEAPMODE 0
+#ifndef LZ4_HEAPMODE
+# define LZ4_HEAPMODE 0
#endif
/*
@@ -73,16 +72,17 @@
* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
* Method 2 : direct access. This method is portable but violate C standard.
- * It can generate buggy code on targets which generate assembly depending on alignment.
+ * It can generate buggy code on targets which assembly generation depends on alignment.
* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
* See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
* Prefer these methods in priority order (0 > 1 > 2)
*/
-#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
-# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */
+# if defined(__GNUC__) && \
+ ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
+ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
# define LZ4_FORCE_MEMORY_ACCESS 2
-# elif defined(__INTEL_COMPILER) || \
- (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
+# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)
# define LZ4_FORCE_MEMORY_ACCESS 1
# endif
#endif
@@ -91,14 +91,32 @@
* LZ4_FORCE_SW_BITCOUNT
* Define this parameter if your target system or compiler does not support hardware bit count
*/
-#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */
# define LZ4_FORCE_SW_BITCOUNT
#endif
+
/*-************************************
* Dependency
**************************************/
+/*
+ * LZ4_SRC_INCLUDED:
+ * Amalgamation flag, whether lz4.c is included
+ */
+#ifndef LZ4_SRC_INCLUDED
+# define LZ4_SRC_INCLUDED 1
+#endif
+
+#ifndef LZ4_STATIC_LINKING_ONLY
+#define LZ4_STATIC_LINKING_ONLY
+#endif
+
+#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS
+#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */
+#endif
+
+#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */
#include "compat-lz4.h"
/* see also "memory routines" below */
@@ -107,42 +125,130 @@
* Compiler Options
**************************************/
#ifdef _MSC_VER /* Visual Studio */
-# define FORCE_INLINE static __forceinline
# include <intrin.h>
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
-#else
-# if defined(__GNUC__) || defined(__clang__)
-# define FORCE_INLINE static inline __attribute__((always_inline))
-# elif defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-# define FORCE_INLINE static inline
-# else
-# define FORCE_INLINE static
-# endif
#endif /* _MSC_VER */
+#ifndef LZ4_FORCE_INLINE
+# ifdef _MSC_VER /* Visual Studio */
+# define LZ4_FORCE_INLINE static __forceinline
+# else
+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# ifdef __GNUC__
+# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
+# else
+# define LZ4_FORCE_INLINE static inline
+# endif
+# else
+# define LZ4_FORCE_INLINE static
+# endif /* __STDC_VERSION__ */
+# endif /* _MSC_VER */
+#endif /* LZ4_FORCE_INLINE */
+
+/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+ * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,
+ * together with a simple 8-byte copy loop as a fall-back path.
+ * However, this optimization hurts the decompression speed by >30%,
+ * because the execution does not go to the optimized loop
+ * for typical compressible data, and all of the preamble checks
+ * before going to the fall-back path become useless overhead.
+ * This optimization happens only with the -O3 flag, and -O2 generates
+ * a simple 8-byte copy loop.
+ * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8
+ * functions are annotated with __attribute__((optimize("O2"))),
+ * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute
+ * of LZ4_wildCopy8 does not affect the compression speed.
+ */
+#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)
+# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2")))
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE
+#else
+# define LZ4_FORCE_O2_GCC_PPC64LE
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static
+#endif
+
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
# define expect(expr,value) (__builtin_expect ((expr),(value)) )
#else
# define expect(expr,value) (expr)
#endif
+#ifndef likely
#define likely(expr) expect((expr) != 0, 1)
+#endif
+#ifndef unlikely
#define unlikely(expr) expect((expr) != 0, 0)
+#endif
/*-************************************
* Memory routines
**************************************/
#include <stdlib.h> /* malloc, calloc, free */
-#define ALLOCATOR(n,s) calloc(n,s)
-#define FREEMEM free
+#define ALLOC(s) malloc(s)
+#define ALLOC_AND_ZERO(s) calloc(1,s)
+#define FREEMEM(p) free(p)
#include <string.h> /* memset, memcpy */
-#define MEM_INIT memset
+#define MEM_INIT(p,v,s) memset((p),(v),(s))
/*-************************************
-* Basic Types
+* Common Constants
+**************************************/
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
+#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
+#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */
+#define FASTLOOP_SAFE_DISTANCE 64
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define LZ4_DISTANCE_ABSOLUTE_MAX 65535
+#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */
+# error "LZ4_DISTANCE_MAX is too big : must be <= 65535"
+#endif
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+
+/*-************************************
+* Error detection
+**************************************/
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)
+# include <assert.h>
+#else
+# ifndef assert
+# define assert(condition) ((void)0)
+# endif
+#endif
+
+#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */
+
+#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)
+# include <stdio.h>
+static int g_debuglog_enable = 1;
+# define DEBUGLOG(l, ...) { \
+ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
+ fprintf(stderr, __FILE__ ": "); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, " \n"); \
+ } }
+#else
+# define DEBUGLOG(l, ...) {} /* disabled */
+#endif
+
+
+/*-************************************
+* Types
**************************************/
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# include <stdint.h>
@@ -167,6 +273,13 @@
typedef size_t reg_t; /* 32-bits in x32 mode */
#endif
+typedef enum {
+ notLimited = 0,
+ limitedOutput = 1,
+ fillOutput = 2
+} limitedOutput_directive;
+
+
/*-************************************
* Reading and writing into memory
**************************************/
@@ -200,7 +313,7 @@ static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArc
static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
-#else /* safe and portable access through memcpy() */
+#else /* safe and portable access using memcpy() */
static U16 LZ4_read16(const void* memPtr)
{
@@ -251,55 +364,113 @@ static void LZ4_writeLE16(void* memPtr, U16 value)
}
}
-static void LZ4_copy8(void* dst, const void* src)
-{
- memcpy(dst,src,8);
-}
-
/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
-static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)
{
BYTE* d = (BYTE*)dstPtr;
const BYTE* s = (const BYTE*)srcPtr;
BYTE* const e = (BYTE*)dstEnd;
- do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+ do { memcpy(d,s,8); d+=8; s+=8; } while (d<e);
}
+static const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};
+static const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};
-/*-************************************
-* Common Constants
-**************************************/
-#define MINMATCH 4
-#define WILDCOPYLENGTH 8
-#define LASTLITERALS 5
-#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
-static const int LZ4_minLength = (MFLIMIT+1);
+#ifndef LZ4_FAST_DEC_LOOP
+# if defined(__i386__) || defined(__x86_64__)
+# define LZ4_FAST_DEC_LOOP 1
+# elif defined(__aarch64__) && !defined(__clang__)
+ /* On aarch64, we disable this optimization for clang because on certain
+ * mobile chipsets and clang, it reduces performance. For more information
+ * refer to https://github.com/lz4/lz4/pull/707. */
+# define LZ4_FAST_DEC_LOOP 1
+# else
+# define LZ4_FAST_DEC_LOOP 0
+# endif
+#endif
-#define KB *(1 <<10)
-#define MB *(1 <<20)
-#define GB *(1U<<30)
+#if LZ4_FAST_DEC_LOOP
+
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE void
+LZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
+{
+ if (offset < 8) {
+ dstPtr[0] = srcPtr[0];
+ dstPtr[1] = srcPtr[1];
+ dstPtr[2] = srcPtr[2];
+ dstPtr[3] = srcPtr[3];
+ srcPtr += inc32table[offset];
+ memcpy(dstPtr+4, srcPtr, 4);
+ srcPtr -= dec64table[offset];
+ dstPtr += 8;
+ } else {
+ memcpy(dstPtr, srcPtr, 8);
+ dstPtr += 8;
+ srcPtr += 8;
+ }
-#define MAXD_LOG 16
-#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+ LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);
+}
-#define ML_BITS 4
-#define ML_MASK ((1U<<ML_BITS)-1)
-#define RUN_BITS (8-ML_BITS)
-#define RUN_MASK ((1U<<RUN_BITS)-1)
+/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd
+ * this version copies two times 16 bytes (instead of one time 32 bytes)
+ * because it must be compatible with offsets >= 16. */
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE void
+LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+
+ do { memcpy(d,s,16); memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);
+}
+/* LZ4_memcpy_using_offset() presumes :
+ * - dstEnd >= dstPtr + MINMATCH
+ * - there is at least 8 bytes available to write after dstEnd */
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE void
+LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
+{
+ BYTE v[8];
-/*-************************************
-* Common Utils
-**************************************/
-#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+ assert(dstEnd >= dstPtr + MINMATCH);
+ LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */
+
+ switch(offset) {
+ case 1:
+ memset(v, *srcPtr, 8);
+ break;
+ case 2:
+ memcpy(v, srcPtr, 2);
+ memcpy(&v[2], srcPtr, 2);
+ memcpy(&v[4], &v[0], 4);
+ break;
+ case 4:
+ memcpy(v, srcPtr, 4);
+ memcpy(&v[4], srcPtr, 4);
+ break;
+ default:
+ LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);
+ return;
+ }
+
+ memcpy(dstPtr, v, 8);
+ dstPtr += 8;
+ while (dstPtr < dstEnd) {
+ memcpy(dstPtr, v, 8);
+ dstPtr += 8;
+ }
+}
+#endif
/*-************************************
* Common functions
**************************************/
-static unsigned LZ4_NbCommonBytes (register reg_t val)
+static unsigned LZ4_NbCommonBytes (reg_t val)
{
if (LZ4_isLittleEndian()) {
if (sizeof(val)==8) {
@@ -308,9 +479,16 @@ static unsigned LZ4_NbCommonBytes (register reg_t val)
_BitScanForward64( &r, (U64)val );
return (int)(r>>3);
# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctzll((U64)val) >> 3);
+ return (unsigned)__builtin_ctzll((U64)val) >> 3;
# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
+ 0, 3, 1, 3, 1, 4, 2, 7,
+ 0, 2, 3, 6, 1, 5, 3, 5,
+ 1, 3, 4, 4, 2, 5, 6, 7,
+ 7, 0, 1, 2, 3, 3, 4, 6,
+ 2, 6, 5, 5, 3, 4, 5, 6,
+ 7, 1, 2, 4, 6, 4, 4, 5,
+ 7, 2, 6, 5, 7, 6, 7, 7 };
return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
# endif
} else /* 32 bits */ {
@@ -319,23 +497,29 @@ static unsigned LZ4_NbCommonBytes (register reg_t val)
_BitScanForward( &r, (U32)val );
return (int)(r>>3);
# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctz((U32)val) >> 3);
+ return (unsigned)__builtin_ctz((U32)val) >> 3;
# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
+ 3, 2, 2, 1, 3, 2, 0, 1,
+ 3, 3, 1, 2, 2, 2, 2, 0,
+ 3, 1, 2, 0, 1, 0, 1, 1 };
return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
# endif
}
} else /* Big Endian CPU */ {
- if (sizeof(val)==8) {
+ if (sizeof(val)==8) { /* 64-bits */
# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
unsigned long r = 0;
_BitScanReverse64( &r, val );
return (unsigned)(r>>3);
# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clzll((U64)val) >> 3);
+ return (unsigned)__builtin_clzll((U64)val) >> 3;
# else
+ static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits.
+ Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.
+ Note that this code path is never triggered in 32-bits mode. */
unsigned r;
- if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
+ if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }
if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
r += (!val);
return r;
@@ -346,7 +530,7 @@ static unsigned LZ4_NbCommonBytes (register reg_t val)
_BitScanReverse( &r, (unsigned long)val );
return (unsigned)(r>>3);
# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clz((U32)val) >> 3);
+ return (unsigned)__builtin_clz((U32)val) >> 3;
# else
unsigned r;
if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
@@ -358,11 +542,20 @@ static unsigned LZ4_NbCommonBytes (register reg_t val)
}
#define STEPSIZE sizeof(reg_t)
-static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+LZ4_FORCE_INLINE
+unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
{
const BYTE* const pStart = pIn;
- while (likely(pIn<pInLimit-(STEPSIZE-1))) {
+ if (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) {
+ pIn+=STEPSIZE; pMatch+=STEPSIZE;
+ } else {
+ return LZ4_NbCommonBytes(diff);
+ } }
+
+ while (likely(pIn < pInLimit-(STEPSIZE-1))) {
reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
pIn += LZ4_NbCommonBytes(diff);
@@ -387,15 +580,34 @@ static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression ru
/*-************************************
* Local Structures and types
**************************************/
-typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
-typedef enum { byPtr, byU32, byU16 } tableType_t;
-
-typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
+
+/**
+ * This enum distinguishes several different modes of accessing previous
+ * content in the stream.
+ *
+ * - noDict : There is no preceding content.
+ * - withPrefix64k : Table entries up to ctx->dictSize before the current blob
+ * blob being compressed are valid and refer to the preceding
+ * content (of length ctx->dictSize), which is available
+ * contiguously preceding in memory the content currently
+ * being compressed.
+ * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere
+ * else in memory, starting at ctx->dictionary with length
+ * ctx->dictSize.
+ * - usingDictCtx : Like usingExtDict, but everything concerning the preceding
+ * content is in a separate context, pointed to by
+ * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table
+ * entries in the current context that refer to positions
+ * preceding the beginning of the current compression are
+ * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
+ * ->dictSize describe the location and size of the preceding
+ * content, and matches are found by looking in the ctx
+ * ->dictCtx->hashTable.
+ */
+typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
-typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
-typedef enum { full = 0, partial = 1 } earlyEnd_directive;
-
/*-************************************
* Local Utils
@@ -406,6 +618,23 @@ int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+/*-************************************
+* Internal Definitions used in Tests
+**************************************/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);
+
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
+ int compressedSize, int maxOutputSize,
+ const void* dictStart, size_t dictSize);
+
+#if defined (__cplusplus)
+}
+#endif
+
/*-******************************
* Compression functions
********************************/
@@ -419,102 +648,225 @@ static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
{
- static const U64 prime5bytes = 889523592379ULL;
- static const U64 prime8bytes = 11400714785074694791ULL;
const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
- if (LZ4_isLittleEndian())
+ if (LZ4_isLittleEndian()) {
+ const U64 prime5bytes = 889523592379ULL;
return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
- else
+ } else {
+ const U64 prime8bytes = 11400714785074694791ULL;
return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+ }
}
-FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
{
if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
return LZ4_hash4(LZ4_read32(p), tableType);
}
-static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)
{
switch (tableType)
{
+ default: /* fallthrough */
+ case clearedTable: { /* illegal! */ assert(0); return; }
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }
+ }
+}
+
+static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
+{
+ switch (tableType)
+ {
+ default: /* fallthrough */
+ case clearedTable: /* fallthrough */
+ case byPtr: { /* illegal! */ assert(0); return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }
+ }
+}
+
+static void LZ4_putPositionOnHash(const BYTE* p, U32 h,
+ void* tableBase, tableType_t const tableType,
+ const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case clearedTable: { /* illegal! */ assert(0); return; }
case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
}
}
-FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
{
U32 const h = LZ4_hashPosition(p, tableType);
LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
}
-static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+/* LZ4_getIndexOnHash() :
+ * Index of match position registered in hash table.
+ * hash position must be calculated by using base+index, or dictBase+index.
+ * Assumption 1 : only valid if tableType == byU32 or byU16.
+ * Assumption 2 : h is presumed valid (within limits of hash table)
+ */
+static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
+{
+ LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
+ if (tableType == byU32) {
+ const U32* const hashTable = (const U32*) tableBase;
+ assert(h < (1U << (LZ4_MEMORY_USAGE-2)));
+ return hashTable[h];
+ }
+ if (tableType == byU16) {
+ const U16* const hashTable = (const U16*) tableBase;
+ assert(h < (1U << (LZ4_MEMORY_USAGE-1)));
+ return hashTable[h];
+ }
+ assert(0); return 0; /* forbidden case */
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)
{
- if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
- if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
- { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+ if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
}
-FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+LZ4_FORCE_INLINE const BYTE*
+LZ4_getPosition(const BYTE* p,
+ const void* tableBase, tableType_t tableType,
+ const BYTE* srcBase)
{
U32 const h = LZ4_hashPosition(p, tableType);
return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
}
+LZ4_FORCE_INLINE void
+LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
+ const int inputSize,
+ const tableType_t tableType) {
+ /* If compression failed during the previous step, then the context
+ * is marked as dirty, therefore, it has to be fully reset.
+ */
+ if (cctx->dirty) {
+ DEBUGLOG(5, "LZ4_prepareTable: Full reset for %p", cctx);
+ MEM_INIT(cctx, 0, sizeof(LZ4_stream_t_internal));
+ return;
+ }
+
+ /* If the table hasn't been used, it's guaranteed to be zeroed out, and is
+ * therefore safe to use no matter what mode we're in. Otherwise, we figure
+ * out if it's safe to leave as is or whether it needs to be reset.
+ */
+ if (cctx->tableType != clearedTable) {
+ assert(inputSize >= 0);
+ if (cctx->tableType != tableType
+ || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)
+ || ((tableType == byU32) && cctx->currentOffset > 1 GB)
+ || tableType == byPtr
+ || inputSize >= 4 KB)
+ {
+ DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
+ MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
+ cctx->currentOffset = 0;
+ cctx->tableType = clearedTable;
+ } else {
+ DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
+ }
+ }
+
+ /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster
+ * than compressing without a gap. However, compressing with
+ * currentOffset == 0 is faster still, so we preserve that case.
+ */
+ if (cctx->currentOffset != 0 && tableType == byU32) {
+ DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
+ cctx->currentOffset += 64 KB;
+ }
+
+ /* Finally, clear history */
+ cctx->dictCtx = NULL;
+ cctx->dictionary = NULL;
+ cctx->dictSize = 0;
+}
/** LZ4_compress_generic() :
inlined, to ensure branches are decided at compilation time */
-FORCE_INLINE int LZ4_compress_generic(
+LZ4_FORCE_INLINE int LZ4_compress_generic(
LZ4_stream_t_internal* const cctx,
const char* const source,
char* const dest,
const int inputSize,
+ int *inputConsumed, /* only written when outputDirective == fillOutput */
const int maxOutputSize,
- const limitedOutput_directive outputLimited,
+ const limitedOutput_directive outputDirective,
const tableType_t tableType,
- const dict_directive dict,
+ const dict_directive dictDirective,
const dictIssue_directive dictIssue,
- const U32 acceleration)
+ const int acceleration)
{
+ int result;
const BYTE* ip = (const BYTE*) source;
- const BYTE* base;
+
+ U32 const startIndex = cctx->currentOffset;
+ const BYTE* base = (const BYTE*) source - startIndex;
const BYTE* lowLimit;
- const BYTE* const lowRefLimit = ip - cctx->dictSize;
- const BYTE* const dictionary = cctx->dictionary;
- const BYTE* const dictEnd = dictionary + cctx->dictSize;
- const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source;
+
+ const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
+ const BYTE* const dictionary =
+ dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
+ const U32 dictSize =
+ dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
+ const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */
+
+ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
+ U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
+ const BYTE* const dictEnd = dictionary + dictSize;
const BYTE* anchor = (const BYTE*) source;
const BYTE* const iend = ip + inputSize;
- const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
const BYTE* const matchlimit = iend - LASTLITERALS;
+ /* the dictCtx currentOffset is indexed on the start of the dictionary,
+ * while a dictionary in the current context precedes the currentOffset */
+ const BYTE* dictBase = (dictDirective == usingDictCtx) ?
+ dictionary + dictSize - dictCtx->currentOffset :
+ dictionary + dictSize - startIndex;
+
BYTE* op = (BYTE*) dest;
BYTE* const olimit = op + maxOutputSize;
+ U32 offset = 0;
U32 forwardH;
- /* Init conditions */
- if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
- switch(dict)
- {
- case noDict:
- default:
- base = (const BYTE*)source;
- lowLimit = (const BYTE*)source;
- break;
- case withPrefix64k:
- base = (const BYTE*)source - cctx->currentOffset;
- lowLimit = (const BYTE*)source - cctx->dictSize;
- break;
- case usingExtDict:
- base = (const BYTE*)source - cctx->currentOffset;
- lowLimit = (const BYTE*)source;
- break;
+ DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType);
+ /* If init conditions are not met, we don't have to mark stream
+ * as having dirty context, since no action was taken yet */
+ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported inputSize, too large (or negative) */
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */
+ if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */
+ assert(acceleration >= 1);
+
+ lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
+
+ /* Update context state */
+ if (dictDirective == usingDictCtx) {
+ /* Subsequent linked blocks can't use the dictionary. */
+ /* Instead, they use the block we just compressed. */
+ cctx->dictCtx = NULL;
+ cctx->dictSize = (U32)inputSize;
+ } else {
+ cctx->dictSize += (U32)inputSize;
}
- if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+ cctx->currentOffset += (U32)inputSize;
+ cctx->tableType = (U16)tableType;
+
+ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
/* First Byte */
LZ4_putPosition(ip, cctx->hashTable, tableType, base);
@@ -522,50 +874,112 @@ FORCE_INLINE int LZ4_compress_generic(
/* Main Loop */
for ( ; ; ) {
- ptrdiff_t refDelta = 0;
const BYTE* match;
BYTE* token;
+ const BYTE* filledIp;
/* Find a match */
- { const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ if (tableType == byPtr) {
+ const BYTE* forwardIp = ip;
+ int step = 1;
+ int searchMatchNb = acceleration << LZ4_skipTrigger;
do {
U32 const h = forwardH;
ip = forwardIp;
forwardIp += step;
step = (searchMatchNb++ >> LZ4_skipTrigger);
- if (unlikely(forwardIp > mflimit)) goto _last_literals;
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+ assert(ip < mflimitPlusOne);
match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
- if (dict==usingExtDict) {
- if (match < (const BYTE*)source) {
- refDelta = dictDelta;
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( (match+LZ4_DISTANCE_MAX < ip)
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+
+ } else { /* byU32, byU16 */
+
+ const BYTE* forwardIp = ip;
+ int step = 1;
+ int searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ U32 const current = (U32)(forwardIp - base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ assert(matchIndex <= current);
+ assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+ assert(ip < mflimitPlusOne);
+
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ assert(tableType == byU32);
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ matchIndex += dictDelta; /* make dictCtx index comparable with current context */
lowLimit = dictionary;
} else {
- refDelta = 0;
+ match = base + matchIndex;
lowLimit = (const BYTE*)source;
- } }
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);
+ assert(startIndex - matchIndex >= MINMATCH);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source;
+ }
+ } else { /* single continuous memory segment */
+ match = base + matchIndex;
+ }
forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+
+ DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex);
+ if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */
+ assert(matchIndex < current);
+ if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))
+ && (matchIndex+LZ4_DISTANCE_MAX < current)) {
+ continue;
+ } /* too far */
+ assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */
+
+ if (LZ4_read32(match) == LZ4_read32(ip)) {
+ if (maybe_extMem) offset = current - matchIndex;
+ break; /* match found */
+ }
- } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
- || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
+ } while(1);
}
/* Catch up */
- while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
+ filledIp = ip;
+ while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
/* Encode Literals */
{ unsigned const litLength = (unsigned)(ip - anchor);
token = op++;
- if ((outputLimited) && /* Check output buffer overflow */
- (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
- return 0;
+ if ((outputDirective == limitedOutput) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {
+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+ }
+ if ((outputDirective == fillOutput) &&
+ (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {
+ op--;
+ goto _last_literals;
+ }
if (litLength >= RUN_MASK) {
- int len = (int)litLength-RUN_MASK;
+ int len = (int)(litLength - RUN_MASK);
*token = (RUN_MASK<<ML_BITS);
for(; len >= 255 ; len-=255) *op++ = 255;
*op++ = (BYTE)len;
@@ -573,82 +987,183 @@ FORCE_INLINE int LZ4_compress_generic(
else *token = (BYTE)(litLength<<ML_BITS);
/* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
+ LZ4_wildCopy8(op, anchor, op+litLength);
op+=litLength;
+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
+ (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));
}
_next_match:
+ /* at this stage, the following variables must be correctly set :
+ * - ip : at start of LZ operation
+ * - match : at start of previous pattern occurence; can be within current prefix, or within extDict
+ * - offset : if maybe_ext_memSegment==1 (constant)
+ * - lowLimit : must be == dictionary to mean "match is within extDict"; must be == source otherwise
+ * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written
+ */
+
+ if ((outputDirective == fillOutput) &&
+ (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {
+ /* the match was too close to the end, rewind and go to last literals */
+ op = token;
+ goto _last_literals;
+ }
+
/* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+ if (maybe_extMem) { /* static test */
+ DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source));
+ assert(offset <= LZ4_DISTANCE_MAX && offset > 0);
+ LZ4_writeLE16(op, (U16)offset); op+=2;
+ } else {
+ DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match));
+ assert(ip-match <= LZ4_DISTANCE_MAX);
+ LZ4_writeLE16(op, (U16)(ip - match)); op+=2;
+ }
/* Encode MatchLength */
{ unsigned matchCode;
- if ((dict==usingExtDict) && (lowLimit==dictionary)) {
- const BYTE* limit;
- match += refDelta;
- limit = ip + (dictEnd-match);
+ if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)
+ && (lowLimit==dictionary) /* match within extDict */ ) {
+ const BYTE* limit = ip + (dictEnd-match);
+ assert(dictEnd > match);
if (limit > matchlimit) limit = matchlimit;
matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
- ip += MINMATCH + matchCode;
+ ip += (size_t)matchCode + MINMATCH;
if (ip==limit) {
- unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit);
+ unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
matchCode += more;
ip += more;
}
+ DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);
} else {
matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
- ip += MINMATCH + matchCode;
+ ip += (size_t)matchCode + MINMATCH;
+ DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);
}
- if ( outputLimited && /* Check output buffer overflow */
- (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
- return 0;
+ if ((outputDirective) && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {
+ if (outputDirective == fillOutput) {
+ /* Match description too long : reduce it */
+ U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;
+ ip -= matchCode - newMatchCode;
+ assert(newMatchCode < matchCode);
+ matchCode = newMatchCode;
+ if (unlikely(ip <= filledIp)) {
+ /* We have already filled up to filledIp so if ip ends up less than filledIp
+ * we have positions in the hash table beyond the current position. This is
+ * a problem if we reuse the hash table. So we have to remove these positions
+ * from the hash table.
+ */
+ const BYTE* ptr;
+ DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));
+ for (ptr = ip; ptr <= filledIp; ++ptr) {
+ U32 const h = LZ4_hashPosition(ptr, tableType);
+ LZ4_clearHash(h, cctx->hashTable, tableType);
+ }
+ }
+ } else {
+ assert(outputDirective == limitedOutput);
+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+ }
+ }
if (matchCode >= ML_MASK) {
*token += ML_MASK;
matchCode -= ML_MASK;
LZ4_write32(op, 0xFFFFFFFF);
- while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255;
+ while (matchCode >= 4*255) {
+ op+=4;
+ LZ4_write32(op, 0xFFFFFFFF);
+ matchCode -= 4*255;
+ }
op += matchCode / 255;
*op++ = (BYTE)(matchCode % 255);
} else
*token += (BYTE)(matchCode);
}
+ /* Ensure we have enough space for the last literals. */
+ assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));
anchor = ip;
/* Test end of chunk */
- if (ip > mflimit) break;
+ if (ip >= mflimitPlusOne) break;
/* Fill table */
LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
/* Test next position */
- match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
- if (dict==usingExtDict) {
- if (match < (const BYTE*)source) {
- refDelta = dictDelta;
- lowLimit = dictionary;
- } else {
- refDelta = 0;
- lowLimit = (const BYTE*)source;
- } }
- LZ4_putPosition(ip, cctx->hashTable, tableType, base);
- if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
- && (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
+ if (tableType == byPtr) {
+
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( (match+LZ4_DISTANCE_MAX >= ip)
+ && (LZ4_read32(match) == LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ } else { /* byU32, byU16 */
+
+ U32 const h = LZ4_hashPosition(ip, tableType);
+ U32 const current = (U32)(ip-base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ assert(matchIndex < current);
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ matchIndex += dictDelta;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else { /* single memory segment */
+ match = base + matchIndex;
+ }
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+ assert(matchIndex < current);
+ if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
+ && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))
+ && (LZ4_read32(match) == LZ4_read32(ip)) ) {
+ token=op++;
+ *token=0;
+ if (maybe_extMem) offset = current - matchIndex;
+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
+ (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
+ goto _next_match;
+ }
+ }
/* Prepare next loop */
forwardH = LZ4_hashPosition(++ip, tableType);
+
}
_last_literals:
/* Encode Last Literals */
- { size_t const lastRun = (size_t)(iend - anchor);
- if ( (outputLimited) && /* Check output buffer overflow */
- ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
- return 0;
+ { size_t lastRun = (size_t)(iend - anchor);
+ if ( (outputDirective) && /* Check output buffer overflow */
+ (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {
+ if (outputDirective == fillOutput) {
+ /* adapt lastRun to fill 'dst' */
+ assert(olimit >= op);
+ lastRun = (size_t)(olimit-op) - 1;
+ lastRun -= (lastRun+240)/255;
+ } else {
+ assert(outputDirective == limitedOutput);
+ return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
+ }
+ }
if (lastRun >= RUN_MASK) {
size_t accumulator = lastRun - RUN_MASK;
*op++ = RUN_MASK << ML_BITS;
@@ -658,251 +1173,154 @@ _last_literals:
*op++ = (BYTE)(lastRun<<ML_BITS);
}
memcpy(op, anchor, lastRun);
+ ip = anchor + lastRun;
op += lastRun;
}
- /* End */
- return (int) (((char*)op)-dest);
+ if (outputDirective == fillOutput) {
+ *inputConsumed = (int) (((const char*)ip)-source);
+ }
+ DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, (int)(((char*)op) - dest));
+ result = (int)(((char*)op) - dest);
+ assert(result > 0);
+ return result;
}
int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
{
+ LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;
+ assert(ctx != NULL);
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit) {
+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ if (inputSize < LZ4_64Klimit) {
+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ }
+}
+
+/**
+ * LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
+ * "correctly initialized").
+ */
+int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
+{
LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
- LZ4_resetStream((LZ4_stream_t*)state);
if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
- if (maxOutputSize >= LZ4_compressBound(inputSize)) {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ if (dstCapacity >= LZ4_compressBound(srcSize)) {
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
} else {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
}
}
int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
{
-#if (HEAPMODE)
- void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+ int result;
+#if (LZ4_HEAPMODE)
+ LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+ if (ctxPtr == NULL) return 0;
#else
LZ4_stream_t ctx;
- void* const ctxPtr = &ctx;
+ LZ4_stream_t* const ctxPtr = &ctx;
#endif
+ result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
- int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
-
-#if (HEAPMODE)
+#if (LZ4_HEAPMODE)
FREEMEM(ctxPtr);
#endif
return result;
}
-int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
+int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)
{
- return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+ return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);
}
/* hidden debug function */
/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
-int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+int LZ4_compress_fast_force(const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
{
LZ4_stream_t ctx;
- LZ4_resetStream(&ctx);
-
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration);
-}
-
-
-/*-******************************
-* *_destSize() variant
-********************************/
+ LZ4_initStream(&ctx, sizeof(ctx));
-static int LZ4_compress_destSize_generic(
- LZ4_stream_t_internal* const ctx,
- const char* const src,
- char* const dst,
- int* const srcSizePtr,
- const int targetDstSize,
- const tableType_t tableType)
-{
- const BYTE* ip = (const BYTE*) src;
- const BYTE* base = (const BYTE*) src;
- const BYTE* lowLimit = (const BYTE*) src;
- const BYTE* anchor = ip;
- const BYTE* const iend = ip + *srcSizePtr;
- const BYTE* const mflimit = iend - MFLIMIT;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- BYTE* op = (BYTE*) dst;
- BYTE* const oend = op + targetDstSize;
- BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
- BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
- BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
-
- U32 forwardH;
-
-
- /* Init conditions */
- if (targetDstSize < 1) return 0; /* Impossible to store anything */
- if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
- if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
-
- /* First Byte */
- *srcSizePtr = 0;
- LZ4_putPosition(ip, ctx->hashTable, tableType, base);
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; ) {
- const BYTE* match;
- BYTE* token;
-
- /* Find a match */
- { const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = 1 << LZ4_skipTrigger;
-
- do {
- U32 h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimit)) goto _last_literals;
-
- match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base);
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base);
-
- } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match) != LZ4_read32(ip)) );
- }
-
- /* Catch up */
- while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
-
- /* Encode Literal length */
- { unsigned litLength = (unsigned)(ip - anchor);
- token = op++;
- if (op + ((litLength+240)/255) + litLength > oMaxLit) {
- /* Not enough space for a last match */
- op--;
- goto _last_literals;
- }
- if (litLength>=RUN_MASK) {
- unsigned len = litLength - RUN_MASK;
- *token=(RUN_MASK<<ML_BITS);
- for(; len >= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength<<ML_BITS);
-
- /* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
- op += litLength;
- }
-
-_next_match:
- /* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
-
- /* Encode MatchLength */
- { size_t matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
-
- if (op + ((matchLength+240)/255) > oMaxMatch) {
- /* Match description too long : reduce it */
- matchLength = (15-1) + (oMaxMatch-op) * 255;
- }
- ip += MINMATCH + matchLength;
-
- if (matchLength>=ML_MASK) {
- *token += ML_MASK;
- matchLength -= ML_MASK;
- while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
- *op++ = (BYTE)matchLength;
- }
- else *token += (BYTE)(matchLength);
- }
-
- anchor = ip;
-
- /* Test end of block */
- if (ip > mflimit) break;
- if (op > oMaxSeq) break;
-
- /* Fill table */
- LZ4_putPosition(ip-2, ctx->hashTable, tableType, base);
-
- /* Test next position */
- match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
- LZ4_putPosition(ip, ctx->hashTable, tableType, base);
- if ( (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
- }
-
-_last_literals:
- /* Encode Last Literals */
- { size_t lastRunSize = (size_t)(iend - anchor);
- if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) {
- /* adapt lastRunSize to fill 'dst' */
- lastRunSize = (oend-op) - 1;
- lastRunSize -= (lastRunSize+240)/255;
- }
- ip = anchor + lastRunSize;
-
- if (lastRunSize >= RUN_MASK) {
- size_t accumulator = lastRunSize - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- } else {
- *op++ = (BYTE)(lastRunSize<<ML_BITS);
- }
- memcpy(op, anchor, lastRunSize);
- op += lastRunSize;
+ if (srcSize < LZ4_64Klimit) {
+ return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ tableType_t const addrMode = (sizeof(void*) > 4) ? byU32 : byPtr;
+ return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, addrMode, noDict, noDictIssue, acceleration);
}
-
- /* End */
- *srcSizePtr = (int) (((const char*)ip)-src);
- return (int) (((char*)op)-dst);
}
+/* Note!: This function leaves the stream in an unclean/broken state!
+ * It is not safe to subsequently use the same state with a _fastReset() or
+ * _continue() call without resetting it. */
static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
{
- LZ4_resetStream(state);
+ void* const s = LZ4_initStream(state, sizeof (*state));
+ assert(s != NULL); (void)s;
if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
} else {
- if (*srcSizePtr < LZ4_64Klimit)
- return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16);
- else
- return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, sizeof(void*)==8 ? byU32 : byPtr);
- }
+ if (*srcSizePtr < LZ4_64Klimit) {
+ return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);
+ } else {
+ tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
+ return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);
+ } }
}
int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
{
-#if (HEAPMODE)
- LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#if (LZ4_HEAPMODE)
+ LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+ if (ctx == NULL) return 0;
#else
LZ4_stream_t ctxBody;
LZ4_stream_t* ctx = &ctxBody;
@@ -910,7 +1328,7 @@ int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targe
int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
-#if (HEAPMODE)
+#if (LZ4_HEAPMODE)
FREEMEM(ctx);
#endif
return result;
@@ -924,19 +1342,54 @@ int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targe
LZ4_stream_t* LZ4_createStream(void)
{
- LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
+ LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
- LZ4_resetStream(lz4s);
+ DEBUGLOG(4, "LZ4_createStream %p", lz4s);
+ if (lz4s == NULL) return NULL;
+ LZ4_initStream(lz4s, sizeof(*lz4s));
return lz4s;
}
+#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :
+ it reports an aligment of 8-bytes,
+ while actually aligning LZ4_stream_t on 4 bytes. */
+static size_t LZ4_stream_t_alignment(void)
+{
+ struct { char c; LZ4_stream_t t; } t_a;
+ return sizeof(t_a) - sizeof(t_a.t);
+}
+#endif
+
+LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)
+{
+ DEBUGLOG(5, "LZ4_initStream");
+ if (buffer == NULL) { return NULL; }
+ if (size < sizeof(LZ4_stream_t)) { return NULL; }
+#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 :
+ it reports an aligment of 8-bytes,
+ while actually aligning LZ4_stream_t on 4 bytes. */
+ if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */
+#endif
+ MEM_INIT(buffer, 0, sizeof(LZ4_stream_t));
+ return (LZ4_stream_t*)buffer;
+}
+
+/* resetStream is now deprecated,
+ * prefer initStream() which is more general */
void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
{
+ DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
}
+void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
+ LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
+}
+
int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
{
+ if (!LZ4_stream) return 0; /* support free on NULL */
+ DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
FREEMEM(LZ4_stream);
return (0);
}
@@ -946,43 +1399,88 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
{
LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;
+ const tableType_t tableType = byU32;
const BYTE* p = (const BYTE*)dictionary;
const BYTE* const dictEnd = p + dictSize;
const BYTE* base;
- if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */
- LZ4_resetStream(LZ4_dict);
+ DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
+
+ /* It's necessary to reset the context,
+ * and not just continue it with prepareTable()
+ * to avoid any risk of generating overflowing matchIndex
+ * when compressing using this dictionary */
+ LZ4_resetStream(LZ4_dict);
+
+ /* We always increment the offset by 64 KB, since, if the dict is longer,
+ * we truncate it to the last 64k, and if it's shorter, we still want to
+ * advance by a whole window length so we can provide the guarantee that
+ * there are only valid offsets in the window, which allows an optimization
+ * in LZ4_compress_fast_continue() where it uses noDictIssue even when the
+ * dictionary isn't a full 64k. */
+ dict->currentOffset += 64 KB;
if (dictSize < (int)HASH_UNIT) {
- dict->dictionary = NULL;
- dict->dictSize = 0;
return 0;
}
if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
- dict->currentOffset += 64 KB;
- base = p - dict->currentOffset;
+ base = dictEnd - dict->currentOffset;
dict->dictionary = p;
dict->dictSize = (U32)(dictEnd - p);
- dict->currentOffset += dict->dictSize;
+ dict->tableType = tableType;
while (p <= dictEnd-HASH_UNIT) {
- LZ4_putPosition(p, dict->hashTable, byU32, base);
+ LZ4_putPosition(p, dict->hashTable, tableType, base);
p+=3;
}
- return dict->dictSize;
+ return (int)dict->dictSize;
}
+void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) {
+ const LZ4_stream_t_internal* dictCtx = dictionaryStream == NULL ? NULL :
+ &(dictionaryStream->internal_donotuse);
+
+ DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",
+ workingStream, dictionaryStream,
+ dictCtx != NULL ? dictCtx->dictSize : 0);
+
+ /* Calling LZ4_resetStream_fast() here makes sure that changes will not be
+ * erased by subsequent calls to LZ4_resetStream_fast() in case stream was
+ * marked as having dirty context, e.g. requiring full reset.
+ */
+ LZ4_resetStream_fast(workingStream);
+
+ if (dictCtx != NULL) {
+ /* If the current offset is zero, we will never look in the
+ * external dictionary context, since there is no value a table
+ * entry can take that indicate a miss. In that case, we need
+ * to bump the offset to something non-zero.
+ */
+ if (workingStream->internal_donotuse.currentOffset == 0) {
+ workingStream->internal_donotuse.currentOffset = 64 KB;
+ }
-static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
+ /* Don't actually attach an empty dictionary.
+ */
+ if (dictCtx->dictSize == 0) {
+ dictCtx = NULL;
+ }
+ }
+ workingStream->internal_donotuse.dictCtx = dictCtx;
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
{
- if ((LZ4_dict->currentOffset > 0x80000000) ||
- ((uptrval)LZ4_dict->currentOffset > (uptrval)src)) { /* address space overflow */
+ assert(nextSize >= 0);
+ if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */
/* rescale hash table */
U32 const delta = LZ4_dict->currentOffset - 64 KB;
const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
int i;
+ DEBUGLOG(4, "LZ4_renormDictT");
for (i=0; i<LZ4_HASH_SIZE_U32; i++) {
if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
else LZ4_dict->hashTable[i] -= delta;
@@ -994,17 +1492,30 @@ static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
}
-int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
+ const char* source, char* dest,
+ int inputSize, int maxOutputSize,
+ int acceleration)
{
+ const tableType_t tableType = byU32;
LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+ const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize;
- const BYTE* smallest = (const BYTE*) source;
- if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
- if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd;
- LZ4_renormDictT(streamPtr, smallest);
+ DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize);
+
+ if (streamPtr->dirty) { return 0; } /* Uninitialized structure detected */
+ LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */
if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+ /* invalidate tiny dictionaries */
+ if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */
+ && (dictEnd != (const BYTE*)source) ) {
+ DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);
+ streamPtr->dictSize = 0;
+ streamPtr->dictionary = (const BYTE*)source;
+ dictEnd = (const BYTE*)source;
+ }
+
/* Check overlapping input/dictionary space */
{ const BYTE* sourceEnd = (const BYTE*) source + inputSize;
if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
@@ -1017,46 +1528,61 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, ch
/* prefix mode : source data follows dictionary */
if (dictEnd == (const BYTE*)source) {
- int result;
if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration);
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
else
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration);
- streamPtr->dictSize += (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
- return result;
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
}
/* external dictionary mode */
{ int result;
- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration);
- else
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration);
+ if (streamPtr->dictCtx) {
+ /* We depend here on the fact that dictCtx'es (produced by
+ * LZ4_loadDict) guarantee that their tables contain no references
+ * to offsets between dictCtx->currentOffset - 64 KB and
+ * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
+ * to use noDictIssue even when the dict isn't a full 64 KB.
+ */
+ if (inputSize > 4 KB) {
+ /* For compressing large blobs, it is faster to pay the setup
+ * cost to copy the dictionary's tables into the active context,
+ * so that the compression loop is only looking into one table.
+ */
+ memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t));
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
+ }
+ } else {
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ }
+ }
streamPtr->dictionary = (const BYTE*)source;
streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
return result;
}
}
-/* Hidden debug function, to force external dictionary mode */
-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize)
+/* Hidden debug function, to force-test external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
{
LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;
int result;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
- const BYTE* smallest = dictEnd;
- if (smallest > (const BYTE*) source) smallest = (const BYTE*) source;
- LZ4_renormDictT(streamPtr, smallest);
+ LZ4_renormDictT(streamPtr, srcSize);
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+ }
streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
+ streamPtr->dictSize = (U32)srcSize;
return result;
}
@@ -1074,8 +1600,8 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
- if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
- if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
+ if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */
+ if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }
memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
@@ -1087,218 +1613,587 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
-/*-*****************************
-* Decompression functions
-*******************************/
+/*-*******************************
+ * Decompression functions
+ ********************************/
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
+
+#undef MIN
+#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
+
+/* Read the variable-length literal or match length.
+ *
+ * ip - pointer to use as input.
+ * lencheck - end ip. Return an error if ip advances >= lencheck.
+ * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so.
+ * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so.
+ * error (output) - error code. Should be set to 0 before call.
+ */
+typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error;
+LZ4_FORCE_INLINE unsigned
+read_variable_length(const BYTE**ip, const BYTE* lencheck, int loop_check, int initial_check, variable_length_error* error)
+{
+ unsigned length = 0;
+ unsigned s;
+ if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */
+ *error = initial_error;
+ return length;
+ }
+ do {
+ s = **ip;
+ (*ip)++;
+ length += s;
+ if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */
+ *error = loop_error;
+ return length;
+ }
+ } while (s==255);
+
+ return length;
+}
+
/*! LZ4_decompress_generic() :
- * This generic decompression function cover all use cases.
- * It shall be instantiated several times, using different sets of directives
- * Note that it is important this generic function is really inlined,
+ * This generic decompression function covers all use cases.
+ * It shall be instantiated several times, using different sets of directives.
+ * Note that it is important for performance that this function really get inlined,
* in order to remove useless branches during compilation optimization.
*/
-FORCE_INLINE int LZ4_decompress_generic(
- const char* const source,
- char* const dest,
- int inputSize,
- int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
-
- int endOnInput, /* endOnOutputSize, endOnInputSize */
- int partialDecoding, /* full, partial */
- int targetOutputSize, /* only used if partialDecoding==partial */
- int dict, /* noDict, withPrefix64k, usingExtDict */
- const BYTE* const lowPrefix, /* == dest when no prefix */
+LZ4_FORCE_INLINE int
+LZ4_decompress_generic(
+ const char* const src,
+ char* const dst,
+ int srcSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
+
+ endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */
+ earlyEnd_directive partialDecoding, /* full, partial */
+ dict_directive dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
const BYTE* const dictStart, /* only if dict==usingExtDict */
const size_t dictSize /* note : = 0 if noDict */
)
{
- /* Local Variables */
- const BYTE* ip = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
+ if (src == NULL) { return -1; }
- BYTE* op = (BYTE*) dest;
- BYTE* const oend = op + outputSize;
- BYTE* cpy;
- BYTE* oexit = op + targetOutputSize;
- const BYTE* const lowLimit = lowPrefix - dictSize;
+ { const BYTE* ip = (const BYTE*) src;
+ const BYTE* const iend = ip + srcSize;
- const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
- const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
- const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
- const int safeDecode = (endOnInput==endOnInputSize);
- const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+ const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
- /* Special cases */
- if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
- if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
- if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
- /* Main Loop : decode sequences */
- while (1) {
- size_t length;
+ /* Set up the "end" pointers for the shortcut. */
+ const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/;
+ const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/;
+
const BYTE* match;
size_t offset;
+ unsigned token;
+ size_t length;
- /* get literal length */
- unsigned const token = *ip++;
- if ((length=(token>>ML_BITS)) == RUN_MASK) {
- unsigned s;
- do {
- s = *ip++;
- length += s;
- } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) );
- if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error; /* overflow detection */
- if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error; /* overflow detection */
+
+ DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);
+
+ /* Special cases */
+ assert(lowPrefix <= op);
+ if ((endOnInput) && (unlikely(outputSize==0))) {
+ /* Empty output buffer */
+ if (partialDecoding) return 0;
+ return ((srcSize==1) && (*ip==0)) ? 0 : -1;
+ }
+ if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); }
+ if ((endOnInput) && unlikely(srcSize==0)) { return -1; }
+
+ /* Currently the fast loop shows a regression on qualcomm arm chips. */
+#if LZ4_FAST_DEC_LOOP
+ if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {
+ DEBUGLOG(6, "skip fast decode loop");
+ goto safe_decode;
}
- /* copy literals */
- cpy = op+length;
- if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
- || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
- {
- if (partialDecoding) {
- if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
- if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */
+ while (1) {
+ /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */
+ assert(oend - op >= FASTLOOP_SAFE_DISTANCE);
+ if (endOnInput) { assert(ip < iend); }
+ token = *ip++;
+ length = token >> ML_BITS; /* literal length */
+
+ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */
+
+ /* decode literal length */
+ if (length == RUN_MASK) {
+ variable_length_error error = ok;
+ length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error);
+ if (error == initial_error) { goto _output_error; }
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
+ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
+
+ /* copy literals */
+ cpy = op+length;
+ LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
+ if (endOnInput) { /* LZ4_decompress_safe() */
+ if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }
+ LZ4_wildCopy32(op, ip, cpy);
+ } else { /* LZ4_decompress_fast() */
+ if (cpy>oend-8) { goto safe_literal_copy; }
+ LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time :
+ * it doesn't know input length, and only relies on end-of-block properties */
+ }
+ ip += length; op = cpy;
} else {
- if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
- if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ cpy = op+length;
+ if (endOnInput) { /* LZ4_decompress_safe() */
+ DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
+ /* We don't need to check oend, since we check it once for each loop below */
+ if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }
+ /* Literals can only be 14, but hope compilers optimize if we copy by a register size */
+ memcpy(op, ip, 16);
+ } else { /* LZ4_decompress_fast() */
+ /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time :
+ * it doesn't know input length, and relies on end-of-block properties */
+ memcpy(op, ip, 8);
+ if (length > 8) { memcpy(op+8, ip+8, 8); }
+ }
+ ip += length; op = cpy;
}
- memcpy(op, ip, length);
- ip += length;
- op += length;
- break; /* Necessarily EOF, due to parsing restrictions */
- }
- LZ4_wildCopy(op, ip, cpy);
- ip += length; op = cpy;
-
- /* get offset */
- offset = LZ4_readLE16(ip); ip+=2;
- match = op - offset;
- if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */
- LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
-
- /* get matchlength */
- length = token & ML_MASK;
- if (length == ML_MASK) {
- unsigned s;
- do {
- s = *ip++;
- if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
- length += s;
- } while (s==255);
- if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ assert(match <= op);
+
+ /* get matchlength */
+ length = token & ML_MASK;
+
+ if (length == ML_MASK) {
+ variable_length_error error = ok;
+ if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */
+ length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error);
+ if (error != ok) { goto _output_error; }
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */
+ length += MINMATCH;
+ if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
+ goto safe_match_copy;
+ }
+ } else {
+ length += MINMATCH;
+ if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
+ goto safe_match_copy;
+ }
+
+ /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */
+ if ((dict == withPrefix64k) || (match >= lowPrefix)) {
+ if (offset >= 8) {
+ assert(match >= lowPrefix);
+ assert(match <= op);
+ assert(op + 18 <= oend);
+
+ memcpy(op, match, 8);
+ memcpy(op+8, match+8, 8);
+ memcpy(op+16, match+16, 2);
+ op += length;
+ continue;
+ } } }
+
+ if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */
+ /* match starting within external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) {
+ if (partialDecoding) {
+ length = MIN(length, (size_t)(oend-op)); /* reach end of buffer */
+ } else {
+ goto _output_error; /* end-of-block condition violated */
+ } }
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match fits entirely within external dictionary : just copy */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match stretches into both external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix - match);
+ size_t const restSize = length - copySize;
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) { *op++ = *copyFrom++; }
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+
+ assert((op <= oend) && (oend-op >= 32));
+ if (unlikely(offset<16)) {
+ LZ4_memcpy_using_offset(op, match, cpy, offset);
+ } else {
+ LZ4_wildCopy32(op, match, cpy);
+ }
+
+ op = cpy; /* wildcopy correction */
}
- length += MINMATCH;
+ safe_decode:
+#endif
+
+ /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */
+ while (1) {
+ token = *ip++;
+ length = token >> ML_BITS; /* literal length */
+
+ assert(!endOnInput || ip <= iend); /* ip < iend before the increment */
+
+ /* A two-stage shortcut for the most common case:
+ * 1) If the literal length is 0..14, and there is enough space,
+ * enter the shortcut and copy 16 bytes on behalf of the literals
+ * (in the fast mode, only 8 bytes can be safely copied this way).
+ * 2) Further if the match length is 4..18, copy 18 bytes in a similar
+ * manner; but we ensure that there's enough space in the output for
+ * those 18 bytes earlier, upon entering the shortcut (in other words,
+ * there is a combined check for both stages).
+ */
+ if ( (endOnInput ? length != RUN_MASK : length <= 8)
+ /* strictly "less than" on input, to re-enter the loop with at least one byte */
+ && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) {
+ /* Copy the literals */
+ memcpy(op, ip, endOnInput ? 16 : 8);
+ op += length; ip += length;
+
+ /* The second stage: prepare for match copying, decode full info.
+ * If it doesn't work out, the info won't be wasted. */
+ length = token & ML_MASK; /* match length */
+ offset = LZ4_readLE16(ip); ip += 2;
+ match = op - offset;
+ assert(match <= op); /* check overflow */
+
+ /* Do not deal with overlapping matches. */
+ if ( (length != ML_MASK)
+ && (offset >= 8)
+ && (dict==withPrefix64k || match >= lowPrefix) ) {
+ /* Copy the match. */
+ memcpy(op + 0, match + 0, 8);
+ memcpy(op + 8, match + 8, 8);
+ memcpy(op +16, match +16, 2);
+ op += length + MINMATCH;
+ /* Both stages worked, load the next token. */
+ continue;
+ }
+
+ /* The second stage didn't work out, but the info is ready.
+ * Propel it right to the point of match copying. */
+ goto _copy_match;
+ }
- /* check external dictionary */
- if ((dict==usingExtDict) && (match < lowPrefix)) {
- if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+ /* decode literal length */
+ if (length == RUN_MASK) {
+ variable_length_error error = ok;
+ length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error);
+ if (error == initial_error) { goto _output_error; }
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
+ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
+ }
- if (length <= (size_t)(lowPrefix-match)) {
- /* match can be copied as a single segment from external dictionary */
- memmove(op, dictEnd - (lowPrefix-match), length);
+ /* copy literals */
+ cpy = op+length;
+#if LZ4_FAST_DEC_LOOP
+ safe_literal_copy:
+#endif
+ LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
+ if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ /* We've either hit the input parsing restriction or the output parsing restriction.
+ * If we've hit the input parsing condition then this must be the last sequence.
+ * If we've hit the output parsing condition then we are either using partialDecoding
+ * or we've hit the output parsing condition.
+ */
+ if (partialDecoding) {
+ /* Since we are partial decoding we may be in this block because of the output parsing
+ * restriction, which is not valid since the output buffer is allowed to be undersized.
+ */
+ assert(endOnInput);
+ /* If we're in this block because of the input parsing condition, then we must be on the
+ * last sequence (or invalid), so we must check that we exactly consume the input.
+ */
+ if ((ip+length>iend-(2+1+LASTLITERALS)) && (ip+length != iend)) { goto _output_error; }
+ assert(ip+length <= iend);
+ /* We are finishing in the middle of a literals segment.
+ * Break after the copy.
+ */
+ if (cpy > oend) {
+ cpy = oend;
+ assert(op<=oend);
+ length = (size_t)(oend-op);
+ }
+ assert(ip+length <= iend);
+ } else {
+ /* We must be on the last sequence because of the parsing limitations so check
+ * that we exactly regenerate the original size (must be exact when !endOnInput).
+ */
+ if ((!endOnInput) && (cpy != oend)) { goto _output_error; }
+ /* We must be on the last sequence (or invalid) because of the parsing limitations
+ * so check that we exactly consume the input and don't overrun the output buffer.
+ */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { goto _output_error; }
+ }
+ memmove(op, ip, length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */
+ ip += length;
op += length;
+ /* Necessarily EOF when !partialDecoding. When partialDecoding
+ * it is EOF if we've either filled the output buffer or hit
+ * the input parsing restriction.
+ */
+ if (!partialDecoding || (cpy == oend) || (ip == iend)) {
+ break;
+ }
} else {
- /* match encompass external dictionary and current block */
- size_t const copySize = (size_t)(lowPrefix-match);
- size_t const restSize = length - copySize;
- memcpy(op, dictEnd - copySize, copySize);
- op += copySize;
- if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
- BYTE* const endOfMatch = op + restSize;
- const BYTE* copyFrom = lowPrefix;
- while (op < endOfMatch) *op++ = *copyFrom++;
+ LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */
+ ip += length; op = cpy;
+ }
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+
+ /* get matchlength */
+ length = token & ML_MASK;
+
+ _copy_match:
+ if (length == ML_MASK) {
+ variable_length_error error = ok;
+ length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error);
+ if (error != ok) goto _output_error;
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+#if LZ4_FAST_DEC_LOOP
+ safe_match_copy:
+#endif
+ if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */
+ /* match starting within external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) {
+ if (partialDecoding) length = MIN(length, (size_t)(oend-op));
+ else goto _output_error; /* doesn't respect parsing restriction */
+ }
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match fits entirely within external dictionary : just copy */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
} else {
- memcpy(op, lowPrefix, restSize);
- op += restSize;
- } }
- continue;
- }
+ /* match stretches into both external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix - match);
+ size_t const restSize = length - copySize;
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+ assert(match >= lowPrefix);
+
+ /* copy match within block */
+ cpy = op + length;
+
+ /* partialDecoding : may end anywhere within the block */
+ assert(op<=oend);
+ if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
+ size_t const mlen = MIN(length, (size_t)(oend-op));
+ const BYTE* const matchEnd = match + mlen;
+ BYTE* const copyEnd = op + mlen;
+ if (matchEnd > op) { /* overlap copy */
+ while (op < copyEnd) { *op++ = *match++; }
+ } else {
+ memcpy(op, match, mlen);
+ }
+ op = copyEnd;
+ if (op == oend) { break; }
+ continue;
+ }
- /* copy match within block */
- cpy = op + length;
- if (unlikely(offset<8)) {
- const int dec64 = dec64table[offset];
- op[0] = match[0];
- op[1] = match[1];
- op[2] = match[2];
- op[3] = match[3];
- match += dec32table[offset];
- memcpy(op+4, match, 4);
- match -= dec64;
- } else { LZ4_copy8(op, match); match+=8; }
- op += 8;
-
- if (unlikely(cpy>oend-12)) {
- BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
- if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
- if (op < oCopyLimit) {
- LZ4_wildCopy(op, match, oCopyLimit);
- match += oCopyLimit - op;
- op = oCopyLimit;
+ if (unlikely(offset<8)) {
+ LZ4_write32(op, 0); /* silence msan warning when offset==0 */
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += inc32table[offset];
+ memcpy(op+4, match, 4);
+ match -= dec64table[offset];
+ } else {
+ memcpy(op, match, 8);
+ match += 8;
}
- while (op<cpy) *op++ = *match++;
- } else {
- LZ4_copy8(op, match);
- if (length>16) LZ4_wildCopy(op+8, match+8, cpy);
+ op += 8;
+
+ if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
+ BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy8(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op < cpy) { *op++ = *match++; }
+ } else {
+ memcpy(op, match, 8);
+ if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }
+ }
+ op = cpy; /* wildcopy correction */
}
- op=cpy; /* correction */
- }
- /* end of decoding */
- if (endOnInput)
- return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
- else
- return (int) (((const char*)ip)-source); /* Nb of input bytes read */
+ /* end of decoding */
+ if (endOnInput) {
+ return (int) (((char*)op)-dst); /* Nb of output bytes decoded */
+ } else {
+ return (int) (((const char*)ip)-src); /* Nb of input bytes read */
+ }
- /* Overflow error detected */
-_output_error:
- return (int) (-(((const char*)ip)-source))-1;
+ /* Overflow error detected */
+ _output_error:
+ return (int) (-(((const char*)ip)-src))-1;
+ }
}
+/*===== Instantiate the API decoding functions. =====*/
+
+LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
+ endOnInputSize, decode_full_block, noDict,
+ (BYTE*)dest, NULL, 0);
}
-int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)
{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
+ dstCapacity = MIN(targetOutputSize, dstCapacity);
+ return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,
+ endOnInputSize, partial_decode,
+ noDict, (BYTE*)dst, NULL, 0);
}
+LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
+ return LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, decode_full_block, withPrefix64k,
+ (BYTE*)dest - 64 KB, NULL, 0);
}
+/*===== Instantiate a few more decoding cases, used more than once. =====*/
-/*===== streaming decompression functions =====*/
+LZ4_FORCE_O2_GCC_PPC64LE /* Exported, an obsolete API function. */
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, decode_full_block, withPrefix64k,
+ (BYTE*)dest - 64 KB, NULL, 0);
+}
-/*
- * If you prefer dynamic allocation methods,
- * LZ4_createStreamDecode()
- * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
+/* Another obsolete API function, paired with the previous one. */
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+ /* LZ4_decompress_fast doesn't validate match offsets,
+ * and thus serves well with any prefixed dictionary. */
+ return LZ4_decompress_fast(source, dest, originalSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,
+ size_t prefixSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, decode_full_block, noDict,
+ (BYTE*)dest-prefixSize, NULL, 0);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
+ int compressedSize, int maxOutputSize,
+ const void* dictStart, size_t dictSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, decode_full_block, usingExtDict,
+ (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,
+ const void* dictStart, size_t dictSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, decode_full_block, usingExtDict,
+ (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+/* The "double dictionary" mode, for use with e.g. ring buffers: the first part
+ * of the dictionary is passed as prefix, and the second via dictStart + dictSize.
+ * These routines are used only once, in LZ4_decompress_*_continue().
*/
+LZ4_FORCE_INLINE
+int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,
+ size_t prefixSize, const void* dictStart, size_t dictSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, decode_full_block, usingExtDict,
+ (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_INLINE
+int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize,
+ size_t prefixSize, const void* dictStart, size_t dictSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, decode_full_block, usingExtDict,
+ (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
+}
+
+/*===== streaming decompression functions =====*/
+
LZ4_streamDecode_t* LZ4_createStreamDecode(void)
{
- LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
+ LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
+ LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal)); /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */
return lz4s;
}
int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
{
+ if (LZ4_stream == NULL) { return 0; } /* support free on NULL */
FREEMEM(LZ4_stream);
return 0;
}
-/*!
- * LZ4_setStreamDecode() :
- * Use this function to instruct where to find the dictionary.
- * This function is not necessary if previous data is still available where it was decoded.
- * Loading a size of 0 is allowed (same effect as no dictionary).
- * Return : 1 if OK, 0 if error
+/*! LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * @return : 1 if OK, 0 if error
*/
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
{
@@ -1310,6 +2205,25 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dicti
return 1;
}
+/*! LZ4_decoderRingBufferSize() :
+ * when setting a ring buffer for streaming decompression (optional scenario),
+ * provides the minimum size of this ring buffer
+ * to be compatible with any source respecting maxBlockSize condition.
+ * Note : in a ring buffer scenario,
+ * blocks are presumed decompressed next to each other.
+ * When not enough space remains for next block (remainingSize < maxBlockSize),
+ * decoding resumes from beginning of ring buffer.
+ * @return : minimum ring buffer size,
+ * or 0 if there is an error (invalid maxBlockSize).
+ */
+int LZ4_decoderRingBufferSize(int maxBlockSize)
+{
+ if (maxBlockSize < 0) return 0;
+ if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;
+ if (maxBlockSize < 16) maxBlockSize = 16;
+ return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);
+}
+
/*
*_continue() :
These decoding functions allow decompression of multiple blocks in "streaming" mode.
@@ -1317,52 +2231,75 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dicti
If it's not possible, save the relevant part of decoded data into a safe buffer,
and indicate where it stands using LZ4_setStreamDecode()
*/
+LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
{
LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
int result;
- if (lz4sd->prefixEnd == (BYTE*)dest) {
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (lz4sd->prefixSize == 0) {
+ /* The first call, no dictionary yet. */
+ assert(lz4sd->extDictSize == 0);
+ result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = (size_t)result;
+ lz4sd->prefixEnd = (BYTE*)dest + result;
+ } else if (lz4sd->prefixEnd == (BYTE*)dest) {
+ /* They're rolling the current segment. */
+ if (lz4sd->prefixSize >= 64 KB - 1)
+ result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
+ else if (lz4sd->extDictSize == 0)
+ result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,
+ lz4sd->prefixSize);
+ else
+ result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,
+ lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
- lz4sd->prefixSize += result;
+ lz4sd->prefixSize += (size_t)result;
lz4sd->prefixEnd += result;
} else {
+ /* The buffer wraps around, or they're switching to another buffer. */
lz4sd->extDictSize = lz4sd->prefixSize;
lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,
+ lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
- lz4sd->prefixSize = result;
+ lz4sd->prefixSize = (size_t)result;
lz4sd->prefixEnd = (BYTE*)dest + result;
}
return result;
}
+LZ4_FORCE_O2_GCC_PPC64LE
int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
{
LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
int result;
+ assert(originalSize >= 0);
- if (lz4sd->prefixEnd == (BYTE*)dest) {
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (lz4sd->prefixSize == 0) {
+ assert(lz4sd->extDictSize == 0);
+ result = LZ4_decompress_fast(source, dest, originalSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = (size_t)originalSize;
+ lz4sd->prefixEnd = (BYTE*)dest + originalSize;
+ } else if (lz4sd->prefixEnd == (BYTE*)dest) {
+ if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0)
+ result = LZ4_decompress_fast(source, dest, originalSize);
+ else
+ result = LZ4_decompress_fast_doubleDict(source, dest, originalSize,
+ lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
- lz4sd->prefixSize += originalSize;
+ lz4sd->prefixSize += (size_t)originalSize;
lz4sd->prefixEnd += originalSize;
} else {
lz4sd->extDictSize = lz4sd->prefixSize;
lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ result = LZ4_decompress_fast_extDict(source, dest, originalSize,
+ lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
- lz4sd->prefixSize = originalSize;
+ lz4sd->prefixSize = (size_t)originalSize;
lz4sd->prefixEnd = (BYTE*)dest + originalSize;
}
@@ -1377,32 +2314,27 @@ Advanced decoding functions :
the dictionary must be explicitly provided within parameters
*/
-FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
{
if (dictSize==0)
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
+ return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
if (dictStart+dictSize == dest) {
- if (dictSize >= (int)(64 KB - 1))
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
+ if (dictSize >= 64 KB - 1) {
+ return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
+ }
+ assert(dictSize >= 0);
+ return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);
}
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+ assert(dictSize >= 0);
+ return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);
}
int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
{
- return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
-}
-
-/* debug function */
-int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+ if (dictSize==0 || dictStart+dictSize == dest)
+ return LZ4_decompress_fast(source, dest, originalSize);
+ assert(dictSize >= 0);
+ return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);
}
@@ -1410,64 +2342,67 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compres
* Obsolete Functions
***************************************************/
/* obsolete compression functions */
-int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
-int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
-int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
-int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
-int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+ return LZ4_compress_default(source, dest, inputSize, maxOutputSize);
+}
+int LZ4_compress(const char* src, char* dest, int srcSize)
+{
+ return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));
+}
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)
+{
+ return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);
+}
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)
+{
+ return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);
+}
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)
+{
+ return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);
+}
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)
+{
+ return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);
+}
/*
-These function names are deprecated and should no longer be used.
+These decompression functions are deprecated and should no longer be used.
They are only provided here for compatibility with older user programs.
- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
*/
-int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
-int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
-
+int LZ4_uncompress (const char* source, char* dest, int outputSize)
+{
+ return LZ4_decompress_fast(source, dest, outputSize);
+}
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)
+{
+ return LZ4_decompress_safe(source, dest, isize, maxOutputSize);
+}
/* Obsolete Streaming functions */
int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
-static void LZ4_init(LZ4_stream_t* lz4ds, BYTE* base)
-{
- MEM_INIT(lz4ds, 0, sizeof(LZ4_stream_t));
- lz4ds->internal_donotuse.bufferStart = base;
-}
-
int LZ4_resetStreamState(void* state, char* inputBuffer)
{
- if ((((uptrval)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */
- LZ4_init((LZ4_stream_t*)state, (BYTE*)inputBuffer);
+ (void)inputBuffer;
+ LZ4_resetStream((LZ4_stream_t*)state);
return 0;
}
void* LZ4_create (char* inputBuffer)
{
- LZ4_stream_t* lz4ds = (LZ4_stream_t*)ALLOCATOR(8, sizeof(LZ4_stream_t));
- LZ4_init (lz4ds, (BYTE*)inputBuffer);
- return lz4ds;
+ (void)inputBuffer;
+ return LZ4_createStream();
}
-char* LZ4_slideInputBuffer (void* LZ4_Data)
-{
- LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)LZ4_Data)->internal_donotuse;
- int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB);
- return (char*)(ctx->bufferStart + dictSize);
-}
-
-/* Obsolete streaming decompression functions */
-
-int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
-}
-
-int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+char* LZ4_slideInputBuffer (void* state)
{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+ /* avoid const char * -> char * conversion warning */
+ return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
}
#endif /* LZ4_COMMONDEFS_ONLY */
diff --git a/src/compat/compat-lz4.h b/src/compat/compat-lz4.h
index 0aae19c..32108e2 100644
--- a/src/compat/compat-lz4.h
+++ b/src/compat/compat-lz4.h
@@ -1,7 +1,7 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
- * Copyright (C) 2011-2016, Yann Collet.
+ * Copyright (C) 2011-present, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@@ -32,13 +32,13 @@
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
-#ifndef LZ4_H_2983827168210
-#define LZ4_H_2983827168210
-
#if defined (__cplusplus)
extern "C" {
#endif
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
/* --- Dependency --- */
#include <stddef.h> /* size_t */
@@ -46,24 +46,31 @@ extern "C" {
/**
Introduction
- LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
The LZ4 compression library provides in-memory compression and decompression functions.
+ It gives full buffer control to user.
Compression can be done in:
- a single step (described as Simple Functions)
- a single step, reusing a context (described in Advanced Functions)
- unbounded multiple steps (described as Streaming compression)
- lz4.h provides block compression functions. It gives full buffer control to user.
- Decompressing an lz4-compressed block also requires metadata (such as compressed size).
- Each application is free to encode such metadata in whichever way it wants.
+ lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
+ Decompressing such a compressed block requires additional metadata.
+ Exact metadata depends on exact decompression function.
+ For the typical case of LZ4_decompress_safe(),
+ metadata includes block's compressed size, and maximum bound of decompressed size.
+ Each application is free to encode and pass such metadata in whichever way it wants.
+
+ lz4.h only handle blocks, it can not generate Frames.
- An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
- take care of encoding standard metadata alongside LZ4-compressed blocks.
- If your application requires interoperability, it's recommended to use it.
- A library is provided to take care of it, see lz4frame.h.
+ Blocks are different from Frames (doc/lz4_Frame_format.md).
+ Frames bundle both blocks and metadata in a specified manner.
+ Embedding metadata is required for compressed data to be self-contained and portable.
+ Frame format is delivered through a companion API, declared in lz4frame.h.
+ The `lz4` CLI can only manage frames.
*/
/*^***************************************************************
@@ -72,20 +79,28 @@ extern "C" {
/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
+* LZ4LIB_VISIBILITY :
+* Control library symbols visibility.
*/
+#ifndef LZ4LIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define LZ4LIB_VISIBILITY
+# endif
+#endif
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
-# define LZ4LIB_API __declspec(dllexport)
+# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
-# define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
-# define LZ4LIB_API
+# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
-
-/*========== Version =========== */
+/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
-#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
-#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */
+#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
@@ -94,8 +109,8 @@ extern "C" {
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
-LZ4LIB_API int LZ4_versionNumber (void);
-LZ4LIB_API const char* LZ4_versionString (void);
+LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
+LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */
/*-************************************
@@ -104,41 +119,49 @@ LZ4LIB_API const char* LZ4_versionString (void);
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
- * Increasing memory usage improves compression ratio
- * Reduced memory usage can improve speed, due to cache effect
+ * Increasing memory usage improves compression ratio.
+ * Reduced memory usage may improve speed, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
-#define LZ4_MEMORY_USAGE 14
+#ifndef LZ4_MEMORY_USAGE
+# define LZ4_MEMORY_USAGE 14
+#endif
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
- Compresses 'sourceSize' bytes from buffer 'source'
- into already allocated 'dest' buffer of size 'maxDestSize'.
- Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
- It also runs faster, so it's a recommended setting.
- If the function cannot compress 'source' into a more limited 'dest' budget,
- compression stops *immediately*, and the function result is zero.
- As a consequence, 'dest' content is not valid.
- This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
- sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
- maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
- return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
- or 0 if compression fails */
-LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
+ * Compresses 'srcSize' bytes from buffer 'src'
+ * into already allocated 'dst' buffer of size 'dstCapacity'.
+ * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
+ * It also runs faster, so it's a recommended setting.
+ * If the function cannot compress 'src' into a more limited 'dst' budget,
+ * compression stops *immediately*, and the function result is zero.
+ * In which case, 'dst' content is undefined (invalid).
+ * srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
+ * dstCapacity : size of buffer 'dst' (which must be already allocated)
+ * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
+ * or 0 if compression fails
+ * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
+ */
+LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() :
- compressedSize : is the precise full size of the compressed block.
- maxDecompressedSize : is the size of destination buffer, which must be already allocated.
- return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
- If destination buffer is not large enough, decoding will stop and output an error code (<0).
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function is protected against buffer overflow exploits, including malicious data packets.
- It never writes outside output buffer, nor reads outside input buffer.
-*/
-LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+ * compressedSize : is the exact complete size of the compressed block.
+ * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.
+ * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
+ * If destination buffer is not large enough, decoding will stop and output an error code (negative value).
+ * If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ * Note 1 : This function is protected against malicious data packets :
+ * it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
+ * even if the compressed block is maliciously modified to order the decoder to do these actions.
+ * In such case, the decoder stops immediately, and considers the compressed block malformed.
+ * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
+ * The implementation is free to send / store / derive this information in whichever way is most beneficial.
+ * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
+ */
+LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
/*-************************************
@@ -147,184 +170,389 @@ LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compress
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
-/*!
-LZ4_compressBound() :
+/*! LZ4_compressBound() :
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
This function is primarily useful for memory allocation purposes (destination buffer size).
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
- Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
+ Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
return : maximum output size in a "worst case" scenario
- or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
+ or 0, if input size is incorrect (too large or negative)
*/
LZ4LIB_API int LZ4_compressBound(int inputSize);
-/*!
-LZ4_compress_fast() :
- Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
+/*! LZ4_compress_fast() :
+ Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
- Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
+ Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c).
*/
-LZ4LIB_API int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
+LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
-/*!
-LZ4_compress_fast_extState() :
- Same compression function, just using an externally allocated memory space to store compression state.
- Use LZ4_sizeofState() to know how much memory must be allocated,
- and allocate it on 8-bytes boundaries (using malloc() typically).
- Then, provide it as 'void* state' to compression function.
-*/
+/*! LZ4_compress_fast_extState() :
+ * Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
+ * Use LZ4_sizeofState() to know how much memory must be allocated,
+ * and allocate it on 8-bytes boundaries (using `malloc()` typically).
+ * Then, provide this buffer as `void* state` to compression function.
+ */
LZ4LIB_API int LZ4_sizeofState(void);
-LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
-
-
-/*!
-LZ4_compress_destSize() :
- Reverse the logic, by compressing as much data as possible from 'source' buffer
- into already allocated buffer 'dest' of size 'targetDestSize'.
- This function either compresses the entire 'source' content into 'dest' if it's large enough,
- or fill 'dest' buffer completely with as much data as possible from 'source'.
- *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
- New value is necessarily <= old value.
- return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
- or 0 if compression fails
-*/
-LZ4LIB_API int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
-
-
-/*!
-LZ4_decompress_fast() :
- originalSize : is the original and therefore uncompressed size
- return : the number of bytes read from the source buffer (in other words, the compressed size)
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
- note : This function fully respect memory boundaries for properly formed compressed data.
- It is a bit faster than LZ4_decompress_safe().
- However, it does not provide any protection against intentionally modified data stream (malicious input).
- Use this function in trusted environment only (data to decode comes from a trusted source).
-*/
-LZ4LIB_API int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
-
-/*!
-LZ4_decompress_safe_partial() :
- This function decompress a compressed block of size 'compressedSize' at position 'source'
- into destination buffer 'dest' of size 'maxDecompressedSize'.
- The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
- reducing decompression time.
- return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
- Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
- Always control how many bytes were decoded.
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*! LZ4_compress_destSize() :
+ * Reverse the logic : compresses as much data as possible from 'src' buffer
+ * into already allocated buffer 'dst', of size >= 'targetDestSize'.
+ * This function either compresses the entire 'src' content into 'dst' if it's large enough,
+ * or fill 'dst' buffer completely with as much data as possible from 'src'.
+ * note: acceleration parameter is fixed to "default".
+ *
+ * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
+ * New value is necessarily <= input value.
+ * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
+ * or 0 if compression fails.
*/
-LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
+LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
+
+
+/*! LZ4_decompress_safe_partial() :
+ * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
+ * into destination buffer 'dst' of size 'dstCapacity'.
+ * Up to 'targetOutputSize' bytes will be decoded.
+ * The function stops decoding on reaching this objective,
+ * which can boost performance when only the beginning of a block is required.
+ *
+ * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity)
+ * If source stream is detected malformed, function returns a negative result.
+ *
+ * Note : @return can be < targetOutputSize, if compressed block contains less data.
+ *
+ * Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity,
+ * and expects targetOutputSize <= dstCapacity.
+ * It effectively stops decoding on reaching targetOutputSize,
+ * so dstCapacity is kind of redundant.
+ * This is because in a previous version of this function,
+ * decoding operation would not "break" a sequence in the middle.
+ * As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize,
+ * it could write more bytes, though only up to dstCapacity.
+ * Some "margin" used to be required for this operation to work properly.
+ * This is no longer necessary.
+ * The function nonetheless keeps its signature, in an effort to not break API.
+ */
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
/*-*********************************************
* Streaming Compression Functions
***********************************************/
-typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
-/*! LZ4_createStream() and LZ4_freeStream() :
- * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
- * LZ4_freeStream() releases its memory.
- */
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
-/*! LZ4_resetStream() :
- * An LZ4_stream_t structure can be allocated once and re-used multiple times.
- * Use this function to init an allocated `LZ4_stream_t` structure and start a new compression.
+/*! LZ4_resetStream_fast() : v1.9.0+
+ * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
+ * (e.g., LZ4_compress_fast_continue()).
+ *
+ * An LZ4_stream_t must be initialized once before usage.
+ * This is automatically done when created by LZ4_createStream().
+ * However, should the LZ4_stream_t be simply declared on stack (for example),
+ * it's necessary to initialize it first, using LZ4_initStream().
+ *
+ * After init, start any new stream with LZ4_resetStream_fast().
+ * A same LZ4_stream_t can be re-used multiple times consecutively
+ * and compress multiple streams,
+ * provided that it starts each new stream with LZ4_resetStream_fast().
+ *
+ * LZ4_resetStream_fast() is much faster than LZ4_initStream(),
+ * but is not compatible with memory regions containing garbage data.
+ *
+ * Note: it's only useful to call LZ4_resetStream_fast()
+ * in the context of streaming compression.
+ * The *extState* functions perform their own resets.
+ * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
*/
-LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
/*! LZ4_loadDict() :
- * Use this function to load a static dictionary into LZ4_stream.
- * Any previous data will be forgotten, only 'dictionary' will remain in memory.
- * Loading a size of 0 is allowed.
- * Return : dictionary size, in bytes (necessarily <= 64 KB)
+ * Use this function to reference a static dictionary into LZ4_stream_t.
+ * The dictionary must remain available during compression.
+ * LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
+ * The same dictionary will have to be loaded on decompression side for successful decoding.
+ * Dictionary are useful for better compression of small data (KB range).
+ * While LZ4 accept any input as dictionary,
+ * results are generally better when using Zstandard's Dictionary Builder.
+ * Loading a size of 0 is allowed, and is the same as reset.
+ * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)
*/
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_compress_fast_continue() :
- * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
- * Important : Previous data blocks are assumed to still be present and unmodified !
- * 'dst' buffer must be already allocated.
- * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
+ * Compress 'src' content using data from previously compressed blocks, for better compression ratio.
+ * 'dst' buffer must be already allocated.
+ * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ *
+ * @return : size of compressed block
+ * or 0 if there is an error (typically, cannot fit into 'dst').
+ *
+ * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
+ * Each block has precise boundaries.
+ * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
+ * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
+ *
+ * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
+ *
+ * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
+ * Make sure that buffers are separated, by at least one byte.
+ * This construction ensures that each block only depends on previous block.
+ *
+ * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
+ *
+ * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
*/
-LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_saveDict() :
- * If previously compressed data block is not guaranteed to remain available at its memory location,
+ * If last 64KB data cannot be guaranteed to remain available at its current memory location,
* save it into a safer place (char* safeBuffer).
- * Note : you don't need to call LZ4_loadDict() afterwards,
- * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
- * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
+ * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
+ * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
*/
-LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
/*-**********************************************
* Streaming Decompression Functions
* Bufferless synchronous API
************************************************/
-typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
-/* creation / destruction of streaming decompression tracking structure */
+/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
+ * creation / destruction of streaming decompression tracking context.
+ * A tracking context can be re-used multiple times.
+ */
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
/*! LZ4_setStreamDecode() :
- * Use this function to instruct where to find the dictionary.
- * Setting a size of 0 is allowed (same effect as reset).
- * @return : 1 if OK, 0 if error
+ * An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
+ * Use this function to start decompression of a new stream of blocks.
+ * A dictionary can optionally be set. Use NULL or size 0 for a reset order.
+ * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
+ * @return : 1 if OK, 0 if error
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
-/*!
-LZ4_decompress_*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
- In the case of a ring buffers, decoding buffer must be either :
- - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
- In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
- - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
- maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including small ones ( < 64 KB).
- - _At least_ 64 KB + 8 bytes + maxBlockSize.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including larger than decoding buffer.
- Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
- and indicate where it is saved using LZ4_setStreamDecode()
+/*! LZ4_decoderRingBufferSize() : v1.8.2+
+ * Note : in a ring buffer scenario (optional),
+ * blocks are presumed decompressed next to each other
+ * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
+ * at which stage it resumes from beginning of ring buffer.
+ * When setting such a ring buffer for streaming decompression,
+ * provides the minimum size of this ring buffer
+ * to be compatible with any source respecting maxBlockSize condition.
+ * @return : minimum ring buffer size,
+ * or 0 if there is an error (invalid maxBlockSize).
+ */
+LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
+#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
+
+/*! LZ4_decompress_*_continue() :
+ * These decoding functions allow decompression of consecutive blocks in "streaming" mode.
+ * A block is an unsplittable entity, it must be presented entirely to a decompression function.
+ * Decompression functions only accepts one block at a time.
+ * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.
+ * If less than 64KB of data has been decoded, all the data must be present.
+ *
+ * Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
+ * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
+ * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
+ * In which case, encoding and decoding buffers do not need to be synchronized.
+ * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
+ * - Synchronized mode :
+ * Decompression buffer size is _exactly_ the same as compression buffer size,
+ * and follows exactly same update rule (block boundaries at same positions),
+ * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
+ * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
+ * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ * In which case, encoding and decoding buffers do not need to be synchronized,
+ * and encoding ring buffer can have any size, including small ones ( < 64 KB).
+ *
+ * Whenever these conditions are not possible,
+ * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
+ * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
-LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
+LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_*_usingDict() :
* These decoding functions work the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
* They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ * Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
+ * Performance tip : Decompression speed can be substantially increased
+ * when dst == dictStart + dictSize.
*/
-LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
-LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
+LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
+#endif /* LZ4_H_2983827168210 */
-/*^**********************************************
+
+/*^*************************************
* !!!!!! STATIC LINKING ONLY !!!!!!
- ***********************************************/
-/*-************************************
- * Private definitions
- **************************************
- * Do not use these definitions.
- * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
- * Using these definitions will expose code to API and/or ABI break in future versions of the library.
- **************************************/
+ ***************************************/
+
+/*-****************************************************************************
+ * Experimental section
+ *
+ * Symbols declared in this section must be considered unstable. Their
+ * signatures or semantics may change, or they may be removed altogether in the
+ * future. They are therefore only safe to depend on when the caller is
+ * statically linked against the library.
+ *
+ * To protect against unsafe usage, not only are the declarations guarded,
+ * the definitions are hidden by default
+ * when building LZ4 as a shared/dynamic library.
+ *
+ * In order to access these declarations,
+ * define LZ4_STATIC_LINKING_ONLY in your application
+ * before including LZ4's headers.
+ *
+ * In order to make their implementations accessible dynamically, you must
+ * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
+ ******************************************************************************/
+
+#ifdef LZ4_STATIC_LINKING_ONLY
+
+#ifndef LZ4_STATIC_3504398509
+#define LZ4_STATIC_3504398509
+
+#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
+#define LZ4LIB_STATIC_API LZ4LIB_API
+#else
+#define LZ4LIB_STATIC_API
+#endif
+
+
+/*! LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step.
+ * It is only safe to call if the state buffer is known to be correctly initialized already
+ * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
+ * From a high level, the difference is that
+ * this function initializes the provided state with a call to something like LZ4_resetStream_fast()
+ * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
+ */
+LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_attach_dictionary() :
+ * This is an experimental API that allows
+ * efficient use of a static dictionary many times.
+ *
+ * Rather than re-loading the dictionary buffer into a working context before
+ * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
+ * working LZ4_stream_t, this function introduces a no-copy setup mechanism,
+ * in which the working stream references the dictionary stream in-place.
+ *
+ * Several assumptions are made about the state of the dictionary stream.
+ * Currently, only streams which have been prepared by LZ4_loadDict() should
+ * be expected to work.
+ *
+ * Alternatively, the provided dictionaryStream may be NULL,
+ * in which case any existing dictionary stream is unset.
+ *
+ * If a dictionary is provided, it replaces any pre-existing stream history.
+ * The dictionary contents are the only history that can be referenced and
+ * logically immediately precede the data compressed in the first subsequent
+ * compression call.
+ *
+ * The dictionary will only remain attached to the working stream through the
+ * first compression call, at the end of which it is cleared. The dictionary
+ * stream (and source buffer) must remain in-place / accessible / unchanged
+ * through the completion of the first compression call on the stream.
+ */
+LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream);
+
+
+/*! In-place compression and decompression
+ *
+ * It's possible to have input and output sharing the same buffer,
+ * for highly contrained memory environments.
+ * In both cases, it requires input to lay at the end of the buffer,
+ * and decompression to start at beginning of the buffer.
+ * Buffer size must feature some margin, hence be larger than final size.
+ *
+ * |<------------------------buffer--------------------------------->|
+ * |<-----------compressed data--------->|
+ * |<-----------decompressed size------------------>|
+ * |<----margin---->|
+ *
+ * This technique is more useful for decompression,
+ * since decompressed size is typically larger,
+ * and margin is short.
+ *
+ * In-place decompression will work inside any buffer
+ * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
+ * This presumes that decompressedSize > compressedSize.
+ * Otherwise, it means compression actually expanded data,
+ * and it would be more efficient to store such data with a flag indicating it's not compressed.
+ * This can happen when data is not compressible (already compressed, or encrypted).
+ *
+ * For in-place compression, margin is larger, as it must be able to cope with both
+ * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
+ * and data expansion, which can happen when input is not compressible.
+ * As a consequence, buffer size requirements are much higher,
+ * and memory savings offered by in-place compression are more limited.
+ *
+ * There are ways to limit this cost for compression :
+ * - Reduce history size, by modifying LZ4_DISTANCE_MAX.
+ * Note that it is a compile-time constant, so all compressions will apply this limit.
+ * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
+ * so it's a reasonable trick when inputs are known to be small.
+ * - Require the compressor to deliver a "maximum compressed size".
+ * This is the `dstCapacity` parameter in `LZ4_compress*()`.
+ * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
+ * in which case, the return code will be 0 (zero).
+ * The caller must be ready for these cases to happen,
+ * and typically design a backup scheme to send data uncompressed.
+ * The combination of both techniques can significantly reduce
+ * the amount of margin required for in-place compression.
+ *
+ * In-place compression can work in any buffer
+ * which size is >= (maxCompressedSize)
+ * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
+ * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
+ * so it's possible to reduce memory requirements by playing with them.
+ */
+
+#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
+#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
+
+#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
+# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
+#endif
+
+#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
+#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
+
+#endif /* LZ4_STATIC_3504398509 */
+#endif /* LZ4_STATIC_LINKING_ONLY */
+
+
+
+#ifndef LZ4_H_98237428734687
+#define LZ4_H_98237428734687
+
+/*-************************************************************
+ * PRIVATE DEFINITIONS
+ **************************************************************
+ * Do not use these definitions directly.
+ * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Accessing members will expose code to API and/or ABI break in future versions of the library.
+ **************************************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
@@ -332,14 +560,16 @@ LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, in
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
#include <stdint.h>
-typedef struct {
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
uint32_t hashTable[LZ4_HASH_SIZE_U32];
uint32_t currentOffset;
- uint32_t initCheck;
+ uint16_t dirty;
+ uint16_t tableType;
const uint8_t* dictionary;
- uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */
+ const LZ4_stream_t_internal* dictCtx;
uint32_t dictSize;
-} LZ4_stream_t_internal;
+};
typedef struct {
const uint8_t* externalDict;
@@ -350,49 +580,67 @@ typedef struct {
#else
-typedef struct {
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
unsigned int hashTable[LZ4_HASH_SIZE_U32];
unsigned int currentOffset;
- unsigned int initCheck;
+ unsigned short dirty;
+ unsigned short tableType;
const unsigned char* dictionary;
- unsigned char* bufferStart; /* obsolete, used for slideInputBuffer */
+ const LZ4_stream_t_internal* dictCtx;
unsigned int dictSize;
-} LZ4_stream_t_internal;
+};
typedef struct {
const unsigned char* externalDict;
- size_t extDictSize;
const unsigned char* prefixEnd;
+ size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#endif
-/*!
- * LZ4_stream_t :
- * information structure to track an LZ4 stream.
- * init this structure before first use.
- * note : only use in association with static linking !
- * this definition is not API/ABI safe,
- * and may change in a future version !
+/*! LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended.
+ * The structure definition can be convenient for static allocation
+ * (on stack, or as part of larger structure).
+ * Init this structure with LZ4_initStream() before first use.
+ * note : only use this definition in association with static linking !
+ * this definition is not API/ABI safe, and may change in a future version.
*/
-#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ )
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
union LZ4_stream_u {
unsigned long long table[LZ4_STREAMSIZE_U64];
LZ4_stream_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_stream_t */
+/*! LZ4_initStream() : v1.9.0+
+ * An LZ4_stream_t structure must be initialized at least once.
+ * This is automatically done when invoking LZ4_createStream(),
+ * but it's not when the structure is simply declared on stack (for example).
+ *
+ * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
+ * It can also initialize any arbitrary buffer of sufficient size,
+ * and will @return a pointer of proper type upon initialization.
+ *
+ * Note : initialization fails if size and alignment conditions are not respected.
+ * In which case, the function will @return NULL.
+ * Note2: An LZ4_stream_t structure guarantees correct alignment and size.
+ * Note3: Before v1.9.0, use LZ4_resetStream() instead
+ */
+LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
-/*!
- * LZ4_streamDecode_t :
- * information structure to track an LZ4 stream during decompression.
- * init this structure using LZ4_setStreamDecode (or memset()) before first use
- * note : only use in association with static linking !
- * this definition is not API/ABI safe,
- * and may change in a future version !
+
+/*! LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure using LZ4_setStreamDecode() before first use.
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * and may change in a future version !
*/
-#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ )
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
union LZ4_streamDecode_u {
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
@@ -400,15 +648,22 @@ union LZ4_streamDecode_u {
} ; /* previously typedef'd to LZ4_streamDecode_t */
-/*=************************************
+
+/*-************************************
* Obsolete Functions
**************************************/
-/* Deprecation warnings */
-/* Should these warnings be a problem,
- it is generally possible to disable them,
- typically with -Wno-deprecated-declarations for gcc
- or _CRT_SECURE_NO_WARNINGS in Visual.
- Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/*! Deprecation warnings
+ *
+ * Deprecated functions make the compiler generate a warning when invoked.
+ * This is meant to invite users to update their source code.
+ * Should deprecation warnings be a problem, it is generally possible to disable them,
+ * typically with -Wno-deprecated-declarations for gcc
+ * or _CRT_SECURE_NO_WARNINGS in Visual.
+ *
+ * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
+ * before including the header file.
+ */
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
@@ -428,36 +683,82 @@ union LZ4_streamDecode_u {
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/* Obsolete compression functions */
-LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress (const char* source, char* dest, int sourceSize);
-LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/* Obsolete decompression functions */
-/* These function names are completely deprecated and must no longer be used.
- They are only provided in lz4.c for compatibility with older programs.
- - LZ4_uncompress is the same as LZ4_decompress_fast
- - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
- These function prototypes are now disabled; uncomment them only if you really need them.
- It is highly recommended to stop using these prototypes and migrate to maintained ones */
-/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
-/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
-
-/* Obsolete streaming functions; use new streaming interface whenever possible */
-LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
-LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
+LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
+LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
+
+/* Obsolete streaming functions; degraded functionality; do not use!
+ *
+ * In order to perform streaming compression, these functions depended on data
+ * that is no longer tracked in the state. They have been preserved as well as
+ * possible: using them will still produce a correct output. However, they don't
+ * actually retain any history between compression calls. The compression ratio
+ * achieved will therefore be no better than compressing each chunk
+ * independently.
+ */
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
/* Obsolete streaming decoding functions */
-LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
-LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+/*! LZ4_decompress_fast() : **unsafe!**
+ * These functions used to be faster than LZ4_decompress_safe(),
+ * but it has changed, and they are now slower than LZ4_decompress_safe().
+ * This is because LZ4_decompress_fast() doesn't know the input size,
+ * and therefore must progress more cautiously in the input buffer to not read beyond the end of block.
+ * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
+ * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
+ *
+ * The last remaining LZ4_decompress_fast() specificity is that
+ * it can decompress a block without knowing its compressed size.
+ * Such functionality could be achieved in a more secure manner,
+ * by also providing the maximum size of input buffer,
+ * but it would require new prototypes, and adaptation of the implementation to this new use case.
+ *
+ * Parameters:
+ * originalSize : is the uncompressed size to regenerate.
+ * `dst` must be already allocated, its size must be >= 'originalSize' bytes.
+ * @return : number of bytes read from source buffer (== compressed size).
+ * The function expects to finish at block's end exactly.
+ * If the source stream is detected malformed, the function stops decoding and returns a negative result.
+ * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
+ * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
+ * Also, since match offsets are not validated, match reads from 'src' may underflow too.
+ * These issues never happen if input (compressed) data is correct.
+ * But they may happen if input data is invalid (error or intentional tampering).
+ * As a consequence, use these functions in trusted environments with trusted data **only**.
+ */
+
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
+LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
+LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead")
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
+
+/*! LZ4_resetStream() :
+ * An LZ4_stream_t structure must be initialized at least once.
+ * This is done with LZ4_initStream(), or LZ4_resetStream().
+ * Consider switching to LZ4_initStream(),
+ * invoking LZ4_resetStream() will trigger deprecation warnings in the future.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+
+#endif /* LZ4_H_98237428734687 */
#if defined (__cplusplus)
}
#endif
-
-#endif /* LZ4_H_2983827168210 */
diff --git a/src/compat/compat-strsep.c b/src/compat/compat-strsep.c
new file mode 100644
index 0000000..7a6e6b3
--- /dev/null
+++ b/src/compat/compat-strsep.c
@@ -0,0 +1,61 @@
+/*
+ * 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) 2019-2021 Arne Schwabe <arne@rfc2549.org>
+ * Copyright (C) 1992-2019 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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
+
+#ifndef HAVE_STRSEP
+#include <string.h>
+
+/*
+ * Modified version based on the glibc
+ */
+char *
+strsep(char **stringp, const char *delim)
+{
+ char *begin, *end;
+ begin = *stringp;
+ if (begin == NULL)
+ {
+ return NULL;
+ }
+ /* Find the end of the token. */
+ end = begin + strcspn(begin, delim);
+ if (*end)
+ {
+ /* Terminate the token and set *STRINGP past NUL character. */
+ *end++ = '\0';
+ *stringp = end;
+ }
+ else
+ {
+ /* No more delimiters; this is the last token. */
+ *stringp = NULL;
+ }
+ return begin;
+}
+#endif /* ifndef HAVE_STRSEP */
diff --git a/src/compat/compat-versionhelpers.h b/src/compat/compat-versionhelpers.h
index 251fb04..9e25470 100644
--- a/src/compat/compat-versionhelpers.h
+++ b/src/compat/compat-versionhelpers.h
@@ -18,6 +18,10 @@
#define _WIN32_WINNT_WINBLUE 0x0603
+#ifndef _WIN32_WINNT_WINTHRESHOLD
+#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 // Windows 10
+#endif
+
VERSIONHELPERAPI
IsWindowsVersionOrGreater(WORD major, WORD minor, WORD servpack)
{
@@ -96,6 +100,12 @@ IsWindows8Point1OrGreater(void)
}
VERSIONHELPERAPI
+IsWindows10OrGreater()
+{
+ return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINTHRESHOLD), LOBYTE(_WIN32_WINNT_WINTHRESHOLD), 0);
+}
+
+VERSIONHELPERAPI
IsWindowsServer(void)
{
OSVERSIONINFOEXW vi = {sizeof(vi),0,0,0,0,{0},0,0,0,VER_NT_WORKSTATION};
diff --git a/src/compat/compat.h b/src/compat/compat.h
index d522898..a66a423 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -70,4 +70,9 @@ int inet_pton(int af, const char *src, void *dst);
#endif
+#ifndef HAVE_STRSEP
+char *strsep(char **stringp, const char *delim);
+
+#endif
+
#endif /* COMPAT_H */
diff --git a/src/compat/compat.vcxproj b/src/compat/compat.vcxproj
index d2695e6..14376e4 100644
--- a/src/compat/compat.vcxproj
+++ b/src/compat/compat.vcxproj
@@ -1,75 +1,154 @@
<?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|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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>{4B2E2719-E661-45D7-9203-F6F456B22F19}</ProjectGuid>
<RootNamespace>compat</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>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
- <PlatformToolset>v120</PlatformToolset>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v120</PlatformToolset>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</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="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="Release.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="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="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="Debug.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="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>
- <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Platform)-Output\$(Configuration)\</OutDir>
- <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
- <Optimization>Disabled</Optimization>
- <AdditionalIncludeDirectories>$(SOURCEBASE);$(SOURCEBASE)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MinimalRebuild>true</MinimalRebuild>
- <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
- <Optimization>MaxSpeed</Optimization>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <AdditionalIncludeDirectories>$(SOURCEBASE);$(SOURCEBASE)/include;$(OPENSSL_HOME)/include;$(LZO_HOME)/include;$(PKCS11H_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
@@ -80,10 +159,17 @@
<ClCompile Include="compat-inet_pton.c" />
<ClCompile Include="compat-daemon.c" />
<ClCompile Include="compat-lz4.c" />
+ <ClCompile Include="compat-strsep.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="compat.h" />
</ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\build\msvc\msvc-generate\msvc-generate.vcxproj">
+ <Project>{8598c2c8-34c4-47a1-99b0-7c295a890615}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
diff --git a/src/compat/compat.vcxproj.filters b/src/compat/compat.vcxproj.filters
index 0f78e86..ec6a20b 100644
--- a/src/compat/compat.vcxproj.filters
+++ b/src/compat/compat.vcxproj.filters
@@ -24,12 +24,6 @@
<ClCompile Include="compat-gettimeofday.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="compat-inet_ntop.c">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="compat-inet_pton.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="compat-daemon.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 0ff23ba..781148b 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -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
index 963f6ab..de99f72 100644
--- a/src/openvpn/Makefile.in
+++ b/src/openvpn/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -114,8 +114,8 @@ 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
+@WIN32_TRUE@am__append_2 = openvpn_win32_resources.rc block_dns.c block_dns.h ring_buffer.h
+@WIN32_TRUE@am__append_3 = -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -lsetupapi
subdir = src/openvpn
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
@@ -135,61 +135,68 @@ 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
+am__openvpn_SOURCES_DIST = argv.c argv.h auth_token.c auth_token.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 env_set.c env_set.h errlevel.h error.c error.h event.c \
+ event.h fdmisc.c fdmisc.h forward.c forward.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 networking_iproute2.c networking_iproute2.h \
+ networking_sitnl.c networking_sitnl.h networking.h ntlm.c \
+ ntlm.h occ.c occ.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 ping.c ping.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 run_command.c run_command.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_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 \
+ ssl_verify_mbedtls.c ssl_verify_mbedtls.h status.c status.h \
+ 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_win32_resources.rc block_dns.c block_dns.h \
+ ring_buffer.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) \
+am_openvpn_OBJECTS = argv.$(OBJEXT) auth_token.$(OBJEXT) \
+ base64.$(OBJEXT) buffer.$(OBJEXT) clinat.$(OBJEXT) \
+ comp.$(OBJEXT) compstub.$(OBJEXT) comp-lz4.$(OBJEXT) \
+ crypto.$(OBJEXT) crypto_openssl.$(OBJEXT) \
+ crypto_mbedtls.$(OBJEXT) dhcp.$(OBJEXT) env_set.$(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) \
+ multi.$(OBJEXT) networking_iproute2.$(OBJEXT) \
+ networking_sitnl.$(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) run_command.$(OBJEXT) \
+ schedule.$(OBJEXT) session_id.$(OBJEXT) shaper.$(OBJEXT) \
+ sig.$(OBJEXT) socket.$(OBJEXT) socks.$(OBJEXT) ssl.$(OBJEXT) \
+ ssl_openssl.$(OBJEXT) ssl_mbedtls.$(OBJEXT) ssl_ncp.$(OBJEXT) \
+ ssl_verify.$(OBJEXT) ssl_verify_openssl.$(OBJEXT) \
+ ssl_verify_mbedtls.$(OBJEXT) status.$(OBJEXT) \
+ tls_crypt.$(OBJEXT) tun.$(OBJEXT) vlan.$(OBJEXT) \
win32.$(OBJEXT) cryptoapi.$(OBJEXT) $(am__objects_1)
openvpn_OBJECTS = $(am_openvpn_OBJECTS)
am__DEPENDENCIES_1 =
@@ -218,14 +225,15 @@ 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 \
+am__depfiles_remade = ./$(DEPDIR)/argv.Po ./$(DEPDIR)/auth_token.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)/env_set.Po \
./$(DEPDIR)/error.Po ./$(DEPDIR)/event.Po \
./$(DEPDIR)/fdmisc.Po ./$(DEPDIR)/forward.Po \
./$(DEPDIR)/fragment.Po ./$(DEPDIR)/gremlin.Po \
@@ -235,23 +243,26 @@ am__depfiles_remade = ./$(DEPDIR)/argv.Po ./$(DEPDIR)/base64.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)/multi.Po ./$(DEPDIR)/networking_iproute2.Po \
+ ./$(DEPDIR)/networking_sitnl.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)/run_command.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_mbedtls.Po ./$(DEPDIR)/ssl_ncp.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
+ ./$(DEPDIR)/vlan.Po ./$(DEPDIR)/win32.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -312,7 +323,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -326,6 +338,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -353,7 +366,6 @@ 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@
@@ -404,6 +416,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -467,6 +481,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -487,7 +502,8 @@ MAINTAINERCLEANFILES = \
EXTRA_DIST = \
openvpn.vcxproj \
- openvpn.vcxproj.filters
+ openvpn.vcxproj.filters \
+ openvpn.manifest
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
@@ -497,35 +513,37 @@ 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_SOURCES = argv.c argv.h auth_token.c auth_token.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 env_set.c env_set.h errlevel.h error.c error.h event.c \
+ event.h fdmisc.c fdmisc.h forward.c forward.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 networking_iproute2.c networking_iproute2.h \
+ networking_sitnl.c networking_sitnl.h networking.h ntlm.c \
+ ntlm.h occ.c occ.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 ping.c ping.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 run_command.c run_command.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_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 \
+ ssl_verify_mbedtls.c ssl_verify_mbedtls.h status.c status.h \
+ syshead.h tls_crypt.c tls_crypt.h tun.c tun.h vlan.c vlan.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) \
@@ -626,6 +644,7 @@ 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)/auth_token.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
@@ -641,6 +660,7 @@ distclean-compile:
@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)/env_set.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
@@ -664,6 +684,8 @@ distclean-compile:
@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)/networking_iproute2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_sitnl.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
@@ -685,6 +707,7 @@ distclean-compile:
@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)/run_command.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
@@ -693,6 +716,7 @@ distclean-compile:
@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_ncp.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
@@ -700,6 +724,7 @@ distclean-compile:
@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)/vlan.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32.Po@am__quote@ # am--include-marker
$(am__depfiles_remade):
@@ -865,6 +890,7 @@ clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
distclean: distclean-am
-rm -f ./$(DEPDIR)/argv.Po
+ -rm -f ./$(DEPDIR)/auth_token.Po
-rm -f ./$(DEPDIR)/base64.Po
-rm -f ./$(DEPDIR)/block_dns.Po
-rm -f ./$(DEPDIR)/buffer.Po
@@ -880,6 +906,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/crypto_openssl.Po
-rm -f ./$(DEPDIR)/cryptoapi.Po
-rm -f ./$(DEPDIR)/dhcp.Po
+ -rm -f ./$(DEPDIR)/env_set.Po
-rm -f ./$(DEPDIR)/error.Po
-rm -f ./$(DEPDIR)/event.Po
-rm -f ./$(DEPDIR)/fdmisc.Po
@@ -903,6 +930,8 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/mtu.Po
-rm -f ./$(DEPDIR)/mudp.Po
-rm -f ./$(DEPDIR)/multi.Po
+ -rm -f ./$(DEPDIR)/networking_iproute2.Po
+ -rm -f ./$(DEPDIR)/networking_sitnl.Po
-rm -f ./$(DEPDIR)/ntlm.Po
-rm -f ./$(DEPDIR)/occ.Po
-rm -f ./$(DEPDIR)/openvpn.Po
@@ -924,6 +953,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/push.Po
-rm -f ./$(DEPDIR)/reliable.Po
-rm -f ./$(DEPDIR)/route.Po
+ -rm -f ./$(DEPDIR)/run_command.Po
-rm -f ./$(DEPDIR)/schedule.Po
-rm -f ./$(DEPDIR)/session_id.Po
-rm -f ./$(DEPDIR)/shaper.Po
@@ -932,6 +962,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/socks.Po
-rm -f ./$(DEPDIR)/ssl.Po
-rm -f ./$(DEPDIR)/ssl_mbedtls.Po
+ -rm -f ./$(DEPDIR)/ssl_ncp.Po
-rm -f ./$(DEPDIR)/ssl_openssl.Po
-rm -f ./$(DEPDIR)/ssl_verify.Po
-rm -f ./$(DEPDIR)/ssl_verify_mbedtls.Po
@@ -939,6 +970,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/status.Po
-rm -f ./$(DEPDIR)/tls_crypt.Po
-rm -f ./$(DEPDIR)/tun.Po
+ -rm -f ./$(DEPDIR)/vlan.Po
-rm -f ./$(DEPDIR)/win32.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
@@ -986,6 +1018,7 @@ installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/argv.Po
+ -rm -f ./$(DEPDIR)/auth_token.Po
-rm -f ./$(DEPDIR)/base64.Po
-rm -f ./$(DEPDIR)/block_dns.Po
-rm -f ./$(DEPDIR)/buffer.Po
@@ -1001,6 +1034,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/crypto_openssl.Po
-rm -f ./$(DEPDIR)/cryptoapi.Po
-rm -f ./$(DEPDIR)/dhcp.Po
+ -rm -f ./$(DEPDIR)/env_set.Po
-rm -f ./$(DEPDIR)/error.Po
-rm -f ./$(DEPDIR)/event.Po
-rm -f ./$(DEPDIR)/fdmisc.Po
@@ -1024,6 +1058,8 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/mtu.Po
-rm -f ./$(DEPDIR)/mudp.Po
-rm -f ./$(DEPDIR)/multi.Po
+ -rm -f ./$(DEPDIR)/networking_iproute2.Po
+ -rm -f ./$(DEPDIR)/networking_sitnl.Po
-rm -f ./$(DEPDIR)/ntlm.Po
-rm -f ./$(DEPDIR)/occ.Po
-rm -f ./$(DEPDIR)/openvpn.Po
@@ -1045,6 +1081,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/push.Po
-rm -f ./$(DEPDIR)/reliable.Po
-rm -f ./$(DEPDIR)/route.Po
+ -rm -f ./$(DEPDIR)/run_command.Po
-rm -f ./$(DEPDIR)/schedule.Po
-rm -f ./$(DEPDIR)/session_id.Po
-rm -f ./$(DEPDIR)/shaper.Po
@@ -1053,6 +1090,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/socks.Po
-rm -f ./$(DEPDIR)/ssl.Po
-rm -f ./$(DEPDIR)/ssl_mbedtls.Po
+ -rm -f ./$(DEPDIR)/ssl_ncp.Po
-rm -f ./$(DEPDIR)/ssl_openssl.Po
-rm -f ./$(DEPDIR)/ssl_verify.Po
-rm -f ./$(DEPDIR)/ssl_verify_mbedtls.Po
@@ -1060,6 +1098,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/status.Po
-rm -f ./$(DEPDIR)/tls_crypt.Po
-rm -f ./$(DEPDIR)/tun.Po
+ -rm -f ./$(DEPDIR)/vlan.Po
-rm -f ./$(DEPDIR)/win32.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c
index 7d06951..2c61e66 100644
--- a/src/openvpn/argv.c
+++ b/src/openvpn/argv.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..1b02714 100644
--- a/src/openvpn/argv.h
+++ b/src/openvpn/argv.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..0ea6d18
--- /dev/null
+++ b/src/openvpn/auth_token.c
@@ -0,0 +1,410 @@
+#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;
+ }
+
+ int auth_token_state_flags = session->key[KS_PRIMARY].auth_token_state_flags;
+
+ const char *state;
+
+ if (!is_auth_token(up->password))
+ {
+ state = "Initial";
+ }
+ else if (auth_token_state_flags & AUTH_TOKEN_HMAC_OK)
+ {
+ switch (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 (auth_token_state_flags & AUTH_TOKEN_HMAC_OK
+ && !(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
+ */
+ struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
+ if (ks->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 *) &timestamp, 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, &timestamp, 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..73a00dd
--- /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-2021 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/basic.h b/src/openvpn/basic.h
index eb9f211..6372e62 100644
--- a/src/openvpn/basic.h
+++ b/src/openvpn/basic.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/block_dns.c b/src/openvpn/block_dns.c
index 889d6bb..b2af457 100644
--- a/src/openvpn/block_dns.c
+++ b/src/openvpn/block_dns.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* 2015-2016 <iam@valdikss.org.ru>
* 2016 Selva Nair <selva.nair@gmail.com>
*
@@ -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..78e5e5d 100644
--- a/src/openvpn/block_dns.h
+++ b/src/openvpn/block_dns.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ * Copyright (C) 2016-2021 Selva Nair <selva.nair@gmail.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
@@ -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..c82d3d4 100644
--- a/src/openvpn/buffer.c
+++ b/src/openvpn/buffer.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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;
@@ -647,7 +709,6 @@ string_alloc(const char *str, struct gc_arena *gc)
*/
#ifdef DMALLOC
ret = openvpn_dmalloc(file, line, n);
- memset(ret, 0, n);
#else
ret = calloc(1, n);
#endif
@@ -1335,3 +1396,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..fc7909b 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/circ_list.h b/src/openvpn/circ_list.h
index 23b42d2..d9fd2e2 100644
--- a/src/openvpn/circ_list.h
+++ b/src/openvpn/circ_list.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
index b08fd54..2dd55f5 100644
--- a/src/openvpn/clinat.c
+++ b/src/openvpn/clinat.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
index eec7a03..a7725f1 100644
--- a/src/openvpn/clinat.h
+++ b/src/openvpn/clinat.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/common.h b/src/openvpn/common.h
index 0f73200..e1a2cde 100644
--- a/src/openvpn/common.h
+++ b/src/openvpn/common.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..3cb427e 100644
--- a/src/openvpn/comp-lz4.c
+++ b/src/openvpn/comp-lz4.c
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
- * Copyright (C) 2013-2018 Gert Doering <gert@greenie.muc.de>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2013-2021 Gert Doering <gert@greenie.muc.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
@@ -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-lz4.h b/src/openvpn/comp-lz4.h
index 8c1ca3a..f02d46f 100644
--- a/src/openvpn/comp-lz4.h
+++ b/src/openvpn/comp-lz4.h
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
- * Copyright (C) 2013-2018 Gert Doering <gert@greenie.muc.de>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2013-2021 Gert Doering <gert@greenie.muc.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
diff --git a/src/openvpn/comp.c b/src/openvpn/comp.c
index a945913..72b1511 100644
--- a/src/openvpn/comp.c
+++ b/src/openvpn/comp.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..dfd70bb 100644
--- a/src/openvpn/comp.h
+++ b/src/openvpn/comp.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/compstub.c b/src/openvpn/compstub.c
index 9123541..a65e8ea 100644
--- a/src/openvpn/compstub.c
+++ b/src/openvpn/compstub.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/console.c b/src/openvpn/console.c
index 4d49722..9bf9ef1 100644
--- a/src/openvpn/console.c
+++ b/src/openvpn/console.c
@@ -5,9 +5,9 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
- * Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net>
+ * Copyright (C) 2016-2021 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
diff --git a/src/openvpn/console.h b/src/openvpn/console.h
index 5a70e5f..2994c23 100644
--- a/src/openvpn/console.h
+++ b/src/openvpn/console.h
@@ -5,9 +5,9 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
- * Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net>
+ * Copyright (C) 2016-2021 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
@@ -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_builtin.c b/src/openvpn/console_builtin.c
index 445928b..3a977ee 100644
--- a/src/openvpn/console_builtin.c
+++ b/src/openvpn/console_builtin.c
@@ -5,9 +5,9 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
- * Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net>
+ * Copyright (C) 2016-2021 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
@@ -58,78 +58,77 @@
static bool
get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity)
{
- HANDLE in = INVALID_HANDLE_VALUE;
- HANDLE err = INVALID_HANDLE_VALUE;
- DWORD len = 0;
-
ASSERT(prompt);
ASSERT(input);
ASSERT(capacity > 0);
input[0] = '\0';
- in = GetStdHandle(STD_INPUT_HANDLE);
- err = get_orig_stderr();
-
- if (in != INVALID_HANDLE_VALUE
- && err != INVALID_HANDLE_VALUE
- && !win32_service_interrupt(&win32_signal)
- && WriteFile(err, prompt, strlen(prompt), &len, NULL))
+ HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
+ int orig_stderr = get_orig_stderr(); // guaranteed to be always valid
+ if ((in == INVALID_HANDLE_VALUE)
+ || win32_service_interrupt(&win32_signal)
+ || (_write(orig_stderr, prompt, strlen(prompt)) == -1))
{
- bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
- DWORD flags_save = 0;
- int status = 0;
- WCHAR *winput;
+ msg(M_WARN|M_ERRNO, "get_console_input_win32(): unexpected error");
+ return false;
+ }
- if (is_console)
- {
- if (GetConsoleMode(in, &flags_save))
- {
- DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
- if (echo)
- {
- flags |= ENABLE_ECHO_INPUT;
- }
- SetConsoleMode(in, flags);
- }
- else
- {
- is_console = 0;
- }
- }
+ bool is_console = (GetFileType(in) == FILE_TYPE_CHAR);
+ DWORD flags_save = 0;
+ int status = 0;
+ WCHAR *winput;
- if (is_console)
+ if (is_console)
+ {
+ if (GetConsoleMode(in, &flags_save))
{
- winput = malloc(capacity * sizeof(WCHAR));
- if (winput == NULL)
+ DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+ if (echo)
{
- return false;
+ flags |= ENABLE_ECHO_INPUT;
}
-
- status = ReadConsoleW(in, winput, capacity, &len, NULL);
- WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
- free(winput);
+ SetConsoleMode(in, flags);
}
else
{
- status = ReadFile(in, input, capacity, &len, NULL);
+ is_console = 0;
}
+ }
- string_null_terminate(input, (int)len, capacity);
- chomp(input);
+ DWORD len = 0;
- if (!echo)
- {
- WriteFile(err, "\r\n", 2, &len, NULL);
- }
- if (is_console)
- {
- SetConsoleMode(in, flags_save);
- }
- if (status && !win32_service_interrupt(&win32_signal))
+ if (is_console)
+ {
+ winput = malloc(capacity * sizeof(WCHAR));
+ if (winput == NULL)
{
- return true;
+ return false;
}
+
+ status = ReadConsoleW(in, winput, capacity, &len, NULL);
+ WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL);
+ free(winput);
+ }
+ else
+ {
+ status = ReadFile(in, input, capacity, &len, NULL);
+ }
+
+ string_null_terminate(input, (int)len, capacity);
+ chomp(input);
+
+ if (!echo)
+ {
+ _write(orig_stderr, "\r\n", 2);
+ }
+ if (is_console)
+ {
+ SetConsoleMode(in, flags_save);
+ }
+ if (status && !win32_service_interrupt(&win32_signal))
+ {
+ return true;
}
return false;
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..619cd96 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..93c33c1 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..b5e3bd9 100644
--- a/src/openvpn/crypto_backend.h
+++ b/src/openvpn/crypto_backend.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..8f0a283 100644
--- a/src/openvpn/crypto_mbedtls.c
+++ b/src/openvpn/crypto_mbedtls.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..019de01 100644
--- a/src/openvpn/crypto_mbedtls.h
+++ b/src/openvpn/crypto_mbedtls.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..79fbab4 100644
--- a/src/openvpn/crypto_openssl.c
+++ b/src/openvpn/crypto_openssl.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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,13 @@ crypto_init_lib_engine(const char *engine_name)
void
crypto_init_lib(void)
{
+#ifndef _WIN32
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+#else
+ OPENSSL_config(NULL);
+#endif
+#endif /* _WIN32 */
/*
* If you build the OpenSSL library and OpenVPN with
* CRYPTO_MDEBUG, you will get a listing of OpenSSL
@@ -202,12 +211,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 +263,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 +275,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 +289,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 +303,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 +315,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 +328,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 +387,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 +588,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 +617,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 +650,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 +689,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 +710,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 +719,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 +798,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 +821,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 +829,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 +858,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 +865,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 +915,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 +944,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 +1051,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..59a31aa 100644
--- a/src/openvpn/crypto_openssl.h
+++ b/src/openvpn/crypto_openssl.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..47fe733 100644
--- a/src/openvpn/dhcp.c
+++ b/src/openvpn/dhcp.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/dhcp.h b/src/openvpn/dhcp.h
index 32aa15e..b2fe8a5 100644
--- a/src/openvpn/dhcp.h
+++ b/src/openvpn/dhcp.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/env_set.c b/src/openvpn/env_set.c
new file mode 100644
index 0000000..a410388
--- /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-2021 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
+ * Copyright (C) 2016-2021 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..f73dea6
--- /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-2021 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..4131cf0 100644
--- a/src/openvpn/errlevel.h
+++ b/src/openvpn/errlevel.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -91,6 +91,7 @@
#define D_OSBUF LOGLEV(3, 43, 0) /* show socket/tun/tap buffer sizes */
#define D_PS_PROXY LOGLEV(3, 44, 0) /* messages related to --port-share option */
#define D_PF_INFO LOGLEV(3, 45, 0) /* packet filter informational messages */
+#define D_IFCONFIG LOGLEV(3, 0, 0) /* show ifconfig info (don't mute) */
#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */
@@ -109,6 +110,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 +141,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 +149,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..10be3e0 100644
--- a/src/openvpn/error.c
+++ b/src/openvpn/error.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,
@@ -497,22 +498,12 @@ close_syslog(void)
}
#ifdef _WIN32
+static int orig_stderr;
-static HANDLE orig_stderr;
-
-HANDLE
-get_orig_stderr(void)
+int get_orig_stderr()
{
- if (orig_stderr)
- {
- return orig_stderr;
- }
- else
- {
- return GetStdHandle(STD_ERROR_HANDLE);
- }
+ return orig_stderr ? orig_stderr : _fileno(stderr);
}
-
#endif
void
@@ -556,16 +547,12 @@ redirect_stdout_stderr(const char *file, bool append)
}
/* save original stderr for password prompts */
- orig_stderr = GetStdHandle(STD_ERROR_HANDLE);
-
-#if 0 /* seems not be necessary with stdout/stderr redirection below*/
- /* set up for redirection */
- if (!SetStdHandle(STD_OUTPUT_HANDLE, log_handle)
- || !SetStdHandle(STD_ERROR_HANDLE, log_handle))
+ orig_stderr = _dup(_fileno(stderr));
+ if (orig_stderr == -1)
{
- msg(M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file);
+ msg(M_WARN | M_ERRNO, "Warning: cannot duplicate stderr, password prompts will appear in log file instead of console.");
+ orig_stderr = _fileno(stderr);
}
-#endif
/* direct stdout/stderr to point to log_handle */
log_fd = _open_osfhandle((intptr_t)log_handle, _O_TEXT);
@@ -687,7 +674,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 +724,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/error.h b/src/openvpn/error.h
index eaedf17..bd15282 100644
--- a/src/openvpn/error.h
+++ b/src/openvpn/error.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -267,8 +267,8 @@ void close_syslog(void);
void redirect_stdout_stderr(const char *file, bool append);
#ifdef _WIN32
-/* get original stderr handle, even if redirected by --log/--log-append */
-HANDLE get_orig_stderr(void);
+/* get original stderr fd, even if redirected by --log/--log-append */
+int get_orig_stderr(void);
#endif
diff --git a/src/openvpn/event.c b/src/openvpn/event.c
index b22741f..fcddeb1 100644
--- a/src/openvpn/event.c
+++ b/src/openvpn/event.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/event.h b/src/openvpn/event.h
index 4af6371..5b6647f 100644
--- a/src/openvpn/event.h
+++ b/src/openvpn/event.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/fdmisc.c b/src/openvpn/fdmisc.c
index 1cea505..729bdb3 100644
--- a/src/openvpn/fdmisc.c
+++ b/src/openvpn/fdmisc.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/fdmisc.h b/src/openvpn/fdmisc.h
index 0fb8b93..86957f0 100644
--- a/src/openvpn/fdmisc.h
+++ b/src/openvpn/fdmisc.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
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..042ba9e 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,16 +524,15 @@ 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.
+ * has not yet succeeded. In non-TLS mode tls_multi is not defined
+ * and we always pass packets.
*/
- if (c->c2.context_auth != CAS_SUCCEEDED)
+ if (c->c2.tls_multi && c->c2.tls_multi->multi_state != CAS_SUCCEEDED)
{
c->c2.buf.len = 0;
}
-#endif
if (comp_frag)
{
@@ -485,7 +551,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 +583,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 +600,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 +664,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 +684,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 +695,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 +724,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 +732,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 +835,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 +933,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 +950,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 +971,16 @@ 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. In non-TLS mode tls_multi is not defined
+ * and we always pass packets.
*/
- if (c->c2.context_auth != CAS_SUCCEEDED)
+ if (c->c2.tls_multi && c->c2.tls_multi->multi_state != 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 +992,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 +1040,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 +1065,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 +1114,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 +1292,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 +1305,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 +1318,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, &ethhdr, sizeof(struct openvpn_ethhdr)));
+ }
+#undef MAX_ICMPV6LEN
+}
+
void
process_ip_header(struct context *c, unsigned int flags, struct buffer *buf)
{
@@ -1243,6 +1475,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 +1514,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 +1532,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 +1675,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 +1682,6 @@ process_outgoing_link(struct context *c)
msg(M_INFO, "Network unreachable, restarting");
register_signal(c, SIGUSR1, "network-unreachable");
}
-#endif
}
else
{
@@ -1481,7 +1724,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 +1751,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 +1828,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 +1841,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 +1983,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..5585366 100644
--- a/src/openvpn/forward.h
+++ b/src/openvpn/forward.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..4f8bd0f 100644
--- a/src/openvpn/fragment.c
+++ b/src/openvpn/fragment.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/fragment.h b/src/openvpn/fragment.h
index 6fa9692..6815446 100644
--- a/src/openvpn/fragment.h
+++ b/src/openvpn/fragment.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/gremlin.c b/src/openvpn/gremlin.c
index 114cb19..23ce3f0 100644
--- a/src/openvpn/gremlin.c
+++ b/src/openvpn/gremlin.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/gremlin.h b/src/openvpn/gremlin.h
index 22c90b9..52f65ac 100644
--- a/src/openvpn/gremlin.h
+++ b/src/openvpn/gremlin.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/helper.c b/src/openvpn/helper.c
index ff9df50..67131b5 100644
--- a/src/openvpn/helper.c
+++ b/src/openvpn/helper.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/helper.h b/src/openvpn/helper.h
index 866a398..084bf38 100644
--- a/src/openvpn/helper.h
+++ b/src/openvpn/helper.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/httpdigest.c b/src/openvpn/httpdigest.c
index 7cf74fd..26b0ed1 100644
--- a/src/openvpn/httpdigest.c
+++ b/src/openvpn/httpdigest.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/httpdigest.h b/src/openvpn/httpdigest.h
index 959220f..75c465b 100644
--- a/src/openvpn/httpdigest.h
+++ b/src/openvpn/httpdigest.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 8bac74f..27c6cac 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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 + strlen(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);
@@ -454,6 +500,17 @@ next_connection_entry(struct context *c)
*/
if (!c->options.persist_remote_ip)
{
+ /* Connection entry addrinfo objects might have been
+ * resolved earlier but the entry itself might have been
+ * skipped by management on the previous loop.
+ * If so, clear the addrinfo objects as close_instance does
+ */
+ if (c->c1.link_socket_addr.remote_list)
+ {
+ clear_remote_addrlist(&c->c1.link_socket_addr,
+ !c->options.resolve_in_advance);
+ }
+
/* close_instance should have cleared the addrinfo objects */
ASSERT(c->c1.link_socket_addr.current_remote == NULL);
ASSERT(c->c1.link_socket_addr.remote_list == NULL);
@@ -529,19 +586,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 +675,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 +782,7 @@ init_static(void)
{
/* configure_path (); */
-#if defined(ENABLE_CRYPTO) && defined(DMALLOC)
+#if defined(DMALLOC)
crypto_init_dmalloc();
#endif
@@ -768,14 +819,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 +887,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 +900,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 +1009,7 @@ init_static(void)
void
uninit_static(void)
{
-#ifdef ENABLE_CRYPTO
free_ssl_lib();
-#endif
#ifdef ENABLE_PKCS11
pkcs11_terminate();
@@ -981,7 +1019,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 +1052,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 +1063,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 +1090,6 @@ print_openssl_info(const struct options *options)
}
return true;
}
-#endif /* ifdef ENABLE_CRYPTO */
return false;
}
@@ -1063,35 +1099,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 +1188,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,10 +1198,11 @@ 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);
+ set_lladdr(ctx, options->dev, options->lladdr, NULL);
}
return true;
#else /* ifdef ENABLE_FEATURE_TUN_PERSIST */
@@ -1122,7 +1210,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 +1342,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 +1430,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 +1443,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 +1453,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 +1499,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 +1524,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 +1536,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 +1573,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 +1597,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
@@ -1523,9 +1606,8 @@ initialization_sequence_completed(struct context *c, const unsigned int flags)
*/
if (c->options.mode == MODE_POINT_TO_POINT)
{
- delayed_auth_pass_purge();
+ ssl_clean_user_pass();
}
-#endif /* ENABLE_CRYPTO */
/* Test if errors */
if (flags & ISC_ERRORS)
@@ -1628,11 +1710,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 +1740,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 +1774,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 +1822,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 +1833,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 +1852,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 +1861,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 */
@@ -1783,14 +1874,16 @@ do_open_tun(struct context *c)
/* set the hardware address */
if (c->options.lladdr)
{
- set_lladdr(c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es);
+ set_lladdr(&c->net_ctx, c->c1.tuntap->actual_name, c->options.lladdr,
+ c->c2.es);
}
/* do ifconfig */
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 +1919,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 +1989,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 +2048,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 +2264,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 +2289,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 +2301,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 +2354,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 +2377,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)
+ if (!check_pull_client_ncp(c, found))
{
- msg(D_PUSH, "OPTIONS IMPORT: data channel crypto options modified");
- }
- else if (c->options.ncp_enabled)
- {
- tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername);
+ return false;
}
struct frame *frame_fragment = NULL;
#ifdef ENABLE_FRAGMENT
@@ -2302,6 +2389,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 +2397,7 @@ do_deferred_options(struct context *c, const unsigned int found)
return false;
}
}
-#endif /* ifdef ENABLE_CRYPTO */
+
return true;
}
@@ -2368,8 +2456,9 @@ socket_restart_pause(struct context *c)
}
#endif
- /* Slow down reconnection after 5 retries per remote -- for tcp only in client mode */
- if (c->options.ce.proto != PROTO_TCP_SERVER)
+ /* Slow down reconnection after 5 retries per remote -- for TCP client or UDP tls-client only */
+ if (c->options.ce.proto == PROTO_TCP_CLIENT
+ || (c->options.ce.proto == PROTO_UDP && c->options.tls_client))
{
backoff = (c->options.unsuccessful_attempts / c->options.connection_list->len) - 4;
if (backoff > 0)
@@ -2394,7 +2483,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 +2550,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->auth_token_key);
}
-#endif /* ENABLE_CRYPTO */
CLEAR(*ks);
}
-#ifdef ENABLE_CRYPTO
-
static void
init_crypto_pre(struct context *c, const unsigned int flags)
{
@@ -2497,7 +2582,6 @@ init_crypto_pre(struct context *c, const unsigned int flags)
rand_ctx_enable_prediction_resistance();
}
#endif
-
}
/*
@@ -2512,11 +2596,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 +2636,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 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);
+}
+
+/*
+ * 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);
+ }
+ }
+
+
+}
+
+/*
+ * 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);
}
/*
@@ -2581,7 +2737,7 @@ do_init_crypto_tls_c1(struct context *c)
* Initialize the OpenSSL library's global
* SSL context.
*/
- init_ssl(options, &(c->c1.ks.ssl_ctx));
+ init_ssl(options, &(c->c1.ks.ssl_ctx), c->c0 && c->c0->uid_gid_chroot_set);
if (!tls_ctx_initialised(&c->c1.ks.ssl_ctx))
{
#if P2MP
@@ -2605,45 +2761,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);
- }
-
- 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");
- }
+ /* initialize tls-auth/crypt/crypt-v2 key */
+ do_init_tls_wrap_key(c);
- /* 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 +2792,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 +2823,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 +2838,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 +2859,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 +2900,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 +2908,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 +2928,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 +2940,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 +2950,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 +2965,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 +2981,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 +3070,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 +3086,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 +3179,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 +3198,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 +3261,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 +3281,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 +3304,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 +3326,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 +3341,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 +3366,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 +3452,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 +3483,6 @@ do_print_data_channel_mtu_parms(struct context *c)
#endif
}
-#ifdef ENABLE_OCC
/*
* Get local and remote options compatibility strings.
*/
@@ -3353,9 +3492,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 +3505,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 +3583,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 +3599,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 +3613,16 @@ 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(&c->c1.ks.tls_crypt_v2_server_key);
+ 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);
@@ -3505,7 +3649,8 @@ do_close_link_socket(struct context *c)
&& ( (c->options.persist_remote_ip)
||
( c->sig->source != SIG_SOURCE_HARD
- && ((c->c1.link_socket_addr.current_remote && c->c1.link_socket_addr.current_remote->ai_next)
+ && ((c->c1.link_socket_addr.current_remote
+ && c->c1.link_socket_addr.current_remote->ai_next)
|| c->options.no_advance))
)))
{
@@ -3530,19 +3675,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 +3770,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 +3795,6 @@ do_close_ifconfig_pool_persist(struct context *c)
c->c1.ifconfig_pool_persist_owned = false;
}
}
-#endif
}
/*
@@ -3721,7 +3860,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 +3868,6 @@ do_signal_on_tls_errors(struct context *c)
{
c->c2.tls_exit_signal = SIGUSR1;
}
-#endif
}
#ifdef ENABLE_PLUGIN
@@ -3880,6 +4017,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 +4259,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 +4363,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 +4375,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 +4505,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 +4546,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 +4632,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 +4688,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 +4756,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 +4773,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..52581f8 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..0761475 100644
--- a/src/openvpn/integer.h
+++ b/src/openvpn/integer.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/interval.c b/src/openvpn/interval.c
index b728560..d06b6e5 100644
--- a/src/openvpn/interval.c
+++ b/src/openvpn/interval.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/interval.h b/src/openvpn/interval.h
index 5623f3a..3ba197c 100644
--- a/src/openvpn/interval.h
+++ b/src/openvpn/interval.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/list.c b/src/openvpn/list.c
index 09e393a..c453726 100644
--- a/src/openvpn/list.c
+++ b/src/openvpn/list.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..0435414 100644
--- a/src/openvpn/list.h
+++ b/src/openvpn/list.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..3ddbebb 100644
--- a/src/openvpn/lladdr.c
+++ b/src/openvpn/lladdr.c
@@ -11,12 +11,13 @@
#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,
+set_lladdr(openvpn_net_ctx_t *ctx, const char *ifname, const char *lladdr,
const struct env_set *es)
{
- struct argv argv = argv_new();
int r;
if (!ifname || !lladdr)
@@ -25,17 +26,13 @@ set_lladdr(const char *ifname, const char *lladdr,
}
#if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
- argv_printf(&argv,
- "%s link set addr %s dev %s",
- iproute_path, lladdr, ifname);
-#else
- argv_printf(&argv,
- "%s %s hw ether %s",
- IFCONFIG_PATH,
- ifname, lladdr);
-#endif
-#elif defined(TARGET_SOLARIS)
+ uint8_t addr[ETH_ALEN];
+
+ sscanf(lladdr, MAC_FMT, MAC_SCAN_ARG(addr));
+ r = (net_addr_ll_set(ctx, ifname, addr) == 0);
+#else /* if defined(TARGET_LINUX) */
+ struct argv argv = argv_new();
+#if defined(TARGET_SOLARIS)
argv_printf(&argv,
"%s %s ether %s",
IFCONFIG_PATH,
@@ -55,18 +52,19 @@ set_lladdr(const char *ifname, const char *lladdr,
"%s %s ether %s",
IFCONFIG_PATH,
ifname, lladdr);
-#else /* if defined(TARGET_LINUX) */
+#else /* if defined(TARGET_SOLARIS) */
msg(M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
return -1;
-#endif /* if defined(TARGET_LINUX) */
-
+#endif /* if defined(TARGET_SOLARIS) */
argv_msg(M_INFO, &argv);
r = openvpn_execve_check(&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
+ argv_free(&argv);
+#endif /* if defined(TARGET_LINUX) */
+
if (r)
{
msg(M_INFO, "TUN/TAP link layer address set to %s", lladdr);
}
- argv_reset(&argv);
return r;
}
diff --git a/src/openvpn/lladdr.h b/src/openvpn/lladdr.h
index f6ea2b1..0c8b416 100644
--- a/src/openvpn/lladdr.h
+++ b/src/openvpn/lladdr.h
@@ -3,6 +3,7 @@
*/
#include "misc.h"
+#include "networking.h"
-int set_lladdr(const char *ifname, const char *lladdr,
+int set_lladdr(openvpn_net_ctx_t *ctx, const char *ifname, const char *lladdr,
const struct env_set *es);
diff --git a/src/openvpn/lzo.c b/src/openvpn/lzo.c
index 8d9efea..0188814 100644
--- a/src/openvpn/lzo.c
+++ b/src/openvpn/lzo.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d19d602 100644
--- a/src/openvpn/lzo.h
+++ b/src/openvpn/lzo.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..c831f8a 100644
--- a/src/openvpn/manage.c
+++ b/src/openvpn/manage.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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))
{
@@ -2021,7 +2102,7 @@ man_io_error(struct management *man, const char *prefix)
static ssize_t
man_send_with_fd(int fd, void *ptr, size_t nbytes, int flags, int sendfd)
{
- struct msghdr msg;
+ struct msghdr msg = { 0 };
struct iovec iov[1];
union {
@@ -2053,7 +2134,7 @@ man_send_with_fd(int fd, void *ptr, size_t nbytes, int flags, int sendfd)
static ssize_t
man_recv_with_fd(int fd, void *ptr, size_t nbytes, int flags, int *recvfd)
{
- struct msghdr msghdr;
+ struct msghdr msghdr = { 0 };
struct iovec iov[1];
ssize_t n;
@@ -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)
@@ -3203,12 +3310,17 @@ man_block(struct management *man, volatile int *signal_received, const time_t ex
if (man_standalone_ok(man))
{
+ /* expire time can be already overdue, for this case init zero
+ * timeout to avoid waiting first time and exit loop early with
+ * either obtained event or timeout.
+ */
+ tv.tv_usec = 0;
+ tv.tv_sec = 0;
+
while (true)
{
event_reset(man->connection.es);
management_socket_set(man, man->connection.es, NULL, NULL);
- tv.tv_usec = 0;
- tv.tv_sec = 1;
if (man_check_for_signals(signal_received))
{
status = -1;
@@ -3236,6 +3348,10 @@ man_block(struct management *man, volatile int *signal_received, const time_t ex
}
break;
}
+
+ /* wait one second more */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
}
}
return status;
@@ -3337,7 +3453,7 @@ management_event_loop_n_seconds(struct management *man, int sec)
/* set expire time */
update_time();
- if (sec)
+ if (sec >= 0)
{
expire = now + sec;
}
@@ -3367,7 +3483,7 @@ management_event_loop_n_seconds(struct management *man, int sec)
/* revert state */
man->persist.standalone_disabled = standalone_disabled_save;
}
- else
+ else if (sec > 0)
{
sleep(sec);
}
@@ -3394,9 +3510,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 +3540,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 +3555,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)
@@ -3503,7 +3613,6 @@ management_query_user_pass(struct management *man,
{
/* preserve caller's settings */
man->connection.up_query.nocache = up->nocache;
- man->connection.up_query.wait_for_push = up->wait_for_push;
*up = man->connection.up_query;
}
secure_memzero(&man->connection.up_query, sizeof(man->connection.up_query));
@@ -3513,8 +3622,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 +3758,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 +3800,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
*/
@@ -4002,11 +4125,15 @@ log_history_ref(const struct log_history *h, const int index)
void
management_sleep(const int n)
{
- if (management)
+ if (n < 0)
+ {
+ return;
+ }
+ else if (management)
{
management_event_loop_n_seconds(management, n);
}
- else
+ else if (n > 0)
{
sleep(n);
}
@@ -4017,7 +4144,10 @@ management_sleep(const int n)
void
management_sleep(const int n)
{
- sleep(n);
+ if (n > 0)
+ {
+ sleep(n);
+ }
}
#endif /* ENABLE_MANAGEMENT */
diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h
index f286754..3c9028f 100644
--- a/src/openvpn/manage.h
+++ b/src/openvpn/manage.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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.c b/src/openvpn/mbuf.c
index 87faff0..1032f23 100644
--- a/src/openvpn/mbuf.c
+++ b/src/openvpn/mbuf.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/mbuf.h b/src/openvpn/mbuf.h
index 4912c95..ea2bfe3 100644
--- a/src/openvpn/mbuf.h
+++ b/src/openvpn/mbuf.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..69aac00 100644
--- a/src/openvpn/memdbg.h
+++ b/src/openvpn/memdbg.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..046d937 100644
--- a/src/openvpn/misc.c
+++ b/src/openvpn/misc.c
@@ -5,9 +5,9 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
- * Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net>
+ * Copyright (C) 2016-2021 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
@@ -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)
@@ -1324,17 +510,49 @@ void
set_auth_token(struct user_pass *up, struct user_pass *tk, const char *token)
{
- if (token && strlen(token) && up && up->defined)
+ if (strlen(token))
{
strncpynt(tk->password, token, USER_PASS_LEN);
- strncpynt(tk->username, up->username, USER_PASS_LEN);
- tk->defined = true;
+ tk->token_defined = true;
+
+ /*
+ * --auth-token has no username, so it needs the username
+ * either already set or copied from up, or later set by
+ * --auth-token-user
+ *
+ * Do not overwrite the username if already set to avoid
+ * overwriting an username set by --auth-token-user
+ */
+ if (up->defined && !tk->defined)
+ {
+ strncpynt(tk->username, up->username, USER_PASS_LEN);
+ tk->defined = true;
+ }
}
/* Cleans user/pass for nocache */
purge_user_pass(up, false);
}
+void
+set_auth_token_user(struct user_pass *tk, const char *username)
+{
+ if (strlen(username))
+ {
+ /* Clear the username before decoding to ensure no old material is left
+ * and also allow decoding to not use all space to ensure the last byte is
+ * always 0 */
+ CLEAR(tk->username);
+ int len = openvpn_base64_decode(username, tk->username, USER_PASS_LEN - 1);
+ tk->defined = len > 0;
+ if (!tk->defined)
+ {
+ msg(D_PUSH, "Error decoding auth-token-username");
+ }
+ }
+}
+
+
/*
* Process string received by untrusted peer before
* printing to console or log file.
@@ -1347,71 +565,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 +643,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 +732,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 +795,33 @@ 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;
+}
+
+struct buffer
+prepend_dir(const char *dir, const char *path, struct gc_arena *gc)
+{
+ size_t len = strlen(dir) + strlen(PATH_SEPARATOR_STR) + strlen(path) + 1;
+ struct buffer combined_path = alloc_buf_gc(len, gc);
+ buf_printf(&combined_path, "%s%s%s", dir, PATH_SEPARATOR_STR, path);
+ ASSERT(combined_path.len > 0);
+
+ return combined_path;
+}
diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h
index 8a34f43..ef94ca1 100644
--- a/src/openvpn/misc.h
+++ b/src/openvpn/misc.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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);
/*
@@ -177,8 +63,10 @@ const char *hostname_randomize(const char *hostname, struct gc_arena *gc);
struct user_pass
{
bool defined;
+ /* For auth-token username and token can be set individually, so we
+ * use this second bool to track if the token (password) is defined */
+ bool token_defined;
bool nocache;
- bool wait_for_push; /* true if this object is waiting for a push-reply */
/* max length of username/password */
#ifdef ENABLE_PKCS11
@@ -190,7 +78,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 +104,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
@@ -259,9 +147,32 @@ void fail_user_pass(const char *prefix,
void purge_user_pass(struct user_pass *up, const bool force);
+/**
+ * Sets the auth-token to token. If a username is available from
+ * either up or already present in tk that will be used as default
+ * username for the token. The method will also purge up if
+ * the auth-nocache option is active.
+ *
+ * @param up (non Auth-token) Username/password
+ * @param tk auth-token userpass to set
+ * @param token token to use as password for the auth-token
+ *
+ * @note all parameters to this function must not be null.
+ */
void set_auth_token(struct user_pass *up, struct user_pass *tk,
const char *token);
+/**
+ * Sets the auth-token username by base64 decoding the passed
+ * username
+ *
+ * @param tk auth-token userpass to set
+ * @param username base64 encoded username to set
+ *
+ * @note all parameters to this function must not be null.
+ */
+void set_auth_token_user(struct user_pass *tk, const char *username);
+
/*
* Process string received by untrusted peer before
* printing to console or log file.
@@ -269,21 +180,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 +192,37 @@ 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);
+
+/**
+ * Prepend a directory to a path.
+ */
+struct buffer
+prepend_dir(const char *dir, const char *path, struct gc_arena *gc);
+
+#define _STRINGIFY(S) #S
+#define MAC_FMT _STRINGIFY(%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx)
+#define MAC_PRINT_ARG(_mac) _mac[0], _mac[1], _mac[2], \
+ _mac[3], _mac[4], _mac[5]
+#define MAC_SCAN_ARG(_mac) &_mac[0], &_mac[1], &_mac[2], \
+ &_mac[3], &_mac[4], &_mac[5]
#endif /* ifndef MISC_H */
diff --git a/src/openvpn/mroute.c b/src/openvpn/mroute.c
index db8c987..4e76fb0 100644
--- a/src/openvpn/mroute.c
+++ b/src/openvpn/mroute.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..8f7e092 100644
--- a/src/openvpn/mroute.h
+++ b/src/openvpn/mroute.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..aa5b68c 100644
--- a/src/openvpn/mss.c
+++ b/src/openvpn/mss.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/mss.h b/src/openvpn/mss.h
index 9350102..41254e2 100644
--- a/src/openvpn/mss.h
+++ b/src/openvpn/mss.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/mstats.c b/src/openvpn/mstats.c
index 281a835..1051e80 100644
--- a/src/openvpn/mstats.c
+++ b/src/openvpn/mstats.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/mstats.h b/src/openvpn/mstats.h
index 0d58cbf..0f710db 100644
--- a/src/openvpn/mstats.h
+++ b/src/openvpn/mstats.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c
index e8d2add..2b40ae8 100644
--- a/src/openvpn/mtcp.c
+++ b/src/openvpn/mtcp.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..716939a 100644
--- a/src/openvpn/mtcp.h
+++ b/src/openvpn/mtcp.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..3200a37 100644
--- a/src/openvpn/mtu.c
+++ b/src/openvpn/mtu.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d0df0ef 100644
--- a/src/openvpn/mtu.h
+++ b/src/openvpn/mtu.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d5459f8 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..2e071c2 100644
--- a/src/openvpn/mudp.h
+++ b/src/openvpn/mudp.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..66f5ada 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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.tls_multi->multi_state == 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,14 +782,13 @@ 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))
{
goto err;
}
- mi->context.c2.context_auth = CAS_PENDING;
+ mi->context.c2.tls_multi->multi_state = CAS_PENDING;
if (hash_n_elements(m->hash) >= m->max_clients)
{
@@ -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.tls_multi->multi_state = CAS_SUCCEEDED;
+
+ /* authentication complete, calculate dynamic client specific options */
+ if (!multi_client_set_protocol_options(&mi->context))
+ {
+ mi->context.c2.tls_multi->multi_state = 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.tls_multi->multi_state = 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.tls_multi->multi_state != 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.tls_multi->multi_state = 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.tls_multi->multi_state = 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.tls_multi->multi_state == 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.tls_multi->multi_state = 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.tls_multi->multi_state)
+ && 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.tls_multi->multi_state)
+ && 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.tls_multi->multi_state))
{
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.tls_multi->multi_state))
{
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..721b24f 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d43979f
--- /dev/null
+++ b/src/openvpn/networking.h
@@ -0,0 +1,305 @@
+/*
+ * Generic interface to platform specific networking code
+ *
+ * Copyright (C) 2016-2021 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);
+
+/**
+ * Set the Link Layer (Ethernet) address of the TAP interface
+ *
+ * @param ctx the implementation specific context
+ * @param iface the interface to modify
+ * @param addr the new address to set (expected ETH_ALEN bytes (6))
+ *
+ * @return 0 on success, a negative error code otherwise
+ */
+int net_addr_ll_set(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
+ uint8_t *addr);
+
+/**
+ * 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..67b8894
--- /dev/null
+++ b/src/openvpn/networking_iproute2.c
@@ -0,0 +1,407 @@
+/*
+ * Networking API implementation for iproute2
+ *
+ * Copyright (C) 2018-2021 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");
+
+ argv_free(&argv);
+
+ return 0;
+}
+
+int
+net_addr_ll_set(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
+ uint8_t *addr)
+{
+ struct argv argv = argv_new();
+ int ret = 0;
+
+ argv_printf(&argv,
+ "%s link set addr " MAC_FMT " dev %s",
+ iproute_path, MAC_PRINT_ARG(addr), iface);
+
+ argv_msg(M_INFO, &argv);
+ if (!openvpn_execve_check(&argv, ctx->es, M_WARN,
+ "Linux ip link set addr failed"))
+ {
+ ret = -1;
+ }
+
+ argv_free(&argv);
+
+ return ret;
+}
+
+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..8a1ab3a
--- /dev/null
+++ b/src/openvpn/networking_iproute2.h
@@ -0,0 +1,37 @@
+/*
+ * Generic interface to platform specific networking code
+ *
+ * Copyright (C) 2016-2021 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..8610e1d
--- /dev/null
+++ b/src/openvpn/networking_sitnl.c
@@ -0,0 +1,1339 @@
+/*
+ * Simplified Interface To NetLink
+ *
+ * Copyright (C) 2016-2021 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 "misc.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_DONE)
+ {
+ ret = 0;
+ goto out;
+ }
+
+ 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)
+ {
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ }
+ }
+ }
+ else
+ {
+ msg(M_WARN, "%s: rtnl: generic error (%d): %s",
+ __func__, err->error, strerror(-err->error));
+ ret = err->error;
+ }
+ }
+ goto out;
+ }
+
+ if (cb)
+ {
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ 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];
+ bool default_only;
+ unsigned int table;
+} 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 table, ifindex = 0;
+ void *gw = NULL;
+
+ /* filter-out non-zero dst prefixes */
+ if (res->default_only && r->rtm_dst_len != 0)
+ {
+ return 1;
+ }
+
+ /* route table, ignored with RTA_TABLE */
+ table = r->rtm_table;
+
+ 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:
+ gw = RTA_DATA(rta);
+ break;
+
+ /* route table */
+ case RTA_TABLE:
+ table = *(unsigned int *)RTA_DATA(rta);
+ break;
+ }
+
+ rta = RTA_NEXT(rta, len);
+ }
+
+ /* filter out any route not coming from the selected table */
+ if (res->table && res->table != table)
+ {
+ return 1;
+ }
+
+ if (!if_indextoname(ifindex, res->iface))
+ {
+ msg(M_WARN | M_ERRNO, "%s: rtnl: can't get ifname for index %d",
+ __func__, ifindex);
+ return -1;
+ }
+
+ if (gw)
+ {
+ memcpy(&res->gw, gw, res->addr_size);
+ }
+
+ 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);
+ /*
+ * kernel can't return 0.0.0.0/8 host route, dump all
+ * the routes and filter for 0.0.0.0/0 in cb()
+ */
+ if (!dst || !dst->ipv4)
+ {
+ req.n.nlmsg_flags |= NLM_F_DUMP;
+ res.default_only = true;
+ res.table = RT_TABLE_MAIN;
+ }
+ else
+ {
+ req.r.rtm_dst_len = 32;
+ }
+ break;
+
+ case AF_INET6:
+ res.addr_size = sizeof(struct in6_addr);
+ /* kernel can return ::/128 host route */
+ req.r.rtm_dst_len = 128;
+ 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;
+}
+
+int
+net_addr_ll_set(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
+ uint8_t *addr)
+{
+ 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_ADDRESS, addr, ETH_ALEN);
+
+ msg(M_INFO, "%s: lladdr " MAC_FMT " for %s", __func__, MAC_PRINT_ARG(addr),
+ 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..f040020
--- /dev/null
+++ b/src/openvpn/networking_sitnl.h
@@ -0,0 +1,28 @@
+/*
+ * Generic interface to platform specific networking code
+ *
+ * Copyright (C) 2016-2021 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..3a2bcab 100644
--- a/src/openvpn/occ.c
+++ b/src/openvpn/occ.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..067a658 100644
--- a/src/openvpn/occ.h
+++ b/src/openvpn/occ.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..3819d4c 100644
--- a/src/openvpn/openssl_compat.h
+++ b/src/openvpn/openssl_compat.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..0ac9614 100644
--- a/src/openvpn/openvpn.c
+++ b/src/openvpn/openvpn.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..ce0cd98 100644
--- a/src/openvpn/openvpn.h
+++ b/src/openvpn/openvpn.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,14 @@ struct context_1
#endif
};
+
+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 +309,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 +316,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 +327,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 +365,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 +419,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 +434,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,15 +447,6 @@ 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 */
-
struct event_timeout push_request_interval;
int n_sent_push_requests;
bool did_pre_pull_restore;
@@ -531,6 +511,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 +549,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 +556,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..2144775 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -1,105 +1,237 @@
<?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|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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)'=='Release|ARM64'" 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>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" 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)'=='Release|ARM64'" 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>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" 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)'=='Debug|ARM64'">
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>arm64-windows-ovpn</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>arm64-windows-ovpn</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>x86-windows-ovpn</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>x86-windows-ovpn</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>x64-windows-ovpn</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgTriplet>x64-windows-ovpn</VcpkgTriplet>
</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>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</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>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir)include;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <ClCompile>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</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>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</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>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</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>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</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>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</AdditionalDependencies>
+ <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <ClCompile>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <UndefinePreprocessorDefinitions>%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
+ <WarningLevel>Level2</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <AdditionalIncludeDirectories>..\compat;$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Ncrypt.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;setupapi.lib;Advapi32.lib</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 +244,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 +289,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 +298,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 +326,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 +354,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 +373,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 +385,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 +394,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 +410,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..a536ebe 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
- * Copyright (C) 2008-2013 David Sommerseth <dazo@users.sourceforge.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2008-2021 David Sommerseth <dazo@eurephia.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -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 (--genkey tls-crypt-v2-client): 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
+ SHOW_STR_INLINE(dh_file);
+#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
{
@@ -1969,7 +1983,25 @@ connection_entry_load_re(struct connection_entry *ce, const struct remote_entry
}
static void
-options_postprocess_verify_ce(const struct options *options, const struct connection_entry *ce)
+connection_entry_preload_key(const char **key_file, bool *key_inline,
+ struct gc_arena *gc)
+{
+ if (key_file && *key_file && !(*key_inline))
+ {
+ struct buffer in = buffer_read_from_file(*key_file, gc);
+ if (!buf_valid(&in))
+ {
+ msg(M_FATAL, "Cannot pre-load keyfile (%s)", *key_file);
+ }
+
+ *key_file = (const char *) in.data;
+ *key_inline = true;
+ }
+}
+
+static void
+options_postprocess_verify_ce(const struct options *options,
+ const struct connection_entry *ce)
{
struct options defaults;
int dev = DEV_TYPE_UNDEF;
@@ -1977,14 +2009,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
@@ -1997,7 +2029,9 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
*/
if (ce->proto == PROTO_TCP)
{
- msg(M_USAGE, "--proto tcp is ambiguous in this context. Please specify --proto tcp-server or --proto tcp-client");
+ msg(M_USAGE,
+ "--proto tcp is ambiguous in this context. Please specify "
+ "--proto tcp-server or --proto tcp-client");
}
/*
@@ -2025,10 +2059,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 +2069,12 @@ 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)
{
@@ -2049,15 +2086,15 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
*/
if (options->ce.tun_mtu_defined && options->ce.link_mtu_defined)
{
- msg(M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT);
+ 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
@@ -2078,18 +2115,23 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
if (string_defined_equal(ce->remote, options->ifconfig_local)
|| string_defined_equal(ce->remote, options->ifconfig_remote_netmask))
{
- msg(M_USAGE, "--local and --remote addresses must be distinct from --ifconfig addresses");
+ msg(M_USAGE,
+ "--local and --remote addresses must be distinct from --ifconfig "
+ "addresses");
}
if (string_defined_equal(ce->local, options->ifconfig_local)
|| string_defined_equal(ce->local, options->ifconfig_remote_netmask))
{
- msg(M_USAGE, "--local addresses must be distinct from --ifconfig addresses");
+ msg(M_USAGE,
+ "--local addresses must be distinct from --ifconfig addresses");
}
- if (string_defined_equal(options->ifconfig_local, options->ifconfig_remote_netmask))
+ if (string_defined_equal(options->ifconfig_local,
+ options->ifconfig_remote_netmask))
{
- msg(M_USAGE, "local and remote/netmask --ifconfig addresses must be different");
+ msg(M_USAGE,
+ "local and remote/netmask --ifconfig addresses must be different");
}
if (ce->bind_defined && !ce->bind_local)
@@ -2099,12 +2141,14 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
if (ce->local && !ce->bind_local)
{
- msg(M_USAGE, "--local and --nobind don't make sense when used together");
+ msg(M_USAGE,
+ "--local and --nobind don't make sense when used together");
}
if (ce->local_port_defined && !ce->bind_local)
{
- msg(M_USAGE, "--lport and --nobind don't make sense when used together");
+ msg(M_USAGE,
+ "--lport and --nobind don't make sense when used together");
}
if (!ce->remote && !ce->bind_local)
@@ -2138,8 +2182,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.
*/
@@ -2157,12 +2211,18 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
}
if (options->tuntap_options.dhcp_options
+ && 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)
{
- msg(M_USAGE, "--dhcp-options requires --ip-win32 dynamic or adaptive");
+ msg(M_USAGE, "--dhcp-option 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 +2235,11 @@ 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");
+ msg(M_USAGE,
+ "--explicit-exit-notify can only be used with --proto udp");
}
-#endif
if (!ce->remote && ce->proto == PROTO_TCP_CLIENT)
{
@@ -2189,16 +2248,21 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
if ((ce->http_proxy_options) && ce->proto != PROTO_TCP_CLIENT)
{
- msg(M_USAGE, "--http-proxy MUST be used in TCP Client mode (i.e. --proto tcp-client)");
+ msg(M_USAGE,
+ "--http-proxy MUST be used in TCP Client mode (i.e. --proto "
+ "tcp-client)");
}
+
if ((ce->http_proxy_options) && !ce->http_proxy_options->server)
{
- msg(M_USAGE, "--http-proxy not specified but other http proxy options present");
+ msg(M_USAGE,
+ "--http-proxy not specified but other http proxy options present");
}
if (ce->http_proxy_options && ce->socks_proxy_server)
{
- msg(M_USAGE, "--http-proxy can not be used together with --socks-proxy");
+ msg(M_USAGE,
+ "--http-proxy can not be used together with --socks-proxy");
}
if (ce->socks_proxy_server && ce->proto == PROTO_TCP_SERVER)
@@ -2211,13 +2275,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");
@@ -2263,8 +2328,9 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
{
msg(M_USAGE, "--socks-proxy cannot be used with --mode server");
}
- /* <connection> blocks force to have a remote embedded, so we check for the
- * --remote and bail out if it is present */
+ /* <connection> blocks force to have a remote embedded, so we check
+ * for the --remote and bail out if it is present
+ */
if (options->connection_list->len >1
|| options->connection_list->array[0]->remote)
{
@@ -2281,12 +2347,15 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
}
if (options->ipchange)
{
- msg(M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
+ msg(M_USAGE,
+ "--ipchange cannot be used with --mode server (use "
+ "--client-connect instead)");
}
if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCP_SERVER))
{
- msg(M_USAGE, "--mode server currently only supports "
- "--proto udp or --proto tcp-server or --proto tcp6-server");
+ msg(M_USAGE,
+ "--mode server currently only supports --proto udp or --proto "
+ "tcp-server or --proto tcp6-server");
}
if (!proto_is_udp(ce->proto) && (options->cf_max || options->cf_per))
{
@@ -2308,9 +2377,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 +2400,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 +2423,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 +2489,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 +2528,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 +2562,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 +2601,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 +2624,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 +2661,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 +2691,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 +2724,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 +2748,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 +2802,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 +2823,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 +2839,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 +2846,7 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
ce->proto = PROTO_TCP_SERVER;
}
}
-#endif
+
#if P2MP
if (o->client)
{
@@ -2795,12 +2857,14 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
}
#endif
- if (ce->proto == PROTO_TCP_CLIENT && !ce->local && !ce->local_port_defined && !ce->bind_defined)
+ if (ce->proto == PROTO_TCP_CLIENT && !ce->local
+ && !ce->local_port_defined && !ce->bind_defined)
{
ce->bind_local = false;
}
- if (ce->proto == PROTO_UDP && ce->socks_proxy_server && !ce->local && !ce->local_port_defined && !ce->bind_defined)
+ if (ce->proto == PROTO_UDP && ce->socks_proxy_server && !ce->local
+ && !ce->local_port_defined && !ce->bind_defined)
{
ce->bind_local = false;
}
@@ -2810,7 +2874,9 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
ce->local_port = NULL;
}
- /* if protocol forcing is enabled, disable all protocols except for the forced one */
+ /* if protocol forcing is enabled, disable all protocols
+ * except for the forced one
+ */
if (o->proto_force >= 0 && o->proto_force != ce->proto)
{
ce->flags |= CE_DISABLED;
@@ -2865,6 +2931,37 @@ 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(-v2) key file if persist-key was specified and
+ * keys were not already embedded in the config file.
+ */
+ if (o->persist_key)
+ {
+ connection_entry_preload_key(&ce->tls_auth_file,
+ &ce->tls_auth_file_inline, &o->gc);
+ connection_entry_preload_key(&ce->tls_crypt_file,
+ &ce->tls_crypt_file_inline, &o->gc);
+ connection_entry_preload_key(&ce->tls_crypt_v2_file,
+ &ce->tls_crypt_v2_file_inline, &o->gc);
+ }
}
#ifdef _WIN32
@@ -2900,9 +2997,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 +3023,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 +3039,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 +3072,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 +3144,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 +3189,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 +3202,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 +3226,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 +3243,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 +3254,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 +3304,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);
}
@@ -3173,14 +3328,8 @@ check_file_access_chroot(const char *chroot, const int type, const char *file, c
{
struct gc_arena gc = gc_new();
struct buffer chroot_file;
- int len = 0;
-
- /* Build up a new full path including chroot directory */
- len = strlen(chroot) + strlen(PATH_SEPARATOR_STR) + strlen(file) + 1;
- chroot_file = alloc_buf_gc(len, &gc);
- buf_printf(&chroot_file, "%s%s%s", chroot, PATH_SEPARATOR_STR, file);
- ASSERT(chroot_file.len > 0);
+ chroot_file = prepend_dir(chroot, file, &gc);
ret = check_file_access(type, BSTR(&chroot_file), mode, opt);
gc_free(&gc);
}
@@ -3192,6 +3341,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 +3422,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 +3436,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");
}
- 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");
+ 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_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 +3532,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.");
@@ -3383,6 +3591,14 @@ pre_pull_save(struct options *o)
o->pre_pull->client_nat = clone_client_nat_option_list(o->client_nat, &o->gc);
o->pre_pull->client_nat_defined = true;
}
+
+ o->pre_pull->route_default_gateway = o->route_default_gateway;
+ o->pre_pull->route_ipv6_default_gateway = o->route_ipv6_default_gateway;
+
+ /* Ping related options should be reset to the config values on reconnect */
+ o->pre_pull->ping_rec_timeout = o->ping_rec_timeout;
+ o->pre_pull->ping_rec_timeout_action = o->ping_rec_timeout_action;
+ o->pre_pull->ping_send_timeout = o->ping_send_timeout;
}
}
@@ -3418,6 +3634,9 @@ pre_pull_restore(struct options *o, struct gc_arena *gc)
o->routes_ipv6 = NULL;
}
+ o->route_default_gateway = pp->route_default_gateway;
+ o->route_ipv6_default_gateway = pp->route_ipv6_default_gateway;
+
if (pp->client_nat_defined)
{
cnol_check_alloc(o);
@@ -3429,6 +3648,10 @@ pre_pull_restore(struct options *o, struct gc_arena *gc)
}
o->foreign_option_index = pp->foreign_option_index;
+
+ o->ping_rec_timeout = pp->ping_rec_timeout;
+ o->ping_rec_timeout_action = pp->ping_rec_timeout_action;
+ o->ping_send_timeout = pp->ping_send_timeout;
}
o->push_continuation = 0;
@@ -3436,9 +3659,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 +3670,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 +3678,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 +3722,6 @@ calc_options_string_link_mtu(const struct options *o, const struct frame *frame)
* --keysize
* --secret
* --no-replay
- * --no-iv
*
* SSL Options:
*
@@ -3518,6 +3735,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 +3749,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 +3785,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 +3821,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 +3849,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 +3867,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 +3882,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 +3890,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 +3920,6 @@ options_string(const struct options *o,
#undef TLS_CLIENT
#undef TLS_SERVER
-#endif /* ENABLE_CRYPTO */
-
return BSTR(&out);
}
@@ -3753,8 +3973,9 @@ options_warning_safe_scan2(const int msglevel,
if (strprefix(p1, "key-method ")
|| strprefix(p1, "keydir ")
|| strprefix(p1, "proto ")
- || strprefix(p1, "tls-auth ")
- || strprefix(p1, "tun-ipv6"))
+ || streq(p1, "tls-auth")
+ || strprefix(p1, "tun-ipv6")
+ || strprefix(p1, "cipher "))
{
return;
}
@@ -3888,8 +4109,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 +4177,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 +4327,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 +4338,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 +4365,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
}
@@ -4155,7 +4386,7 @@ usage_version(void)
show_windows_version( M_INFO|M_NOPREFIX );
#endif
msg(M_INFO|M_NOPREFIX, "Originally developed by James Yonan");
- msg(M_INFO|M_NOPREFIX, "Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>");
+ msg(M_INFO|M_NOPREFIX, "Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>");
#ifndef ENABLE_SMALL
#ifdef CONFIGURE_DEFINES
msg(M_INFO|M_NOPREFIX, "Compile time defines: %s", CONFIGURE_DEFINES);
@@ -4164,7 +4395,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
@@ -4437,7 +4668,8 @@ in_src_get(const struct in_src *is, char *line, const int size)
}
static char *
-read_inline_file(struct in_src *is, const char *close_tag, struct gc_arena *gc)
+read_inline_file(struct in_src *is, const char *close_tag,
+ int *num_lines, struct gc_arena *gc)
{
char line[OPTION_LINE_SIZE];
struct buffer buf = alloc_buf(8*OPTION_LINE_SIZE);
@@ -4446,6 +4678,7 @@ read_inline_file(struct in_src *is, const char *close_tag, struct gc_arena *gc)
while (in_src_get(is, line, sizeof(line)))
{
+ (*num_lines)++;
char *line_ptr = line;
/* Remove leading spaces */
while (isspace(*line_ptr))
@@ -4479,31 +4712,31 @@ read_inline_file(struct in_src *is, const char *close_tag, struct gc_arena *gc)
return ret;
}
-static bool
+static int
check_inline_file(struct in_src *is, char *p[], struct gc_arena *gc)
{
- bool ret = false;
+ int num_inline_lines = 0;
+
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), &num_inline_lines, gc);
+ p[2] = NULL;
free_buf(&close_tag);
- ret = true;
}
}
- return ret;
+ return num_inline_lines;
}
-static bool
+static int
check_inline_file_via_fp(FILE *fp, char *p[], struct gc_arena *gc)
{
struct in_src is;
@@ -4512,8 +4745,9 @@ check_inline_file_via_fp(FILE *fp, char *p[], struct gc_arena *gc)
return check_inline_file(&is, p, gc);
}
-static bool
-check_inline_file_via_buf(struct buffer *multiline, char *p[], struct gc_arena *gc)
+static int
+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 +4758,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,
@@ -4582,8 +4817,11 @@ read_config_file(struct options *options,
if (parse_line(line + offset, p, SIZE(p)-1, file, line_num, msglevel, &options->gc))
{
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);
+ int lines_inline = check_inline_file_via_fp(fp, p, &options->gc);
+ add_option(options, p, lines_inline, file, line_num, level,
+ msglevel, permission_mask, option_types_found,
+ es);
+ line_num += lines_inline;
}
}
if (fp != stdin)
@@ -4627,8 +4865,10 @@ read_config_string(const char *prefix,
if (parse_line(line, p, SIZE(p)-1, prefix, line_num, msglevel, &options->gc))
{
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);
+ int lines_inline = check_inline_file_via_buf(&multiline, p, &options->gc);
+ add_option(options, p, lines_inline, prefix, line_num, 0, msglevel,
+ permission_mask, option_types_found, es);
+ line_num += lines_inline;
}
CLEAR(p);
}
@@ -4659,7 +4899,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 +4934,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 +5006,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 +5046,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 +5062,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 +5071,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 +5184,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 +5275,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 */
}
@@ -5037,13 +5317,14 @@ add_option(struct options *options,
}
if (good)
{
-#if 0
- /* removed for now since ECHO can potentially include
- * security-sensitive strings */
- msg(M_INFO, "%s:%s",
- pull_mode ? "ECHO-PULL" : "ECHO",
- BSTR(&string));
-#endif
+ /* only message-related ECHO are logged, since other ECHOs
+ * can potentially include security-sensitive strings */
+ if (p[1] && strncmp(p[1], "msg", 3) == 0)
+ {
+ msg(M_INFO, "%s:%s",
+ pull_mode ? "ECHO-PULL" : "ECHO",
+ BSTR(&string));
+ }
#ifdef ENABLE_MANAGEMENT
if (management)
{
@@ -5128,10 +5409,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 +5445,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 +5496,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 +5516,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 +5620,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");
@@ -5413,7 +5724,9 @@ add_option(struct options *options,
const sa_family_t af = ascii2af(p[3]);
if (proto < 0)
{
- msg(msglevel, "remote: bad protocol associated with host %s: '%s'", p[1], p[3]);
+ msg(msglevel,
+ "remote: bad protocol associated with host %s: '%s'",
+ p[1], p[3]);
goto err;
}
re.proto = proto;
@@ -5704,6 +6017,12 @@ add_option(struct options *options,
{
VERIFY_PERMISSION(OPT_P_MESSAGES);
options->verbosity = positive_atoi(p[1]);
+ if (options->verbosity >= (D_TLS_DEBUG_MED & M_DEBUG_LEVEL))
+ {
+ /* We pass this flag to the SSL library to avoid
+ * mbed TLS always generating debug level logging */
+ options->ssl_flags |= SSLF_TLS_DEBUG_ENABLED;
+ }
#if !defined(ENABLE_DEBUG) && !defined(ENABLE_SMALL)
/* Warn when a debug verbosity is supplied when built without debug support */
if (options->verbosity >= 7)
@@ -5799,13 +6118,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 +6161,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);
@@ -5928,7 +6252,8 @@ add_option(struct options *options,
af = ascii2af(p[1]);
if (proto < 0)
{
- msg(msglevel, "Bad protocol: '%s'. Allowed protocols with --proto option: %s",
+ msg(msglevel,
+ "Bad protocol: '%s'. Allowed protocols with --proto option: %s",
p[1],
proto2ascii_all(&gc));
goto err;
@@ -5999,17 +6324,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 +6434,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 +6446,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 +6543,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 +6644,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 +6693,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 +6705,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 +6728,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 +6754,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 +6769,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 +6818,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 +6925,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 +6937,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 +7005,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 +7078,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 +7320,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 +7353,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 +7370,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))
{
@@ -7128,7 +7483,8 @@ add_option(struct options *options,
VERIFY_PERMISSION(OPT_P_IPWIN32);
bool ipv6dns = false;
- if (streq(p[1], "DOMAIN") && p[2])
+ if ((streq(p[1], "DOMAIN") || streq(p[1], "ADAPTER_DOMAIN_SUFFIX"))
+ && p[2])
{
o->domain = p[2];
}
@@ -7151,7 +7507,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 +7528,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 +7713,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 +7797,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 +7819,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 +7860,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 +7884,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 +7905,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 +7922,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 +7972,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 +8076,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 +8127,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 +8135,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 +8150,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 +8163,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 +8208,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 +8243,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])
@@ -7810,18 +8278,21 @@ add_option(struct options *options,
}
#endif
}
+ else if (streq(p[0], "auth-token-user") && p[1] && !p[2])
+ {
+ VERIFY_PERMISSION(OPT_P_ECHO);
+ ssl_set_auth_token_user(p[1]);
+ }
else if (streq(p[0], "single-session") && !p[1])
{
VERIFY_PERMISSION(OPT_P_GENERAL);
options->single_session = true;
}
-#ifdef ENABLE_PUSH_PEER_INFO
else if (streq(p[0], "push-peer-info") && !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 +8313,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 +8347,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 +8460,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 +8481,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_file = p[1];
+ options->tls_crypt_file_inline = is_inline;
+ }
+ else if (permission_mask & OPT_P_CONNECTION)
{
- options->tls_crypt_inline = p[2];
+ 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 +8594,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 +8709,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 +8731,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..dea9642 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -41,9 +41,7 @@
#include "comp.h"
#include "pushlist.h"
#include "clinat.h"
-#ifdef ENABLE_CRYPTO
#include "crypto_backend.h"
-#endif
/*
@@ -74,14 +72,21 @@ struct options_pre_pull
bool routes_ipv6_defined;
struct route_ipv6_option_list *routes_ipv6;
+ const char *route_default_gateway;
+ const char *route_ipv6_default_gateway;
+
bool client_nat_defined;
struct client_nat_option_list *client_nat;
+ int ping_send_timeout;
+ int ping_rec_timeout;
+ int ping_rec_timeout_action;
+
int foreign_option_index;
};
#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 +137,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 +176,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 +191,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 +221,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 +228,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 +269,7 @@ struct options
int proto_force;
-#ifdef ENABLE_OCC
bool mtu_test;
-#endif
#ifdef ENABLE_MEMSTATS
char *memstats_fn;
@@ -325,6 +357,7 @@ struct options
/* mark value */
int mark;
+ char *bind_dev;
/* socket flags */
unsigned int sockflags;
@@ -333,6 +366,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 +374,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 +408,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 +461,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 +479,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 +500,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 +524,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 +533,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 +555,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 +577,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 +584,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 +600,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 +634,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 +679,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 +691,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 +761,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 +778,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..f31d882 100644
--- a/src/openvpn/otime.c
+++ b/src/openvpn/otime.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/otime.h b/src/openvpn/otime.h
index a6f7ec2..f847296 100644
--- a/src/openvpn/otime.h
+++ b/src/openvpn/otime.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -84,6 +84,7 @@ update_time(void)
openvpn_gettimeofday(&tv, NULL);
#else
update_now(time(NULL));
+ now_usec = 0;
#endif
}
diff --git a/src/openvpn/packet_id.c b/src/openvpn/packet_id.c
index d58761b..baa7054 100644
--- a/src/openvpn/packet_id.c
+++ b/src/openvpn/packet_id.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..8f70596 100644
--- a/src/openvpn/packet_id.h
+++ b/src/openvpn/packet_id.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/perf.c b/src/openvpn/perf.c
index d882358..2ad5825 100644
--- a/src/openvpn/perf.c
+++ b/src/openvpn/perf.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/perf.h b/src/openvpn/perf.h
index 9cf0343..27c645f 100644
--- a/src/openvpn/perf.h
+++ b/src/openvpn/perf.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
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..3645631 100644
--- a/src/openvpn/pf.c
+++ b/src/openvpn/pf.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,21 @@ pf_init_context(struct context *c)
}
#endif
}
- else
- {
- msg(M_WARN, "WARNING: OPENVPN_PLUGIN_ENABLE_PF disabled");
- }
+ }
+ if (!c->c2.pf.enabled)
+ {
+ /* At some point in openvpn history, this code just printed a
+ * warning and signalled itself (SIGUSR1, "plugin-pf-init-failed")
+ * to terminate the client instance. This got broken at one of
+ * the client auth state refactorings (leading to SIGSEGV crashes)
+ * and due to "pf will be removed anyway" reasons the easiest way
+ * to prevent crashes is to REQUIRE that plugins succeed - so if
+ * the plugin fails, we cleanly abort OpenVPN
+ *
+ * see also: https://community.openvpn.net/openvpn/ticket/1377
+ */
+ msg(M_FATAL, "FATAL: failed to init PF plugin, must succeed.");
+ return;
}
}
#endif /* ifdef PLUGIN_PF */
@@ -658,7 +666,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..609c842 100644
--- a/src/openvpn/pf.h
+++ b/src/openvpn/pf.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..67bbca1 100644
--- a/src/openvpn/ping.c
+++ b/src/openvpn/ping.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..7518404 100644
--- a/src/openvpn/ping.h
+++ b/src/openvpn/ping.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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.c b/src/openvpn/pkcs11.c
index d40ca45..367d67d 100644
--- a/src/openvpn/pkcs11.c
+++ b/src/openvpn/pkcs11.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/pkcs11.h b/src/openvpn/pkcs11.h
index 66c6a7e..ec52470 100644
--- a/src/openvpn/pkcs11.h
+++ b/src/openvpn/pkcs11.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/pkcs11_backend.h b/src/openvpn/pkcs11_backend.h
index e8fb664..eebfc55 100644
--- a/src/openvpn/pkcs11_backend.h
+++ b/src/openvpn/pkcs11_backend.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/src/openvpn/pkcs11_mbedtls.c b/src/openvpn/pkcs11_mbedtls.c
index 7620624..3cfcacc 100644
--- a/src/openvpn/pkcs11_mbedtls.c
+++ b/src/openvpn/pkcs11_mbedtls.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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/pkcs11_openssl.c b/src/openvpn/pkcs11_openssl.c
index 642769c..f5d3add 100644
--- a/src/openvpn/pkcs11_openssl.c
+++ b/src/openvpn/pkcs11_openssl.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c
index fbffd0f..964d578 100644
--- a/src/openvpn/platform.c
+++ b/src/openvpn/platform.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..1b9340c 100644
--- a/src/openvpn/platform.h
+++ b/src/openvpn/platform.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..73c25ff 100644
--- a/src/openvpn/plugin.c
+++ b/src/openvpn/plugin.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..98b3078 100644
--- a/src/openvpn/plugin.h
+++ b/src/openvpn/plugin.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..b3f0bcd 100644
--- a/src/openvpn/pool.c
+++ b/src/openvpn/pool.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -147,61 +147,144 @@ 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;
+
+ /* if a pool starts at a base address that has all-zero in the
+ * host part, that first IPv6 address must not be assigned to
+ * clients because it is not usable (subnet anycast address).
+ * Start with 1, then.
+ *
+ * NOTE: this will also (mis-)fire for something like
+ * ifconfig-ipv6-pool 2001:db8:0:1:1234::0/64
+ * as we only check the rightmost 32 bits of the host part. So be it.
+ */
+ if (base == 0)
+ {
+ msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: incrementing pool start "
+ "to avoid ::0 assignment");
+ base++;
+ pool->ipv6.base.s6_addr[15]++;
+ }
+
+ 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);
+ }
+ }
+
+ ASSERT(pool->size > 0);
- msg(D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
- print_in_addr_t(pool->base, 0, &gc),
- pool->size, pool->ipv6 );
+ ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size);
gc_free(&gc);
return pool;
@@ -213,6 +296,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 +323,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 +361,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 +374,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 +406,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 +478,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 +511,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 +604,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 +624,109 @@ ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool *
break;
}
++line;
- if (BLEN(&in))
+ if (!BLEN(&in))
{
- int c = *BSTR(&in);
- if (c == '#' || c == ';')
+ continue;
+ }
+
+ 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)
{
- continue;
+ msg(M_WARN, "pool: invalid IPv4 (%s) for CN=%s", ip_buf,
+ cn_buf);
}
- msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
- BSTR(&in) );
+ else
+ {
+ 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);
+ }
+ }
+ }
+
+ 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..e8db68e 100644
--- a/src/openvpn/pool.h
+++ b/src/openvpn/pool.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..cff0ef0 100644
--- a/src/openvpn/proto.c
+++ b/src/openvpn/proto.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..f73e50c 100644
--- a/src/openvpn/proto.h
+++ b/src/openvpn/proto.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..8822998 100644
--- a/src/openvpn/proxy.c
+++ b/src/openvpn/proxy.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/proxy.h b/src/openvpn/proxy.h
index 707f7fa..7668dc9 100644
--- a/src/openvpn/proxy.h
+++ b/src/openvpn/proxy.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/ps.c b/src/openvpn/ps.c
index 25ab374..a611761 100644
--- a/src/openvpn/ps.c
+++ b/src/openvpn/ps.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -983,13 +983,38 @@ is_openvpn_protocol(const struct buffer *buf)
const int len = BLEN(buf);
if (len >= 3)
{
- return p[0] == 0
- && p[1] >= 14
- && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
+ int plen = (p[0] << 8) | p[1];
+
+ if (p[2] == (P_CONTROL_HARD_RESET_CLIENT_V3 << P_OPCODE_SHIFT))
+ {
+ /* WKc is at least 290 byte (not including metadata):
+ *
+ * 16 bit len + 256 bit HMAC + 2048 bit Kc = 2320 bit
+ *
+ * This is increased by the normal length of client handshake +
+ * tls-crypt overhead (32)
+ *
+ * For metadata tls-crypt-v2.txt does not explicitly specify
+ * an upper limit but we also have TLS_CRYPT_V2_MAX_WKC_LEN
+ * as 1024 bytes. We err on the safe side with 255 extra overhead
+ *
+ * We don't do the 2 byte check for tls-crypt-v2 because it is very
+ * unrealistic to have only 2 bytes available.
+ */
+ return (plen >= 336 && plen < (1024 + 255));
+ }
+ else
+ {
+ /* For non tls-crypt2 we assume the packet length to valid between
+ * 14 and 255 */
+ return plen >= 14 && plen <= 255
+ && (p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2 << P_OPCODE_SHIFT));
+ }
}
else if (len >= 2)
{
- return p[0] == 0 && p[1] >= 14;
+ int plen = (p[0] << 8) | p[1];
+ return plen >= 14 && plen <= 255;
}
else
{
diff --git a/src/openvpn/ps.h b/src/openvpn/ps.h
index b4490f5..2192034 100644
--- a/src/openvpn/ps.h
+++ b/src/openvpn/ps.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 002be23..bc94c32 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,22 @@ 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;
+ struct key_state *ks = &c->c2.tls_multi->session[TM_ACTIVE].key[KS_PRIMARY];
-#ifdef ENABLE_ASYNC_PUSH
- c->c2.push_request_received = true;
-#endif
- if (tls_authentication_status(c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
+ if (tls_authentication_status(c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED
+ || c->c2.tls_multi->multi_state == 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.tls_multi->multi_state == CAS_SUCCEEDED
+ && ks->authenticated == KS_AUTH_TRUE)
{
time_t now;
@@ -674,10 +754,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 +773,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 +794,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 +858,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 +940,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..fa323f4 100644
--- a/src/openvpn/push.h
+++ b/src/openvpn/push.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..a7b5998 100644
--- a/src/openvpn/pushlist.h
+++ b/src/openvpn/pushlist.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d0a8d78 100644
--- a/src/openvpn/reliable.c
+++ b/src/openvpn/reliable.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..2daab6e 100644
--- a/src/openvpn/reliable.h
+++ b/src/openvpn/reliable.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..77579e3
--- /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-2021 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..5cfbb28 100644
--- a/src/openvpn/route.c
+++ b/src/openvpn/route.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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"
@@ -48,6 +49,10 @@
#include <linux/rtnetlink.h> /* RTM_GETROUTE etc. */
#endif
+#if defined(TARGET_NETBSD)
+#include <net/route.h> /* RT_ROUNDUP(), RT_ADVANCE() */
+#endif
+
#ifdef _WIN32
#include "openvpn-msg.h"
@@ -62,7 +67,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);
@@ -322,6 +327,10 @@ init_route(struct route_ipv4 *r,
if (get_special_addr(rl, ro->network, (in_addr_t *) &special.s_addr, &status))
{
+ if (!status)
+ {
+ goto fail;
+ }
special.s_addr = htonl(special.s_addr);
ret = openvpn_getaddrinfo(0, inet_ntoa(special), NULL, 0, NULL,
AF_INET, network_list);
@@ -448,11 +457,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 +617,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;
@@ -622,7 +627,7 @@ init_route_list(struct route_list *rl,
rl->flags = opt->flags;
- if (remote_host)
+ if (remote_host != IPV4_INVALID_ADDR)
{
rl->spec.remote_host = remote_host;
rl->spec.flags |= RTSA_REMOTE_HOST;
@@ -634,7 +639,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 +773,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 +799,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 +907,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 +916,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 +926,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 +935,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 +944,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 +958,8 @@ add_bypass_routes(struct route_bypass *rb,
tt,
flags | ROUTE_REF_GW,
rgi,
- es);
+ es,
+ ctx);
}
}
}
@@ -960,7 +970,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 +984,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)
{
@@ -997,14 +1011,10 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un
* - we are connecting to a non-IPv4 remote host (i.e. we use IPv6)
*/
else if (!(rl->rgi.flags & RGI_ADDR_DEFINED) && !local
- && (rl->spec.remote_host != IPV4_INVALID_ADDR))
+ && (rl->spec.flags & RTSA_REMOTE_HOST))
{
msg(M_WARN, "%s Cannot read current default gateway from system", err);
}
- else if (!(rl->spec.flags & RTSA_REMOTE_HOST))
- {
- msg(M_WARN, "%s Cannot obtain current remote host address", err);
- }
else
{
#ifndef TARGET_ANDROID
@@ -1027,7 +1037,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un
/* route remote host to original default gateway */
/* if remote_host is not ipv4 (ie: ipv6), just skip
* adding this special /32 route */
- if (rl->spec.remote_host != IPV4_INVALID_ADDR)
+ if ((rl->spec.flags & RTSA_REMOTE_HOST)
+ && rl->spec.remote_host != IPV4_INVALID_ADDR)
{
add_route3(rl->spec.remote_host,
IPV4_NETMASK_HOST,
@@ -1035,7 +1046,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 +1058,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 +1072,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 +1082,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 +1092,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 +1102,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un
tt,
flags,
&rl->rgi,
- es);
+ es,
+ ctx);
}
}
@@ -1098,7 +1114,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 +1130,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 +1150,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 +1160,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 +1172,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 +1188,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 +1223,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 +1236,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 +1255,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 +1280,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 +1357,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 +1388,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)
@@ -1441,6 +1476,13 @@ setenv_route_ipv6(struct env_set *es, const struct route_ipv6 *r6, int i)
buf_printf( &name2, "route_ipv6_gateway_%d", i );
setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
+
+ if (r6->flags & RT_METRIC_DEFINED)
+ {
+ struct buffer name3 = alloc_buf_gc( 256, &gc );
+ buf_printf( &name3, "route_ipv6_metric_%d", i) ;
+ setenv_int( es, BSTR(&name3), r6->metric);
+ }
}
gc_free(&gc);
}
@@ -1525,30 +1567,36 @@ 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;
if (!(r->flags & RT_DEFINED))
{
+ argv_free(&argv);
return;
}
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 +1605,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);
- }
- if (is_on_link(is_local_route, flags, rgi))
- {
- argv_printf_cat(&argv, "dev %s", rgi->iface);
+ metric = r->metric;
}
- 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 +1655,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 +1843,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 +1855,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 +1874,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,11 +1885,14 @@ 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) )
{
+ argv_free(&argv);
return;
}
@@ -1917,46 +1952,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)
@@ -1966,25 +1993,24 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag
}
else
{
- struct buffer out = alloc_buf_gc(64, &gc);
+ DWORD adapter_index;
if (r6->adapter_index) /* vpn server special route */
{
- buf_printf(&out, "interface=%lu", r6->adapter_index );
+ adapter_index = r6->adapter_index;
gateway_needed = true;
}
else
{
- buf_printf(&out, "interface=%lu", tt->adapter_index );
+ adapter_index = tt->adapter_index;
}
- device = buf_bptr(&out);
- /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
- argv_printf(&argv, "%s%sc interface ipv6 add route %s/%d %s",
+ /* netsh interface ipv6 add route 2001:db8::/32 42 */
+ argv_printf(&argv, "%s%s interface ipv6 add route %s/%d %lu",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
network,
r6->netbits,
- device);
+ adapter_index);
/* next-hop depends on TUN or TAP mode:
* - in TAP mode, we use the "real" next-hop
@@ -2114,6 +2140,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 +2149,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 +2160,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 +2185,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 +2202,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 +2343,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 +2381,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 +2414,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)
@@ -2413,25 +2436,24 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned
}
else
{
- struct buffer out = alloc_buf_gc(64, &gc);
+ DWORD adapter_index;
if (r6->adapter_index) /* vpn server special route */
{
- buf_printf(&out, "interface=%lu", r6->adapter_index );
+ adapter_index = r6->adapter_index;
gateway_needed = true;
}
else
{
- buf_printf(&out, "interface=%lu", tt->adapter_index );
+ adapter_index = tt->adapter_index;
}
- device = buf_bptr(&out);
- /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */
- argv_printf(&argv, "%s%sc interface ipv6 delete route %s/%d %s",
+ /* netsh interface ipv6 delete route 2001:db8::/32 42 */
+ argv_printf(&argv, "%s%s interface ipv6 delete route %s/%d %lu",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
network,
r6->netbits,
- device);
+ adapter_index);
/* next-hop depends on TUN or TAP mode:
* - in TAP mode, we use the "real" next-hop
@@ -2548,8 +2570,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 +2666,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 +2739,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 +2826,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 +2845,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 +3015,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 +3098,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 +3184,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 +3294,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 +3371,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)
+ if (net_route_v6_best_gw(ctx, dest, &rgi6->gateway.addr_ipv6,
+ rgi6->iface) == 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))
- {
- 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)
+ if (!IN6_IS_ADDR_UNSPECIFIED(&rgi6->gateway.addr_ipv6))
{
- 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;
+ rgi6->flags |= RGI_ADDR_DEFINED;
}
- if (nh->nlmsg_type != RTM_NEWROUTE)
+ if (strlen(rgi6->iface) > 0)
{
- /* shouldn't happen */
- msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type );
- continue;
- }
-
- 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)
- {
- 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 +3401,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) \
@@ -3547,11 +3420,15 @@ struct rtmsg {
/* the route socket code is identical for all 4 supported BSDs and for
* MacOS X (Darwin), with one crucial difference: when going from
- * 32 bit to 64 bit, the BSDs increased the structure size but kept
+ * 32 bit to 64 bit, FreeBSD/OpenBSD increased the structure size but kept
* source code compatibility by keeping the use of "long", while
* MacOS X decided to keep binary compatibility by *changing* the API
* to use "uint32_t", thus 32 bit on all OS X variants
*
+ * NetBSD does the MacOS way of "fixed number of bits, no matter if
+ * 32 or 64 bit OS", but chose uint64_t. For maximum portability, we
+ * just use the OS RT_ROUNDUP() macro, which is guaranteed to be correct.
+ *
* We used to have a large amount of duplicate code here which really
* differed only in this (long) vs. (uint32_t) - IMHO, worse than
* having a combined block for all BSDs with this single #ifdef inside
@@ -3560,6 +3437,8 @@ struct rtmsg {
#if defined(TARGET_DARWIN)
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
+#elif defined(TARGET_NETBSD)
+#define ROUNDUP(a) RT_ROUNDUP(a)
#else
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
@@ -3568,14 +3447,14 @@ struct rtmsg {
#if defined(TARGET_SOLARIS)
#define NEXTADDR(w, u) \
if (rtm_addrs & (w)) { \
- l = ROUNDUP(sizeof(u)); memmove(cp, &(u), l); cp += l; \
+ l = sizeof(u); memmove(cp, &(u), l); cp += ROUNDUP(l); \
}
#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr_in)))
#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 +3463,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 +3683,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;
@@ -3868,7 +3747,7 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
}
if (write(sockfd, (char *)&m_rtmsg, l) < 0)
{
- msg(M_WARN, "GDG6: problem writing to routing socket");
+ msg(M_WARN|M_ERRNO, "GDG6: problem writing to routing socket");
goto done;
}
@@ -3984,13 +3863,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..64d57a5 100644
--- a/src/openvpn/route.h
+++ b/src/openvpn/route.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..bdb0afb
--- /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-2021 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..5061f75
--- /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-2021 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..d3044d3 100644
--- a/src/openvpn/schedule.c
+++ b/src/openvpn/schedule.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..d911f1e 100644
--- a/src/openvpn/schedule.h
+++ b/src/openvpn/schedule.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..495db4f 100644
--- a/src/openvpn/session_id.c
+++ b/src/openvpn/session_id.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..f0c4c9e 100644
--- a/src/openvpn/session_id.h
+++ b/src/openvpn/session_id.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..f97b045 100644
--- a/src/openvpn/shaper.c
+++ b/src/openvpn/shaper.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..f565055 100644
--- a/src/openvpn/shaper.h
+++ b/src/openvpn/shaper.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..25af9de 100644
--- a/src/openvpn/sig.c
+++ b/src/openvpn/sig.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..3ce57ab 100644
--- a/src/openvpn/sig.h
+++ b/src/openvpn/sig.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/socket.c b/src/openvpn/socket.c
index 9131ec2..cd41893 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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,
@@ -373,7 +378,8 @@ do_preresolve(struct context *c)
/* HTTP remote hostname does not need to be resolved */
if (!ce->http_proxy_options)
{
- status = do_preresolve_host(c, remote, ce->remote_port, ce->af, flags);
+ status = do_preresolve_host(c, remote, ce->remote_port,
+ ce->af, flags);
if (status != 0)
{
goto err;
@@ -412,7 +418,8 @@ do_preresolve(struct context *c)
{
flags |= GETADDR_PASSIVE;
flags &= ~GETADDR_RANDOMIZE;
- status = do_preresolve_host(c, ce->local, ce->local_port, ce->af, flags);
+ status = do_preresolve_host(c, ce->local, ce->local_port,
+ ce->af, flags);
if (status != 0)
{
goto err;
@@ -521,7 +528,9 @@ openvpn_getaddrinfo(unsigned int flags,
if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
&& !resolve_retry_seconds)
{
- fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s) (I would have retried this name query if you had specified the --resolv-retry option.)";
+ fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s) "
+ "(I would have retried this name query if you had "
+ "specified the --resolv-retry option.)";
}
if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
@@ -553,11 +562,13 @@ openvpn_getaddrinfo(unsigned int flags,
while (true)
{
#ifndef _WIN32
+ /* force resolv.conf reload */
res_init();
#endif
/* try hostname lookup */
hints.ai_flags &= ~AI_NUMERICHOST;
- dmsg(D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d",
+ dmsg(D_SOCKET_DEBUG,
+ "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d",
flags, hints.ai_family, hints.ai_socktype);
status = getaddrinfo(hostname, servname, &hints, res);
@@ -568,7 +579,9 @@ openvpn_getaddrinfo(unsigned int flags,
{
if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
{
- msg(level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
+ msg(level,
+ "RESOLVE: Ignored SIGUSR1 signal received during "
+ "DNS resolution attempt");
*signal_received = 0;
}
else
@@ -629,7 +642,9 @@ openvpn_getaddrinfo(unsigned int flags,
/* IP address parse succeeded */
if (flags & GETADDR_RANDOMIZE)
{
- msg(M_WARN, "WARNING: ignoring --remote-random-hostname because the hostname is an IP address");
+ msg(M_WARN,
+ "WARNING: ignoring --remote-random-hostname because the "
+ "hostname is an IP address");
}
}
@@ -987,7 +1002,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 +1148,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);
}
@@ -1453,14 +1480,14 @@ openvpn_connect(socket_descriptor_t sd,
struct pollfd fds[1];
fds[0].fd = sd;
fds[0].events = POLLOUT;
- status = poll(fds, 1, 0);
+ status = poll(fds, 1, (connect_timeout > 0) ? 1000 : 0);
#else
fd_set writes;
struct timeval tv;
FD_ZERO(&writes);
openvpn_fd_set(sd, &writes);
- tv.tv_sec = 0;
+ tv.tv_sec = (connect_timeout > 0) ? 1 : 0;
tv.tv_usec = 0;
status = select(sd + 1, NULL, &writes, NULL, &tv);
@@ -1490,7 +1517,7 @@ openvpn_connect(socket_descriptor_t sd,
#endif
break;
}
- management_sleep(1);
+ management_sleep(0);
continue;
}
@@ -1607,6 +1634,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. */
@@ -1769,7 +1812,8 @@ resolve_remote(struct link_socket *sock,
sock->info.lsa->remote_list = ai;
sock->info.lsa->current_remote = ai;
- dmsg(D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
+ dmsg(D_SOCKET_DEBUG,
+ "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
flags,
phase,
retry,
@@ -1859,6 +1903,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 +1930,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;
@@ -1995,8 +2041,14 @@ phase2_inetd(struct link_socket *sock, const struct frame *frame,
}
else
{
- msg(M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET",
+ int saved_errno = errno;
+ msg(M_WARN|M_ERRNO, "inetd(%s): getsockname(%d) failed, using AF_INET",
proto2ascii(sock->info.proto, sock->info.af, false), (int)sock->sd);
+ /* if not called with a socket on stdin, --inetd cannot work */
+ if (saved_errno == ENOTSOCK)
+ {
+ msg(M_FATAL, "ERROR: socket required for --inetd operation");
+ }
}
}
#else /* ifdef HAVE_GETSOCKNAME */
@@ -2012,7 +2064,6 @@ phase2_inetd(struct link_socket *sock, const struct frame *frame,
false,
sock->inetd == INETD_NOWAIT,
signal_received);
-
}
ASSERT(!remote_changed);
}
@@ -2415,8 +2466,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 +2500,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 +2510,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 +2564,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 +2595,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 +2666,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 +2740,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 +2807,7 @@ stream_buf_added(struct stream_buf *sb,
}
}
-void
+static void
stream_buf_close(struct stream_buf *sb)
{
free_buf(&sb->residual);
@@ -3116,22 +3166,22 @@ struct proto_names {
/* Indexed by PROTO_x */
static const struct proto_names proto_names[] = {
- {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE},
+ {"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE},
/* try IPv4 and IPv6 (client), bind dual-stack (server) */
- {"udp", "UDP", AF_UNSPEC, PROTO_UDP},
- {"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER},
- {"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT},
- {"tcp", "TCP", AF_UNSPEC, PROTO_TCP},
+ {"udp", "UDP", AF_UNSPEC, PROTO_UDP},
+ {"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER},
+ {"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT},
+ {"tcp", "TCP", AF_UNSPEC, PROTO_TCP},
/* force IPv4 */
- {"udp4", "UDPv4", AF_INET, PROTO_UDP},
- {"tcp4-server","TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER},
- {"tcp4-client","TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT},
- {"tcp4", "TCPv4", AF_INET, PROTO_TCP},
+ {"udp4", "UDPv4", AF_INET, PROTO_UDP},
+ {"tcp4-server", "TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER},
+ {"tcp4-client", "TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT},
+ {"tcp4", "TCPv4", AF_INET, PROTO_TCP},
/* force IPv6 */
- {"udp6","UDPv6", AF_INET6, PROTO_UDP},
- {"tcp6-server","TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER},
- {"tcp6-client","TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT},
- {"tcp6","TCPv6", AF_INET6, PROTO_TCP},
+ {"udp6", "UDPv6", AF_INET6, PROTO_UDP},
+ {"tcp6-server", "TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER},
+ {"tcp6-client", "TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT},
+ {"tcp6", "TCPv6", AF_INET6, PROTO_TCP},
};
bool
@@ -3143,6 +3193,7 @@ proto_is_net(int proto)
}
return proto != PROTO_NONE;
}
+
bool
proto_is_dgram(int proto)
{
@@ -3258,7 +3309,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 +3394,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 +3909,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..c02c848 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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/socks.c b/src/openvpn/socks.c
index 57f0cee..71f82b2 100644
--- a/src/openvpn/socks.c
+++ b/src/openvpn/socks.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -312,7 +312,7 @@ recv_socks_reply(socket_descriptor_t sd,
char atyp = '\0';
int alen = 0;
int len = 0;
- char buf[22];
+ char buf[270]; /* 4 + alen(max 256) + 2 */
const int timeout_sec = 5;
if (addr != NULL)
@@ -381,7 +381,10 @@ recv_socks_reply(socket_descriptor_t sd,
break;
case '\x03': /* DOMAINNAME */
- alen = (unsigned char) c;
+ /* RFC 1928, section 5: 1 byte length, <n> bytes name,
+ * so the total "address length" is (length+1)
+ */
+ alen = (unsigned char) c + 1;
break;
case '\x04': /* IP V6 */
@@ -451,7 +454,7 @@ establish_socks_proxy_passthru(struct socks_proxy_info *p,
const char *servname, /* openvpn server port */
volatile int *signal_received)
{
- char buf[128];
+ char buf[270];
size_t len;
if (!socks_handshake(p, sd, signal_received))
diff --git a/src/openvpn/socks.h b/src/openvpn/socks.h
index aef873c..9bda2e8 100644
--- a/src/openvpn/socks.h
+++ b/src/openvpn/socks.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index cf66899..d66299f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -5,9 +5,9 @@
* 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>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
+ * Copyright (C) 2008-2021 David Sommerseth <dazo@eurephia.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -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 */
}
}
@@ -452,8 +434,6 @@ ssl_set_auth_nocache(void)
{
passbuf.nocache = true;
auth_user_pass.nocache = true;
- /* wait for push-reply, because auth-token may still need the username */
- auth_user_pass.wait_for_push = true;
}
/*
@@ -465,11 +445,17 @@ ssl_set_auth_token(const char *token)
set_auth_token(&auth_user_pass, &auth_token, token);
}
+void
+ssl_set_auth_token_user(const char *username)
+{
+ set_auth_token_user(&auth_token, username);
+}
+
/*
* 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 +476,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 +547,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};
@@ -578,7 +564,15 @@ tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
}
else if (platform_stat(crl_file, &crl_stat) < 0)
{
- msg(M_WARN, "WARNING: Failed to stat CRL file, not (re)loading CRL.");
+ /* If crl_last_mtime is zero, the CRL file has not been read before. */
+ if (ssl_ctx->crl_last_mtime == 0)
+ {
+ msg(M_FATAL, "ERROR: Failed to stat CRL file during initialization, exiting.");
+ }
+ else
+ {
+ msg(M_WARN, "WARNING: Failed to stat CRL file, not reloading CRL.");
+ }
return;
}
@@ -603,7 +597,7 @@ tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
* All files are in PEM format.
*/
void
-init_ssl(const struct options *options, struct tls_root_ctx *new_ctx)
+init_ssl(const struct options *options, struct tls_root_ctx *new_ctx, bool in_chroot)
{
ASSERT(NULL != new_ctx);
@@ -633,6 +627,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 +663,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)
{
- /* Load Certificate */
- if (options->cert_file)
+ tls_ctx_load_cert_file(new_ctx, options->cert_file, options->cert_file_inline);
+ }
+
+ if (options->priv_key_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)
{
@@ -719,7 +715,24 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx)
/* Read CRL */
if (options->crl_file && !(options->ssl_flags & SSLF_CRL_VERIFY_DIR))
{
- tls_ctx_reload_crl(new_ctx, options->crl_file, options->crl_file_inline);
+ /* If we're running with the chroot option, we may run init_ssl() before
+ * and after chroot-ing. We can use the crl_file path as-is if we're
+ * not going to chroot, or if we already are inside the chroot.
+ *
+ * If we're going to chroot later, we need to prefix the path of the
+ * chroot directory to crl_file.
+ */
+ if (!options->chroot_dir || in_chroot || options->crl_file_inline)
+ {
+ tls_ctx_reload_crl(new_ctx, options->crl_file, options->crl_file_inline);
+ }
+ else
+ {
+ struct gc_arena gc = gc_new();
+ struct buffer crl_file_buf = prepend_dir(options->chroot_dir, options->crl_file, &gc);
+ tls_ctx_reload_crl(new_ctx, BSTR(&crl_file_buf), options->crl_file_inline);
+ gc_free(&gc);
+ }
}
/* Once keys and cert are loaded, load ECDH parameters */
@@ -771,9 +784,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 +809,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 +857,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 +871,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_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
+ || op == P_CONTROL_HARD_RESET_CLIENT_V3)
{
- if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2)
- {
- return true;
- }
+ return true;
}
return false;
@@ -1094,16 +1096,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 +1143,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;
+ tls_wrap_free(&session->tls_wrap);
- if (packet_id_initialized(&session->tls_wrap.opt.packet_id))
- {
- packet_id_free(&session->tls_wrap.opt.packet_id);
- }
-
- free_buf(&session->tls_wrap.work);
-
- for (i = 0; i < KS_SIZE; ++i)
+ for (size_t i = 0; i < KS_SIZE; ++i)
{
key_state_free(&session->key[i], false);
}
@@ -1234,11 +1227,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 +1329,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 +1340,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 +1358,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 +1390,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 +1457,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 +1476,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 +1506,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 +1561,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 +1645,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 +1729,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 +1897,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 +1913,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 +1947,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 +1970,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 +1991,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 +2008,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 +2204,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 +2238,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 +2273,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 +2285,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 +2308,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 +2321,16 @@ 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)
+key_method_2_write(struct buffer *buf, struct tls_multi *multi,
+ 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 +2340,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,16 +2353,16 @@ 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;
}
}
- /* write username/password if specified */
- if (auth_user_pass_enabled)
+ /* write username/password if specified or we are using a auth-token */
+ if (auth_user_pass_enabled || (auth_token.token_defined && auth_token.defined))
{
-#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);
@@ -2415,8 +2373,10 @@ key_method_2_write(struct buffer *buf, struct tls_session *session)
* If we have a valid auth-token, send that instead of real
* username/password
*/
- if (auth_token.defined)
+ if (auth_token.token_defined && auth_token.defined)
+ {
up = &auth_token;
+ }
if (!write_string(buf, up->username, -1))
{
@@ -2428,14 +2388,15 @@ key_method_2_write(struct buffer *buf, struct tls_session *session)
}
/* if auth-nocache was specified, the auth_user_pass object reaches
* a "complete" state only after having received the push-reply
- * message.
+ * message. The push message might contain an auth-token that needs
+ * the username of auth_user_pass.
*
* For this reason, skip the purge operation here if no push-reply
* message has been received yet.
*
* This normally happens upon first negotiation only.
*/
- if (!auth_user_pass.wait_for_push)
+ if (!session->opt->pull)
{
purge_user_pass(&auth_user_pass, false);
}
@@ -2457,15 +2418,19 @@ key_method_2_write(struct buffer *buf, struct tls_session *session)
goto error;
}
- /* Generate tunnel keys if we're a TLS server.
- * If we're a p2mp server and IV_NCP >= 2 is negotiated, the first key
- * generation is postponed until after the pull/push, so we can process pushed
- * cipher directives.
+ /*
+ * Generate tunnel keys if we're a TLS server.
+ *
+ * If we're a p2mp server to allow NCP, the first key
+ * generation is postponed until after the connect script finished and the
+ * NCP options can be processed. Since that always happens at after connect
+ * script options are available the CAS_SUCCEEDED status is identical to
+ * NCP options are processed and we have no extra state for NCP finished.
*/
- 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
+ || multi->multi_state == CAS_SUCCEEDED))
{
- if (ks->authenticated)
+ if (ks->authenticated > KS_AUTH_FALSE)
{
if (!tls_session_generate_data_channel_keys(session))
{
@@ -2483,73 +2448,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 +2466,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 +2475,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 +2498,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 +2508,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,18 +2520,13 @@ 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)
+ /* In OCC we send '[null-cipher]' instead 'none' */
+ if (multi->remote_ciphername
+ && strcmp(multi->remote_ciphername, "[null-cipher]") == 0)
{
- /* 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;
- }
+ free(multi->remote_ciphername);
+ multi->remote_ciphername = string_alloc("none", NULL);
}
-#endif /* if P2MP_SERVER */
if (tls_session_user_pass_enabled(session))
{
@@ -2653,19 +2552,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 +2572,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 +2582,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 +2613,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 +2676,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 +2746,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 +2791,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 +2884,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, multi, session))
{
- if (!key_method_2_write(buf, session))
- {
- goto error;
- }
- }
- else
- {
- ASSERT(0);
+ goto error;
}
state_change = true;
@@ -3022,23 +2900,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 +3026,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 +3038,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 +3113,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 +3146,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 +3163,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 +3186,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 +3308,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 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 */
- /* get opcode and key 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 */
+ }
- /* verify legal opcode */
- if (op < P_FIRST_OPCODE || op > P_LAST_OPCODE)
+ 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];
+
+ 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 +3688,6 @@ done:
error:
++multi->n_soft_errors;
-error_lite:
tls_clear_error();
goto done;
}
@@ -3833,94 +3709,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 +3802,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 +3970,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 +3994,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 +4001,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"
+ );
}
/*
@@ -4309,15 +4140,7 @@ done:
}
void
-delayed_auth_pass_purge(void)
+ssl_clean_user_pass(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..ebc1bf6 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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
@@ -146,7 +154,7 @@ void free_ssl_lib(void);
* Build master SSL context object that serves for the whole of OpenVPN
* instantiation
*/
-void init_ssl(const struct options *options, struct tls_root_ctx *ctx);
+void init_ssl(const struct options *options, struct tls_root_ctx *ctx, bool in_chroot);
/** @addtogroup control_processor
* @{ */
@@ -430,7 +438,11 @@ void ssl_purge_auth(const bool auth_user_pass_only);
void ssl_set_auth_token(const char *token);
-#ifdef ENABLE_CLIENT_CR
+void ssl_set_auth_token_user(const char *username);
+
+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 +450,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 +499,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 +507,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,14 +594,16 @@ 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);
+/**
+ * Cleans the saved user/password unless auth-nocache is in use.
+ */
+void ssl_clean_user_pass(void);
/*
@@ -619,6 +618,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..9d25321 100644
--- a/src/openvpn/ssl_backend.h
+++ b/src/openvpn/ssl_backend.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..74faf68 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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
@@ -148,6 +166,8 @@ struct key_source2 {
struct key_state
{
int state;
+ /** The state of the auth-token sent from the client */
+ int auth_token_state_flags;
/**
* Key id for this key_state, inherited from struct tls_session.
@@ -185,12 +205,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 +217,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 +229,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 +255,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 +280,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 +300,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 +316,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 */
@@ -326,6 +349,7 @@ struct tls_options
#define SSLF_TLS_VERSION_MIN_MASK 0xF /* (uses bit positions 6 to 9) */
#define SSLF_TLS_VERSION_MAX_SHIFT 10
#define SSLF_TLS_VERSION_MAX_MASK 0xF /* (uses bit positions 10 to 13) */
+#define SSLF_TLS_DEBUG_ENABLED (1<<14)
unsigned int ssl_flags;
#ifdef MANAGEMENT_DEF_AUTH
@@ -334,7 +358,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 +385,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
@@ -462,6 +482,18 @@ struct tls_session
#define KEY_SCAN_SIZE 3
+/* 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,
+};
+
+
/**
* Security parameter state for a single VPN tunnel.
* @ingroup control_processor
@@ -502,6 +534,7 @@ struct tls_multi
int n_sessions; /**< Number of sessions negotiated thus
* far. */
+ enum client_connect_status multi_state;
/*
* Number of errors.
@@ -517,22 +550,40 @@ 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.
+ */
/* For P_DATA_V2 */
uint32_t peer_id;
@@ -540,13 +591,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..0fe70e4 100644
--- a/src/openvpn/ssl_mbedtls.c
+++ b/src/openvpn/ssl_mbedtls.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
* Copyright (C) 2006-2010, Brainspark B.V.
*
* This program is free software; you can redistribute it and/or modify
@@ -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);
@@ -979,7 +1070,18 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl,
mbedtls_ssl_config_defaults(ks_ssl->ssl_config, ssl_ctx->endpoint,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
#ifdef MBEDTLS_DEBUG_C
- mbedtls_debug_set_threshold(3);
+ /* We only want to have mbed TLS generate debug level logging when we would
+ * also display it.
+ * In fact mbed TLS 2.25.0 crashes generating debug log if Curve25591 is
+ * selected for DH (https://github.com/ARMmbed/mbedtls/issues/4208) */
+ if (session->opt->ssl_flags & SSLF_TLS_DEBUG_ENABLED)
+ {
+ mbedtls_debug_set_threshold(3);
+ }
+ else
+ {
+ mbedtls_debug_set_threshold(2);
+ }
#endif
mbedtls_ssl_conf_dbg(ks_ssl->ssl_config, my_debug, NULL);
mbedtls_ssl_conf_rng(ks_ssl->ssl_config, mbedtls_ctr_drbg_random,
@@ -992,6 +1094,18 @@ 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 TLS renegotiations if the mbedtls library supports that feature.
+ * OpenVPN's renegotiation creates new SSL sessions and does not depend on
+ * this feature and TLS renegotiations have been problematic in the past. */
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+ mbedtls_ssl_conf_renegotiation(ks_ssl->ssl_config, MBEDTLS_SSL_RENEGOTIATION_DISABLED);
+#endif /* MBEDTLS_SSL_RENEGOTIATION */
+
/* 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 +1126,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 +1171,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 +1196,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 +1544,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..c7eaec8 100644
--- a/src/openvpn/ssl_mbedtls.h
+++ b/src/openvpn/ssl_mbedtls.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..aabfc6d
--- /dev/null
+++ b/src/openvpn/ssl_ncp.c
@@ -0,0 +1,342 @@
+/*
+ * 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-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
+ * Copyright (C) 2008-2021 David Sommerseth <dazo@eurephia.org>
+ *
+ * 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 (strcmp(token, "none") == 0)
+ {
+ msg(M_WARN, "WARNING: cipher 'none' specified for --data-ciphers. "
+ "This allows negotiation of NO encryption and "
+ "tunnelled data WILL then be transmitted in clear text "
+ "over the network! "
+ "PLEASE DO RECONSIDER THIS SETTING!");
+ }
+ if (!ktc && strcmp(token, "none") != 0)
+ {
+ msg(M_WARN, "Unsupported cipher in --data-ciphers: %s", token);
+ error_found = true;
+ }
+ else
+ {
+ const char *ovpn_cipher_name = cipher_kt_name(ktc);
+ if (ktc == NULL)
+ {
+ /* NULL resolves to [null-cipher] but we need none for
+ * data-ciphers */
+ ovpn_cipher_name = "none";
+ }
+
+ 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
+ && 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 */
+ if(tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername))
+ {
+ return true;
+ }
+
+ /* We could not figure out the peer's cipher but we have fallback
+ * enabled */
+ if (!c->c2.tls_multi->remote_ciphername && 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;
+ }
+}
diff --git a/src/openvpn/ssl_ncp.h b/src/openvpn/ssl_ncp.h
new file mode 100644
index 0000000..3fa68e2
--- /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-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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..31d94f2 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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,17 +329,17 @@ 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;
+ /* Disable TLS renegotiations. OpenVPN's renegotiation creates new SSL
+ * session and does not depend on this feature. And TLS renegotiations have
+ * been problematic in the past */
+#ifdef SSL_OP_NO_RENEGOTIATION
+ sslopt |= SSL_OP_NO_RENEGOTIATION;
#endif
SSL_CTX_set_options(ctx->ctx, sslopt);
@@ -307,17 +356,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 +373,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 +383,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 +422,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 +442,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 +516,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 +560,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 +628,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 +654,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 +686,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 +720,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 +730,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 +767,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 +779,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 +939,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 +982,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 +1000,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 +1019,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 +1036,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 +1049,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 +1075,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 +1102,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 +1113,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 +1136,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 +1157,7 @@ end:
}
-#ifdef MANAGMENT_EXTERNAL_KEY
+#ifdef ENABLE_MANAGEMENT
/* encrypt */
static int
@@ -1133,7 +1187,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 +1195,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 +1303,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 +1311,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 +1322,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 +1337,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 +1535,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 +1556,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 +1630,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 +1648,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 +1658,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 +1687,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 +1701,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 +1772,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 +1783,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);
}
@@ -1803,6 +2046,80 @@ key_state_read_plaintext(struct key_state_ssl *ks_ssl, struct buffer *buf,
return ret;
}
+/**
+ * Print human readable information about the certifcate into buf
+ * @param cert the certificate being used
+ * @param buf output buffer
+ * @param buflen output buffer length
+ */
+static void
+print_cert_details(X509 *cert, char *buf, size_t buflen)
+{
+ const char *curve = "";
+ const char *type = "(error getting type)";
+ EVP_PKEY *pkey = X509_get_pubkey(cert);
+
+ if (pkey == NULL)
+ {
+ buf[0] = 0;
+ return;
+ }
+
+ int typeid = EVP_PKEY_id(pkey);
+
+#ifndef OPENSSL_NO_EC
+ if (typeid == EVP_PKEY_EC && EVP_PKEY_get0_EC_KEY(pkey) != NULL)
+ {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+
+ int nid = EC_GROUP_get_curve_name(group);
+ if (nid == 0 || (curve = OBJ_nid2sn(nid)) == NULL)
+ {
+ curve = "(error getting curve name)";
+ }
+ }
+#endif
+ if (EVP_PKEY_id(pkey) != 0)
+ {
+ int typeid = EVP_PKEY_id(pkey);
+ type = OBJ_nid2sn(typeid);
+
+ /* OpenSSL reports rsaEncryption, dsaEncryption and
+ * id-ecPublicKey, map these values to nicer ones */
+ if (typeid == EVP_PKEY_RSA)
+ {
+ type = "RSA";
+ }
+ else if (typeid == EVP_PKEY_DSA)
+ {
+ type = "DSA";
+ }
+ else if (typeid == EVP_PKEY_EC)
+ {
+ /* EC gets the curve appended after the type */
+ type = "EC, curve ";
+ }
+ else if (type == NULL)
+ {
+ type = "unknown type";
+ }
+ }
+
+ char sig[128] = { 0 };
+ int signature_nid = X509_get_signature_nid(cert);
+ if (signature_nid != 0)
+ {
+ openvpn_snprintf(sig, sizeof(sig), ", signature: %s",
+ OBJ_nid2sn(signature_nid));
+ }
+
+ openvpn_snprintf(buf, buflen, ", peer certificate: %d bit %s%s%s",
+ EVP_PKEY_bits(pkey), type, curve, sig);
+
+ EVP_PKEY_free(pkey);
+}
+
/* **************************************
*
* Information functions
@@ -1814,7 +2131,6 @@ void
print_details(struct key_state_ssl *ks_ssl, const char *prefix)
{
const SSL_CIPHER *ciph;
- X509 *cert;
char s1[256];
char s2[256];
@@ -1825,55 +2141,20 @@ print_details(struct key_state_ssl *ks_ssl, const char *prefix)
SSL_get_version(ks_ssl->ssl),
SSL_CIPHER_get_version(ciph),
SSL_CIPHER_get_name(ciph));
- cert = SSL_get_peer_certificate(ks_ssl->ssl);
- if (cert != NULL)
- {
- EVP_PKEY *pkey = X509_get_pubkey(cert);
- if (pkey != NULL)
- {
- if ((EVP_PKEY_id(pkey) == EVP_PKEY_RSA) && (EVP_PKEY_get0_RSA(pkey) != NULL))
- {
- RSA *rsa = EVP_PKEY_get0_RSA(pkey);
- openvpn_snprintf(s2, sizeof(s2), ", %d bit RSA",
- RSA_bits(rsa));
- }
- else if ((EVP_PKEY_id(pkey) == EVP_PKEY_DSA) && (EVP_PKEY_get0_DSA(pkey) != NULL))
- {
- DSA *dsa = EVP_PKEY_get0_DSA(pkey);
- openvpn_snprintf(s2, sizeof(s2), ", %d bit DSA",
- DSA_bits(dsa));
- }
-#ifndef OPENSSL_NO_EC
- else if ((EVP_PKEY_id(pkey) == EVP_PKEY_EC) && (EVP_PKEY_get0_EC_KEY(pkey) != NULL))
- {
- EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
- const EC_GROUP *group = EC_KEY_get0_group(ec);
- const char* curve;
-
- int nid = EC_GROUP_get_curve_name(group);
- if (nid == 0 || (curve = OBJ_nid2sn(nid)) == NULL)
- {
- curve = "Error getting curve name";
- }
-
- openvpn_snprintf(s2, sizeof(s2), ", %d bit EC, curve: %s",
- EC_GROUP_order_bits(group), curve);
+ X509 *cert = SSL_get_peer_certificate(ks_ssl->ssl);
- }
-#endif
- EVP_PKEY_free(pkey);
- }
+ if (cert)
+ {
+ print_cert_details(cert, s2, sizeof(s2));
X509_free(cert);
}
- /* The SSL API does not allow us to look at temporary RSA/DH keys,
- * otherwise we should print their lengths too */
msg(D_HANDSHAKE, "%s%s", s1, s2);
}
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 +2164,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 +2186,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 +2203,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 +2230,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 +2241,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 +2293,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..2eeb716 100644
--- a/src/openvpn/ssl_openssl.h
+++ b/src/openvpn/ssl_openssl.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..4f3b61d 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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);
@@ -931,6 +906,39 @@ key_state_test_auth_control_file(struct key_state *ks)
#endif /* ifdef PLUGIN_DEF_AUTH */
+/* This function is called when a session's primary key state first becomes KS_TRUE */
+void ssl_session_fully_authenticated(struct tls_multi *multi, struct tls_session* session)
+{
+ struct key_state *ks = &session->key[KS_PRIMARY];
+ if (ks->key_id == 0)
+ {
+ /* A key id of 0 indicates a new session and the client will
+ * get the auth-token as part of the initial push reply */
+ return;
+ }
+
+ /*
+ * 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)
+ {
+ /*
+ * 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 scheduling
+ */
+ send_push_reply_auth_token(multi);
+ }
+}
/*
* Return current session authentication state. Return
* value is TLS_AUTHENTICATION_x.
@@ -983,7 +991,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 +1008,13 @@ tls_authentication_status(struct tls_multi *multi, const int latency)
case ACF_SUCCEEDED:
case ACF_DISABLED:
success = true;
- ks->auth_deferred = false;
+ /* i=0 is the TM_ACTIVE/KS_PRIMARY session */
+ if (i == 0 && ks->authenticated == KS_AUTH_DEFERRED)
+ {
+ ssl_session_fully_authenticated(multi,
+ &multi->session[TM_ACTIVE]);
+ }
+ ks->authenticated = KS_AUTH_TRUE;
break;
case ACF_UNDEFINED:
@@ -1011,7 +1025,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 +1069,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,79 +1099,66 @@ 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();
const char *tmp_file = "";
bool ret = false;
- /* Is username defined? */
- if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username))
+ /* Set environmental variables prior to calling script */
+ setenv_str(session->opt->es, "script_type", "user-pass-verify");
+
+ /* format command line */
+ argv_parse_cmd(&argv, session->opt->auth_user_pass_verify_script);
+
+ if (session->opt->auth_user_pass_verify_script_via_file)
{
- /* Set environmental variables prior to calling script */
- setenv_str(session->opt->es, "script_type", "user-pass-verify");
+ struct status_output *so;
- if (session->opt->auth_user_pass_verify_script_via_file)
+ tmp_file = platform_create_temp_file(session->opt->tmp_dir, "up",
+ &gc);
+ if (tmp_file)
{
- struct status_output *so;
-
- tmp_file = create_temp_file(session->opt->tmp_dir, "up", &gc);
- if (tmp_file)
+ so = status_open(tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
+ status_printf(so, "%s", up->username);
+ status_printf(so, "%s", up->password);
+ if (!status_close(so))
{
- so = status_open(tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
- status_printf(so, "%s", up->username);
- status_printf(so, "%s", up->password);
- if (!status_close(so))
- {
- msg(D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s",
- tmp_file);
- goto done;
- }
- }
- else
- {
- msg(D_TLS_ERRORS, "TLS Auth Error: could not create write "
- "username/password to temp file");
+ msg(D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s",
+ tmp_file);
+ goto done;
}
+ /* pass temp file name to script */
+ argv_printf_cat(&argv, "%s", tmp_file);
}
else
{
- setenv_str(session->opt->es, "username", up->username);
- setenv_str(session->opt->es, "password", up->password);
- }
-
- /* setenv incoming cert common name for script */
- setenv_str(session->opt->es, "common_name", session->common_name);
-
- /* setenv client real IP address */
- setenv_untrusted(session);
-
- /* format command line */
- argv_parse_cmd(&argv, session->opt->auth_user_pass_verify_script);
- argv_printf_cat(&argv, "%s", tmp_file);
-
- /* call command */
- ret = openvpn_run_script(&argv, session->opt->es, 0,
- "--auth-user-pass-verify");
-
- if (!session->opt->auth_user_pass_verify_script_via_file)
- {
- setenv_del(session->opt->es, "password");
+ msg(D_TLS_ERRORS, "TLS Auth Error: could not create write "
+ "username/password to temp file");
}
}
else
{
- msg(D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
+ setenv_str(session->opt->es, "password", up->password);
}
+ /* call command */
+ ret = openvpn_run_script(&argv, session->opt->es, 0,
+ "--auth-user-pass-verify");
+
+ if (!session->opt->auth_user_pass_verify_script_via_file)
+ {
+ setenv_del(session->opt->es, "password");
+ }
done:
if (tmp_file && strlen(tmp_file) > 0)
{
platform_unlink(tmp_file);
}
- argv_reset(&argv);
+ argv_free(&argv);
gc_free(&gc);
return ret;
}
@@ -1166,59 +1167,40 @@ 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
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
#endif
- /* Is username defined? */
- if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen(up->username))
- {
- /* set username/password in private env space */
- setenv_str(session->opt->es, "username", (raw_username ? raw_username : up->username));
- setenv_str(session->opt->es, "password", up->password);
-
- /* setenv incoming cert common name for script */
- setenv_str(session->opt->es, "common_name", session->common_name);
-
- /* setenv client real IP address */
- setenv_untrusted(session);
+ /* set password in private env space */
+ setenv_str(session->opt->es, "password", up->password);
#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;
- }
+ /* 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__);
+ return retval;
+ }
#endif
- /* call command */
- retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
+ /* call command */
+ retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
#ifdef PLUGIN_DEF_AUTH
- /* purge auth control filename (and file itself) for non-deferred returns */
- if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
- {
- key_state_rm_auth_control_file(ks);
- }
-#endif
-
- setenv_del(session->opt->es, "password");
- if (raw_username)
- {
- setenv_str(session->opt->es, "username", up->username);
- }
- }
- else
+ /* purge auth control filename (and file itself) for non-deferred returns */
+ if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
{
- msg(D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
+ key_state_rm_auth_control_file(ks);
}
+#endif
+
+ setenv_del(session->opt->es, "password");
-cleanup:
return retval;
}
@@ -1233,17 +1215,37 @@ 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 */
+ /* set username/password in private env space */
+ setenv_str(session->opt->es, "password", up->password);
+
+ 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");
+
+ retval = KMDA_SUCCESS;
+
+ return retval;
+}
+#endif /* ifdef MANAGEMENT_DEF_AUTH */
+
+static bool
+set_verify_user_pass_env(struct user_pass *up, struct tls_multi *multi,
+ struct tls_session *session)
+{
/* Is username defined? */
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, "password", up->password);
+ setenv_str(session->opt->es, "username", up->username);
/* setenv incoming cert common name for script */
setenv_str(session->opt->es, "common_name", session->common_name);
@@ -1251,30 +1253,25 @@ verify_user_pass_management(struct tls_session *session, const struct user_pass
/* setenv client real IP address */
setenv_untrusted(session);
- 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;
+ /*
+ * 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);
+ return true;
}
else
{
- msg(D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
+ msg(D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
+ return false;
}
-
- return retval;
}
-#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 +1281,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 +1290,90 @@ 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)
+ ks->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 (ks->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)
+ /* Set the environment variables used by all auth variants */
+ if (!set_verify_user_pass_env(up, multi, session))
{
- man_def_auth = verify_user_pass_management(session, up, raw_username);
- }
-#endif
- if (plugin_defined(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
- {
- s1 = verify_user_pass_plugin(session, up, raw_username);
+ skip_auth = true;
+ s1 = OPENVPN_PLUGIN_FUNC_ERROR;
}
- if (session->opt->auth_user_pass_verify_script)
+
+ /* call plugin(s) and/or script */
+ if (!skip_auth)
{
- s2 = verify_user_pass_script(session, up);
+#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, 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 +1385,59 @@ 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
+ && (ks->auth_token_state_flags & AUTH_TOKEN_HMAC_OK)
+ && !(ks->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);
- }
-
- if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME))
- {
- set_common_name(session, up->username);
+ generate_auth_token(up, multi);
}
-
-#ifdef ENABLE_DEF_AUTH
- msg(D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
- 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",
+ (ks->authenticated == KS_AUTH_DEFERRED) ? "deferred" : "succeeded",
up->username,
(session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
-#endif
+ if (ks->authenticated == KS_AUTH_TRUE)
+ {
+ ssl_session_fully_authenticated(multi, session);
+ }
}
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 +1452,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 +1468,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 +1482,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 +1517,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..ffba6a9 100644
--- a/src/openvpn/ssl_verify.h
+++ b/src/openvpn/ssl_verify.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..ca04261 100644
--- a/src/openvpn/ssl_verify_backend.h
+++ b/src/openvpn/ssl_verify_backend.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..c767178 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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_mbedtls.h b/src/openvpn/ssl_verify_mbedtls.h
index 00dc8a3..6f2de99 100644
--- a/src/openvpn/ssl_verify_mbedtls.h
+++ b/src/openvpn/ssl_verify_mbedtls.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index b1ce06b..aadc517 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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/ssl_verify_openssl.h b/src/openvpn/ssl_verify_openssl.h
index 118e16f..70a9d50 100644
--- a/src/openvpn/ssl_verify_openssl.h
+++ b/src/openvpn/ssl_verify_openssl.h
@@ -5,8 +5,8 @@
* 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) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/src/openvpn/status.c b/src/openvpn/status.c
index 91391d1..8476b4d 100644
--- a/src/openvpn/status.c
+++ b/src/openvpn/status.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..da1775d 100644
--- a/src/openvpn/status.h
+++ b/src/openvpn/status.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..24ee27c 100644
--- a/src/openvpn/syshead.h
+++ b/src/openvpn/syshead.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..663f5e1 100644
--- a/src/openvpn/tls_crypt.c
+++ b/src/openvpn/tls_crypt.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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, &timestamp, 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..81d0a10 100644
--- a/src/openvpn/tls_crypt.h
+++ b/src/openvpn/tls_crypt.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -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..512ccba 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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)
@@ -64,14 +68,18 @@
#define NI_OPTIONS (1<<2)
static void netsh_ifconfig(const struct tuntap_options *to,
- const char *flex_name,
+ DWORD adapter_index,
const in_addr_t ip,
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);
+ DWORD adapter_index);
static void netsh_command(const struct argv *a, int n, int msglevel);
@@ -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,20 +113,23 @@ 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);
+ msg(D_IFCONFIG, "INET address service: %s %s/%d",
+ add ? "add" : "remove",
+ print_in_addr_t(tt->local, 0, &gc), addr.prefix_len);
}
else
{
addr.address.ipv6 = tt->local_ipv6;
- addr.prefix_len = tt->netbits_ipv6;
+ addr.prefix_len = (tt->type == DEV_TYPE_TUN) ? 128 : tt->netbits_ipv6;
+ msg(D_IFCONFIG, "INET6 address service: %s %s/%d",
+ add ? "add" : "remove",
+ print_in6_addr(tt->local_ipv6, 0, &gc), addr.prefix_len);
}
- 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,20 +149,77 @@ out:
}
static bool
-do_dns6_service(bool add, const struct tuntap *tt)
+do_dns_domain_service(bool add, 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;
+
+ if (!tt->options.domain) /* no domain to add or delete */
+ {
+ return true;
+ }
+
+ /* Use dns_cfg_msg with addr_len = 0 for setting only the DOMAIN */
+ dns_cfg_message_t dns = {
+ .header = {
+ (add ? msg_add_dns_cfg : msg_del_dns_cfg),
+ sizeof(dns_cfg_message_t),
+ 0
+ },
+ .iface = { .index = tt->adapter_index, .name = "" },
+ .domains = "", /* set below */
+ .family = AF_INET, /* unused */
+ .addr_len = 0 /* add/delete only the domain, not DNS servers */
+ };
+
+ strncpynt(dns.iface.name, tt->actual_name, sizeof(dns.iface.name));
+ strncpynt(dns.domains, tt->options.domain, sizeof(dns.domains));
+ /* truncation of domain name is not checked as it can't happen
+ * with 512 bytes room in dns.domains.
+ */
+
+ msg(D_LOW, "%s dns domain on '%s' (if_index = %d) using service",
+ (add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index);
+ if (!send_msg_iservice(pipe, &dns, sizeof(dns), &ack, "TUN"))
+ {
+ goto out;
+ }
+
+ if (ack.error_number != NO_ERROR)
+ {
+ msg(M_WARN, "TUN: %s dns domain failed using service: %s [status=%u if_name=%s]",
+ (add ? "adding" : "deleting"), strerror_win32(ack.error_number, &gc),
+ ack.error_number, dns.iface.name);
+ goto out;
+ }
+
+ msg(M_INFO, "DNS domain %s using service", (add ? "set" : "deleted"));
+ ret = true;
+
+out:
+ gc_free(&gc);
+ return ret;
+}
+
+static bool
+do_dns_service(bool add, const short family, const struct tuntap *tt)
+{
+ bool ret = false;
+ ack_message_t ack;
+ struct gc_arena gc = gc_new();
+ HANDLE pipe = tt->options.msg_channel;
+ 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 */
{
return true;
}
+ /* Use dns_cfg_msg with domain = "" for setting only the DNS servers */
dns_cfg_message_t dns = {
.header = {
(add ? msg_add_dns_cfg : msg_del_dns_cfg),
@@ -161,7 +228,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 +240,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 +280,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 +459,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 +567,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 +668,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 +709,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 +744,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 +836,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 +896,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 +940,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 +952,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 +967,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 +998,612 @@ 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_printf(&argv, "%s %s inet6 addif %s/%d up", IFCONFIG_PATH,
+ ifname, 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, ifname, true);
+ }
+
+ 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 ip link set failed");
+ 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 (tun)
- {
+ openvpn_execve_check(&argv, es, S_FATAL,
+ "generic BSD ifconfig inet6 failed");
+
+#if defined(TARGET_FREEBSD) && __FreeBSD_version >= 1200000
+ /* On FreeBSD 12 and up, there is ipv6_activate_all_interfaces="YES"
+ * in rc.conf, which is not set by default. If it is *not* set,
+ * "all new interfaces that are not already up" are configured by
+ * devd + /etc/pccard_ether as "inet6 ifdisabled".
+ *
+ * The "is this interface already up?" test is a non-zero time window
+ * which we manage to hit with our ifconfig often enough to cause
+ * frequent fails in the openvpn test environment.
+ *
+ * Thus: assume that the system might interfere, wait for things to
+ * settle (it's a very short time window), and remove -ifdisable again.
+ *
+ * See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=248172
+ */
+ sleep(1);
+ argv_printf(&argv, "%s %s inet6 -ifdisabled", IFCONFIG_PATH, ifname);
+ argv_msg(M_INFO, &argv);
- /*
- * 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)
+ openvpn_execve_check(&argv, es, S_FATAL,
+ "FreeBSD BSD 'ifconfig inet6 -ifdisabled' failed");
+#endif
+
+#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);
+
+ /* 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");
+
+ openvpn_execve_check(&argv, es, S_FATAL,
+ "generic BSD ifconfig inet6 failed");
+
+ 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);
+ if (tt->type == DEV_TYPE_TUN)
{
- argv_printf(&argv,
- "%s %s %s pointopoint %s mtu %d",
- IFCONFIG_PATH,
- actual,
- ifconfig_local,
- ifconfig_remote_netmask,
- tun_mtu
- );
+ add_route_connected_v6_net(tt, es);
}
- else
+ do_dns_service(true, AF_INET6, tt);
+ do_set_mtu_service(tt, AF_INET6, tun_mtu);
+ /* If IPv4 is not enabled, set DNS domain here */
+ if (!tt->did_ifconfig_setup)
{
- argv_printf(&argv,
- "%s %s %s netmask %s mtu %d broadcast %s",
- IFCONFIG_PATH,
- actual,
- ifconfig_local,
- ifconfig_remote_netmask,
- tun_mtu,
- ifconfig_broadcast
- );
+ do_dns_domain_service(true, tt);
}
- 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)
+ }
+ else
+ {
+ /* example: netsh interface ipv6 set address 42
+ * 2001:608:8003::d/bits store=active
+ */
- if (do_ipv6)
+ /* in TUN mode, we only simulate a subnet, so the interface
+ * is configured with /128 + a route to fe80::8. In TAP mode,
+ * the correct netbits must be set, and no on-link route
+ */
+ int netbits = (tt->type == DEV_TYPE_TUN) ? 128 : tt->netbits_ipv6;
+
+ argv_printf(&argv, "%s%s interface ipv6 set address %lu %s/%d store=active",
+ get_win_sys_path(), NETSH_PATH_SUFFIX, tt->adapter_index,
+ ifconfig_ipv6_local, netbits);
+ netsh_command(&argv, 4, M_FATAL);
+ if (tt->type == DEV_TYPE_TUN)
{
- 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));
+ 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, tt->adapter_index);
+ 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 */
- struct buffer out = alloc_buf_gc(64, &gc);
-
- char *top;
- switch (tt->topology)
- {
- case TOP_NET30:
- top = "net30";
- break;
+#if !defined(TARGET_LINUX)
+ gc_free(&gc);
+ argv_free(&argv);
+#endif
+}
- case TOP_P2P:
- top = "p2p";
- break;
+/**
+ * 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);
- case TOP_SUBNET:
- top = "subnet";
- break;
+#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();
- default:
- top = "undef";
- }
+ /*
+ * 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
- 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)
+ if (net_iface_mtu_set(ctx, ifname, tun_mtu) < 0)
+ {
+ msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, ifname);
+ }
-#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);
- }
+ if (net_iface_up(ctx, ifname, true) < 0)
+ {
+ msg(M_FATAL, "Linux can't bring %s up", ifname);
+ }
- argv_printf(&argv,
- "%s %s netmask 255.255.255.255",
- IFCONFIG_PATH,
- actual
- );
- }
- else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)
+ if (tun)
+ {
+ if (net_addr_ptp_v4_add(ctx, ifname, &tt->local,
+ &tt->remote_netmask) < 0)
{
- argv_printf(&argv,
- "%s %s %s %s netmask %s mtu %d up",
- IFCONFIG_PATH,
- actual,
- ifconfig_local,
- ifconfig_local,
- ifconfig_remote_netmask,
- tun_mtu
- );
+ msg(M_FATAL, "Linux can't add IP to interface %s", ifname);
}
- else
+ }
+ else
+ {
+ if (net_addr_v4_add(ctx, ifname, &tt->local,
+ netmask_to_netbits2(tt->remote_netmask)) < 0)
{
- argv_printf(&argv,
- " %s %s %s netmask %s broadcast + up",
- IFCONFIG_PATH,
- actual,
- ifconfig_local,
- ifconfig_remote_netmask
- );
+ msg(M_FATAL, "Linux can't add IP to interface %s", ifname);
}
+ }
+#elif defined(TARGET_ANDROID)
+ char out[64];
- argv_msg(M_INFO, &argv);
- if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed"))
- {
- solaris_error_close(tt, es, actual, false);
- }
+ char *top;
+ switch (tt->topology)
+ {
+ case TOP_NET30:
+ top = "net30";
+ break;
- 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);
+ case TOP_P2P:
+ top = "p2p";
+ break;
- 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);
- }
+ case TOP_SUBNET:
+ top = "subnet";
+ break;
- /* 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);
- }
- }
+ default:
+ top = "undef";
+ }
+
+ openvpn_snprintf(out, sizeof(out), "%s %s %d %s", ifconfig_local,
+ ifconfig_remote_netmask, tun_mtu, top);
+ management_android_control(management, "IFCONFIG", out);
+
+#elif defined(TARGET_SOLARIS)
+ /* Solaris 2.6 (and 7?) cannot set all parameters in one go...
+ * example:
+ * ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up
+ * 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);
- if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)
+ argv_msg(M_INFO, &argv);
+ if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-1 failed"))
{
- /* 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);
+ solaris_error_close(tt, es, ifname, false);
}
- tt->did_ifconfig = true;
+ argv_printf(&argv, "%s %s netmask 255.255.255.255", IFCONFIG_PATH,
+ ifname);
+ }
+ 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,
+ 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);
+ }
+
+ argv_msg(M_INFO, &argv);
+ if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed"))
+ {
+ solaris_error_close(tt, es, ifname, false);
+ }
+
+ if (!tun && tt->type == DEV_TYPE_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);
+ }
#elif defined(TARGET_OPENBSD)
- in_addr_t remote_end; /* for "virtual" subnet topology */
+ in_addr_t remote_end; /* for "virtual" subnet topology */
- /*
- * On OpenBSD, tun interfaces are persistent if created with
- * "ifconfig tunX create", and auto-destroyed if created by
- * opening "/dev/tunX" (so we just use the /dev/tunX)
- */
+ /*
+ * 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)
+ */
- /* 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");
+ /* 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->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, 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");
- /* 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);
- }
-
- 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");
-
- /* 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->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, NULL);
+ }
#elif defined(TARGET_NETBSD)
+ in_addr_t remote_end; /* for "virtual" subnet topology */
- 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,
- 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");
+ 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->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,
+ ifname, 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",
+ 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");
- /* 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);
- }
-
- 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");
-
- /* 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->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, NULL);
+ }
#elif defined(TARGET_DARWIN)
- /*
- * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
- */
+ /*
+ * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
+ */
- 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");
+ 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->type == DEV_TYPE_TUN && 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_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, "Mac OS X ifconfig failed");
- tt->did_ifconfig = true;
+ argv_msg(M_INFO, &argv);
+ openvpn_execve_check(&argv, es, S_FATAL, "Mac OS X 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 = tt->local;
- add_route(&r, tt, 0, NULL, es);
- }
-
- 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");
-
- /* 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->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, 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->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,
+ 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->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, 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);
+ 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);
+ do_dns_domain_service(true, tt);
+ }
+ else if (tt->options.ip_win32_type == IPW32_SET_NETSH)
+ {
+ netsh_ifconfig(&tt->options, tt->adapter_index, 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) */
- 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 !defined(TARGET_LINUX)
+ gc_free(&gc);
+ argv_free(&argv);
+#endif
+}
- case IPW32_SET_NETSH:
- netsh_ifconfig(&tt->options,
- actual,
- tt->local,
- tt->adapter_netmask,
- NI_IP_NETMASK|NI_OPTIONS);
+/* 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);
- break;
- }
- tt->did_ifconfig = true;
- }
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_set_state(management,
+ OPENVPN_STATE_ASSIGN_IP,
+ NULL,
+ &tt->local,
+ &tt->local_ipv6,
+ NULL,
+ NULL);
+ }
+#endif
- 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);
- }
+ if (tt->did_ifconfig_setup)
+ {
+ do_ifconfig_ipv4(tt, ifname, tun_mtu, es, ctx);
+ }
- /* 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_ipv6_setup)
+ {
+ do_ifconfig_ipv6(tt, ifname, tun_mtu, es, ctx);
}
- gc_free(&gc);
+
+ /* release resources potentially allocated during interface setup */
+ net_ctx_free(ctx);
}
static void
@@ -1913,13 +1913,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 +2064,19 @@ 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
+/* TUNSETGROUP appeared in 2.6.23 */
+#ifndef TUNSETGROUP
+# define TUNSETGROUP _IOW('T', 206, int)
+#endif
+
void
-tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+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;
@@ -2106,89 +2112,98 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_
}
else if (ioctl(tt->fd, TUNSETGROUP, platform_state_group.gr->gr_gid) < 0)
{
- msg(M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", groupname, dev);
+ msg(M_ERR, "Cannot ioctl TUNSETGROUP(%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 (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
+ if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local,
+ &tt->remote_netmask) < 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
+ {
+ if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0)
+ {
+ 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 */
+}
+
+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_reset(&argv);
- gc_free(&gc);
+ 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 +2461,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 +2516,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 +2552,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 +2615,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 +2698,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 +2842,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 +2958,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 +3053,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 +3080,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 +3124,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 +3224,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 +3348,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 +3372,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 +3399,7 @@ close_tun(struct tuntap *tt)
free(tt);
env_set_destroy(es);
+ argv_free(&argv);
}
int
@@ -3393,6 +3416,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 +3643,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 +3855,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 +3896,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 +4103,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 +4115,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 +4165,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 +4176,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 +4199,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 +4220,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 +4234,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 +4284,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 +4322,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 +4335,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 +4832,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 +4896,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 +5036,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 +5079,7 @@ tap_allow_nonadmin_access(const char *dev_node)
sizeof(actual_buffer),
tap_reg,
panel_reg,
+ NULL,
&gc);
if (!device_guid)
@@ -5026,7 +5212,7 @@ netsh_command(const struct argv *a, int n, int msglevel)
for (i = 0; i < n; ++i)
{
bool status;
- management_sleep(1);
+ management_sleep(0);
netcmd_semaphore_lock();
argv_msg_prefix(M_INFO, a, "NETSH");
status = openvpn_execve_check(a, NULL, 0, "ERROR: netsh command failed");
@@ -5049,19 +5235,18 @@ 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_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...");
@@ -5157,23 +5342,29 @@ ip_addr_member_of(const in_addr_t addr, const IP_ADDR_STRING *ias)
* Set the ipv6 dns servers on the specified interface.
* The list of dns servers currently set on the interface
* are cleared first.
- * No action is taken if number of addresses (addr_len) < 1.
*/
static void
netsh_set_dns6_servers(const struct in6_addr *addr_list,
const int addr_len,
- const char *flex_name)
+ DWORD adapter_index)
{
struct gc_arena gc = gc_new();
struct argv argv = argv_new();
+ /* delete existing DNS settings from TAP interface */
+ argv_printf(&argv, "%s%s interface ipv6 delete dns %lu all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ adapter_index);
+ netsh_command(&argv, 2, M_FATAL);
+
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 %lu static %s"
+ : "%s%s interface ipv6 add dns %lu %s";
argv_printf(&argv, fmt, get_win_sys_path(),
- NETSH_PATH_SUFFIX, flex_name,
+ NETSH_PATH_SUFFIX, adapter_index,
print_in6_addr(addr_list[i], 0, &gc));
/* disable slow address validation on Windows 7 and higher */
@@ -5186,7 +5377,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);
}
@@ -5195,12 +5386,13 @@ netsh_ifconfig_options(const char *type,
const in_addr_t *addr_list,
const int addr_len,
const IP_ADDR_STRING *current,
- const char *flex_name,
+ DWORD adapter_index,
const bool test_first)
{
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,11 +5410,11 @@ 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 %lu all",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
type,
- flex_name);
+ adapter_index);
netsh_command(&argv, 2, M_FATAL);
}
@@ -5235,30 +5427,38 @@ 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 %lu %s"
+ : "%s%s interface ip set %s %lu static %s";
argv_printf(&argv, fmt,
get_win_sys_path(),
NETSH_PATH_SUFFIX,
type,
- flex_name,
+ adapter_index,
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;
}
else
{
- msg(M_INFO, "NETSH: \"%s\" %s %s [already set]",
- flex_name,
+ msg(M_INFO, "NETSH: %lu %s %s [already set]",
+ adapter_index,
type,
print_in_addr_t(addr_list[i], 0, &gc));
}
}
}
- argv_reset(&argv);
+ argv_free(&argv);
gc_free(&gc);
}
@@ -5282,7 +5482,7 @@ init_ip_addr_string2(IP_ADDR_STRING *dest, const IP_ADDR_STRING *src1, const IP_
static void
netsh_ifconfig(const struct tuntap_options *to,
- const char *flex_name,
+ DWORD adapter_index,
const in_addr_t ip,
const in_addr_t netmask,
const unsigned int flags)
@@ -5295,27 +5495,26 @@ netsh_ifconfig(const struct tuntap_options *to,
if (flags & NI_TEST_FIRST)
{
const IP_ADAPTER_INFO *list = get_adapter_info_list(&gc);
- const int index = get_adapter_index_flexible(flex_name);
- ai = get_adapter(list, index);
- pai = get_per_adapter_info(index, &gc);
+ ai = get_adapter(list, adapter_index);
+ pai = get_per_adapter_info(adapter_index, &gc);
}
if (flags & NI_IP_NETMASK)
{
if (test_adapter_ip_netmask(ai, ip, netmask))
{
- msg(M_INFO, "NETSH: \"%s\" %s/%s [already set]",
- flex_name,
+ msg(M_INFO, "NETSH: %lu %s/%s [already set]",
+ adapter_index,
print_in_addr_t(ip, 0, &gc),
print_in_addr_t(netmask, 0, &gc));
}
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",
+ /* example: netsh interface ip set address 42 static 10.3.0.1 255.255.255.0 */
+ argv_printf(&argv, "%s%s interface ip set address %lu static %s %s",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
- flex_name,
+ adapter_index,
print_in_addr_t(ip, 0, &gc),
print_in_addr_t(netmask, 0, &gc));
@@ -5334,7 +5533,7 @@ netsh_ifconfig(const struct tuntap_options *to,
to->dns,
to->dns_len,
pai ? &pai->DnsServerList : NULL,
- flex_name,
+ adapter_index,
BOOL_CAST(flags & NI_TEST_FIRST));
if (ai && ai->HaveWins)
{
@@ -5345,36 +5544,35 @@ netsh_ifconfig(const struct tuntap_options *to,
to->wins,
to->wins_len,
ai ? wins : NULL,
- flex_name,
+ adapter_index,
BOOL_CAST(flags & NI_TEST_FIRST));
}
- argv_reset(&argv);
+ argv_free(&argv);
gc_free(&gc);
}
static void
-netsh_enable_dhcp(const char *actual_name)
+netsh_enable_dhcp(DWORD adapter_index)
{
struct argv argv = argv_new();
- /* example: netsh interface ip set address my-tap dhcp */
+ /* example: netsh interface ip set address 42 dhcp */
argv_printf(&argv,
- "%s%sc interface ip set address %s dhcp",
+ "%s%s interface ip set address %lu dhcp",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
- actual_name);
+ adapter_index);
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 +5587,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 +5608,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 +5662,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;
}
@@ -5474,7 +5708,7 @@ tun_standby(struct tuntap *tt)
{
msg(M_INFO, "NOTE: now trying netsh (this may take some time)");
netsh_ifconfig(&tt->options,
- tt->actual_name,
+ tt->adapter_index,
tt->local,
tt->adapter_netmask,
NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
@@ -5556,6 +5790,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 +5883,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 +5928,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 +5938,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 +5964,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 +6052,670 @@ 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.");
- }
+ status = FlushIpNetTable(index);
+ }
- /* 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 (status == NO_ERROR)
+ {
+ msg(M_INFO, "Successful ARP Flush on interface [%lu] %s",
+ index,
+ device_guid);
+ }
+ else if (status != -1)
+ {
+ msg(D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%lu] %s (status=%lu) : %s",
+ index,
+ device_guid,
+ status,
+ strerror_win32(status, &gc));
+ }
- if (tt->hand == INVALID_HANDLE_VALUE)
- {
- msg(D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path);
- }
- else
- {
- break;
- }
+ /*
+ * 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_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'");
+ }
- device_number++;
+ /* force an explicit DHCP lease renewal on TAP adapter? */
+ if (tt->options.dhcp_pre_release)
+ {
+ dhcp_release(tt);
+ }
+ if (tt->options.dhcp_renew)
+ {
+ dhcp_renew(tt);
}
}
-
- /* translate high-level device name into a device instance
- * GUID using the registry */
- tt->actual_name = string_alloc(actual_buffer, NULL);
+ else
+ {
+ fork_dhcp_action(tt);
+ }
}
- 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 */
+ if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI)
{
- ULONG info[3];
- CLEAR(info);
- if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION,
- &info, sizeof(info),
- &info, sizeof(info), &len, NULL))
- {
- msg(D_TUNTAP_INFO, "TAP-Windows Driver Version %d.%d %s",
- (int) info[0],
- (int) info[1],
- (info[2] ? "(DEBUG)" : ""));
+ 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')";
- }
- if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR))
+ /* couldn't get adapter index */
+ if (index == TUN_ADAPTER_INDEX_INVALID)
{
- 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(M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s",
+ device_guid,
+ error_suffix);
}
- /* 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)
+ /* 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, "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'");
}
- /* 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)
+ /* 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)
{
- 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] );
+ 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
+ );
}
- }
-
- /* get driver MTU */
- {
- ULONG mtu;
- if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU,
- &mtu, sizeof(mtu),
- &mtu, sizeof(mtu), &len, NULL))
+ else
{
- tt->post_open_mtu = (int) mtu;
- msg(D_MTU_INFO, "TAP-Windows MTU=%d", (int) mtu);
+ 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;
}
- /*
- * Preliminaries for setting TAP-Windows adapter TCP/IP
- * properties via --ip-win32 dynamic or --ip-win32 adaptive.
- */
- if (tt->did_ifconfig_setup)
+ gc_free(&gc);
+}
+
+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->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
- {
- /*
- * 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;
- }
- else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
+ ret = service_register_ring_buffers(tt);
+ }
+ else
+ {
+ if (!register_ring_buffers(tt->hand,
+ tt->wintun_send_ring,
+ tt->wintun_receive_ring,
+ tt->rw_handle.read,
+ tt->rw_handle.write))
{
- /*
- * 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
+ switch (GetLastError())
{
- dhcp_masq = true;
+ case ERROR_ACCESS_DENIED:
+ 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).");
+ break;
+
+ case ERROR_ALREADY_INITIALIZED:
+ msg(M_NONFATAL, "Adapter %s is already in use", device_guid);
+ break;
+
+ default:
+ msg(M_NONFATAL | M_ERRNO, "Failed to register ring buffers");
}
+ ret = false;
}
+
}
+ return ret;
+}
- /* set point-to-point mode if TUN device */
+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.");
+ }
- if (tt->type == DEV_TYPE_TUN)
+ int s = tt->options.tap_sleep;
+ if (s > 0)
{
- if (!tt->did_ifconfig_setup)
- {
- msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig");
- }
+ msg(M_INFO, "Sleeping for %d seconds...", s);
+ management_sleep(s);
+ }
+}
- if (tt->topology == TOP_SUBNET)
- {
- in_addr_t ep[3];
- BOOL status;
+static void
+tuntap_set_ptp(const struct tuntap *tt)
+{
+ DWORD len;
+ struct gc_arena gc = gc_new();
- ep[0] = htonl(tt->local);
- ep[1] = htonl(tt->local & tt->remote_netmask);
- ep[2] = htonl(tt->remote_netmask);
+ if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup)
+ {
+ msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig");
+ }
- status = DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_TUN,
- ep, sizeof(ep),
- ep, sizeof(ep), &len, NULL);
+ /* 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(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call");
- }
- }
- else
+ 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_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 */
+
+ gc_free(&gc);
+}
- /* set driver media status to 'connected' */
+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;
+ }
+
+ 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)
+ {
+ msg(D_TUNTAP_INFO, "CreateFile failed on %s device: %s", print_windows_driver(tt->windows_driver), path);
+ return false;
+ }
- /* possible wait for adapter to come up */
+ if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
{
- int s = tt->options.tap_sleep;
- if (s > 0)
+ /* 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;
- /* flush arp cache */
- if (index != TUN_ADAPTER_INDEX_INVALID)
+ /* 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);
+
+ 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 or disabled.", 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->adapter_index);
}
}
+ *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->adapter_index,
+ 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 +6733,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 +6748,176 @@ 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 %lu all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ ipv6 ? "ipv6" : "ipv4",
+ tt->adapter_index);
+ 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 && tt->options.wins_len > 0)
+ {
+ argv_printf(&argv,
+ "%s%s interface ipv4 delete winsservers %lu all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ tt->adapter_index);
+ netsh_command(&argv, 1, M_WARN);
+ }
- /* "store=active" is needed in Windows 8(.1) to delete the
- * address we added (pointed out by Cedric Tabary).
- */
+ if (ipv6 && tt->type == DEV_TYPE_TUN)
+ {
+ delete_route_connected_v6_net(tt);
+ }
- /* 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);
+ /* "store=active" is needed in Windows 8(.1) to delete the
+ * address we added (pointed out by Cedric Tabary).
+ */
- netsh_command(&argv, 1, M_WARN);
+ /* netsh interface ipvX delete address %lu %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 %lu %s store=active",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ ipv6 ? "ipv6" : "ipv4",
+ tt->adapter_index,
+ ifconfig_ip_local);
+ netsh_command(&argv, 1, M_WARN);
- /* 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);
- }
+ argv_free(&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 IPv4 is not enabled, delete DNS domain here */
+ if (!tt->did_ifconfig_setup)
{
- 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_domain_service(false, tt);
}
+ if (tt->options.dns6_len > 0)
+ {
+ 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_domain_service(false, tt);
+ 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");
}
+ }
+
+ dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter");
+ overlapped_io_close(&tt->reads);
- if (tt->actual_name)
+ 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 +6994,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..ff0919d 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..573a990
--- /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-2021 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..d4b93c4
--- /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-2021 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..6cff17b 100644
--- a/src/openvpn/win32.c
+++ b/src/openvpn/win32.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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..5d3371a 100644
--- a/src/openvpn/win32.h
+++ b/src/openvpn/win32.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -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 */
diff --git a/src/openvpnmsica/Makefile.am b/src/openvpnmsica/Makefile.am
new file mode 100644
index 0000000..0fdc1f6
--- /dev/null
+++ b/src/openvpnmsica/Makefile.am
@@ -0,0 +1,56 @@
+#
+# openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+#
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+#
+# 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.
+#
+
+include $(top_srcdir)/build/ltrc.inc
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+EXTRA_DIST = \
+ openvpnmsica.vcxproj \
+ openvpnmsica.vcxproj.filters \
+ openvpnmsica.props \
+ openvpnmsica-Debug.props \
+ openvpnmsica-Release.props
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/compat
+
+AM_CFLAGS = \
+ $(TAP_CFLAGS)
+
+if WIN32
+lib_LTLIBRARIES = libopenvpnmsica.la
+libopenvpnmsica_la_CFLAGS = \
+ -municode -D_UNICODE \
+ -UNTDDI_VERSION -U_WIN32_WINNT \
+ -D_WIN32_WINNT=_WIN32_WINNT_VISTA \
+ -Wl,--kill-at
+libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshell32 -lshlwapi -lversion -no-undefined -avoid-version
+endif
+
+libopenvpnmsica_la_SOURCES = \
+ dllmain.c \
+ msiex.c msiex.h \
+ msica_arg.c msica_arg.h \
+ openvpnmsica.c openvpnmsica.h \
+ $(top_srcdir)/src/tapctl/basic.h \
+ $(top_srcdir)/src/tapctl/error.c $(top_srcdir)/src/tapctl/error.h \
+ $(top_srcdir)/src/tapctl/tap.c $(top_srcdir)/src/tapctl/tap.h \
+ openvpnmsica_resources.rc
diff --git a/src/openvpnmsica/Makefile.in b/src/openvpnmsica/Makefile.in
new file mode 100644
index 0000000..b21ea04
--- /dev/null
+++ b/src/openvpnmsica/Makefile.in
@@ -0,0 +1,865 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+
+#
+# openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+#
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+#
+# 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.
+#
+
+#
+# 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@
+subdir = src/openvpnmsica
+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__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libopenvpnmsica_la_LIBADD =
+am_libopenvpnmsica_la_OBJECTS = libopenvpnmsica_la-dllmain.lo \
+ libopenvpnmsica_la-msiex.lo libopenvpnmsica_la-msica_arg.lo \
+ libopenvpnmsica_la-openvpnmsica.lo libopenvpnmsica_la-error.lo \
+ libopenvpnmsica_la-tap.lo openvpnmsica_resources.lo
+libopenvpnmsica_la_OBJECTS = $(am_libopenvpnmsica_la_OBJECTS)
+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 =
+libopenvpnmsica_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) \
+ $(libopenvpnmsica_la_LDFLAGS) $(LDFLAGS) -o $@
+@WIN32_TRUE@am_libopenvpnmsica_la_rpath = -rpath $(libdir)
+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)/libopenvpnmsica_la-dllmain.Plo \
+ ./$(DEPDIR)/libopenvpnmsica_la-error.Plo \
+ ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo \
+ ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo \
+ ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo \
+ ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo
+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 = $(libopenvpnmsica_la_SOURCES)
+DIST_SOURCES = $(libopenvpnmsica_la_SOURCES)
+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@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+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@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
+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@
+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@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
+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@
+runstatedir = @runstatedir@
+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 = \
+ openvpnmsica.vcxproj \
+ openvpnmsica.vcxproj.filters \
+ openvpnmsica.props \
+ openvpnmsica-Debug.props \
+ openvpnmsica-Release.props
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/compat
+
+AM_CFLAGS = \
+ $(TAP_CFLAGS)
+
+@WIN32_TRUE@lib_LTLIBRARIES = libopenvpnmsica.la
+@WIN32_TRUE@libopenvpnmsica_la_CFLAGS = \
+@WIN32_TRUE@ -municode -D_UNICODE \
+@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \
+@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA \
+@WIN32_TRUE@ -Wl,--kill-at
+
+@WIN32_TRUE@libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshell32 -lshlwapi -lversion -no-undefined -avoid-version
+libopenvpnmsica_la_SOURCES = \
+ dllmain.c \
+ msiex.c msiex.h \
+ msica_arg.c msica_arg.h \
+ openvpnmsica.c openvpnmsica.h \
+ $(top_srcdir)/src/tapctl/basic.h \
+ $(top_srcdir)/src/tapctl/error.c $(top_srcdir)/src/tapctl/error.h \
+ $(top_srcdir)/src/tapctl/tap.c $(top_srcdir)/src/tapctl/tap.h \
+ openvpnmsica_resources.rc
+
+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/openvpnmsica/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/openvpnmsica/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-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libopenvpnmsica.la: $(libopenvpnmsica_la_OBJECTS) $(libopenvpnmsica_la_DEPENDENCIES) $(EXTRA_libopenvpnmsica_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libopenvpnmsica_la_LINK) $(am_libopenvpnmsica_la_rpath) $(libopenvpnmsica_la_OBJECTS) $(libopenvpnmsica_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-tap.Plo@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 $@ $<
+
+libopenvpnmsica_la-dllmain.lo: dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-dllmain.Tpo -c -o libopenvpnmsica_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-dllmain.Tpo $(DEPDIR)/libopenvpnmsica_la-dllmain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libopenvpnmsica_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+
+libopenvpnmsica_la-msiex.lo: msiex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-msiex.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-msiex.Tpo -c -o libopenvpnmsica_la-msiex.lo `test -f 'msiex.c' || echo '$(srcdir)/'`msiex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-msiex.Tpo $(DEPDIR)/libopenvpnmsica_la-msiex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msiex.c' object='libopenvpnmsica_la-msiex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-msiex.lo `test -f 'msiex.c' || echo '$(srcdir)/'`msiex.c
+
+libopenvpnmsica_la-msica_arg.lo: msica_arg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-msica_arg.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-msica_arg.Tpo -c -o libopenvpnmsica_la-msica_arg.lo `test -f 'msica_arg.c' || echo '$(srcdir)/'`msica_arg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-msica_arg.Tpo $(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msica_arg.c' object='libopenvpnmsica_la-msica_arg.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-msica_arg.lo `test -f 'msica_arg.c' || echo '$(srcdir)/'`msica_arg.c
+
+libopenvpnmsica_la-openvpnmsica.lo: openvpnmsica.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-openvpnmsica.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Tpo -c -o libopenvpnmsica_la-openvpnmsica.lo `test -f 'openvpnmsica.c' || echo '$(srcdir)/'`openvpnmsica.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Tpo $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openvpnmsica.c' object='libopenvpnmsica_la-openvpnmsica.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-openvpnmsica.lo `test -f 'openvpnmsica.c' || echo '$(srcdir)/'`openvpnmsica.c
+
+libopenvpnmsica_la-error.lo: $(top_srcdir)/src/tapctl/error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-error.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-error.Tpo -c -o libopenvpnmsica_la-error.lo `test -f '$(top_srcdir)/src/tapctl/error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-error.Tpo $(DEPDIR)/libopenvpnmsica_la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/tapctl/error.c' object='libopenvpnmsica_la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-error.lo `test -f '$(top_srcdir)/src/tapctl/error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/error.c
+
+libopenvpnmsica_la-tap.lo: $(top_srcdir)/src/tapctl/tap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-tap.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-tap.Tpo -c -o libopenvpnmsica_la-tap.lo `test -f '$(top_srcdir)/src/tapctl/tap.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/tap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-tap.Tpo $(DEPDIR)/libopenvpnmsica_la-tap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/tapctl/tap.c' object='libopenvpnmsica_la-tap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-tap.lo `test -f '$(top_srcdir)/src/tapctl/tap.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/tap.c
+
+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 $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; 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-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-error.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo
+ -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-libLTLIBRARIES
+
+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)/libopenvpnmsica_la-dllmain.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-error.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo
+ -rm -f ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo
+ -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-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am 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-libLTLIBRARIES
+
+.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/openvpnmsica/dllmain.c b/src/openvpnmsica/dllmain.c
new file mode 100644
index 0000000..7315543
--- /dev/null
+++ b/src/openvpnmsica/dllmain.c
@@ -0,0 +1,198 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 "openvpnmsica.h"
+#include "../tapctl/error.h"
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+#ifdef _MSC_VER
+#pragma comment(lib, "msi.lib")
+#endif
+#include <stdio.h>
+#include <tchar.h>
+
+
+DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES;
+
+
+/**
+ * DLL entry point
+ */
+BOOL WINAPI
+DllMain(
+ _In_ HINSTANCE hinstDLL,
+ _In_ DWORD dwReason,
+ _In_ LPVOID lpReserved)
+{
+ UNREFERENCED_PARAMETER(hinstDLL);
+ UNREFERENCED_PARAMETER(lpReserved);
+
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Allocate thread local storage index. */
+ openvpnmsica_thread_data_idx = TlsAlloc();
+ if (openvpnmsica_thread_data_idx == TLS_OUT_OF_INDEXES)
+ {
+ return FALSE;
+ }
+ /* Fall through. */
+
+ case DLL_THREAD_ATTACH:
+ {
+ /* Create thread local storage data. */
+ struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)calloc(1, sizeof(struct openvpnmsica_thread_data));
+ if (s == NULL)
+ {
+ return FALSE;
+ }
+
+ TlsSetValue(openvpnmsica_thread_data_idx, s);
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ if (openvpnmsica_thread_data_idx != TLS_OUT_OF_INDEXES)
+ {
+ /* Free thread local storage data and index. */
+ free(TlsGetValue(openvpnmsica_thread_data_idx));
+ TlsFree(openvpnmsica_thread_data_idx);
+ }
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Free thread local storage data. */
+ free(TlsGetValue(openvpnmsica_thread_data_idx));
+ break;
+ }
+
+ return TRUE;
+}
+
+
+bool
+dont_mute(unsigned int flags)
+{
+ UNREFERENCED_PARAMETER(flags);
+
+ return true;
+}
+
+
+void
+x_msg_va(const unsigned int flags, const char *format, va_list arglist)
+{
+ /* Secure last error before it is overridden. */
+ DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
+
+ struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)TlsGetValue(openvpnmsica_thread_data_idx);
+ if (s->hInstall == 0)
+ {
+ /* No MSI session, no fun. */
+ return;
+ }
+
+ /* Prepare the message record. The record will contain up to four fields. */
+ MSIHANDLE hRecordProg = MsiCreateRecord(4);
+
+ {
+ /* Field 2: The message string. */
+ char szBufStack[128];
+ int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist);
+ if (iResultLen < _countof(szBufStack))
+ {
+ /* Use from stack. */
+ MsiRecordSetStringA(hRecordProg, 2, szBufStack);
+ }
+ else
+ {
+ /* Allocate on heap and retry. */
+ char *szMessage = (char *)malloc(++iResultLen * sizeof(char));
+ if (szMessage != NULL)
+ {
+ vsnprintf(szMessage, iResultLen, format, arglist);
+ MsiRecordSetStringA(hRecordProg, 2, szMessage);
+ free(szMessage);
+ }
+ else
+ {
+ /* Use stack variant anyway, but make sure it's zero-terminated. */
+ szBufStack[_countof(szBufStack) - 1] = 0;
+ MsiRecordSetStringA(hRecordProg, 2, szBufStack);
+ }
+ }
+ }
+
+ if ((flags & M_ERRNO) == 0)
+ {
+ /* Field 1: MSI Error Code */
+ MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA);
+ }
+ else
+ {
+ /* Field 1: MSI Error Code */
+ MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO);
+
+ /* Field 3: The Windows error number. */
+ MsiRecordSetInteger(hRecordProg, 3, dwResult);
+
+ /* Field 4: The Windows error description. */
+ LPTSTR szErrMessage = NULL;
+ if (FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ dwResult,
+ 0,
+ (LPTSTR)&szErrMessage,
+ 0,
+ NULL) && szErrMessage)
+ {
+ /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
+ for (size_t i = 0, i_last = 0;; i++)
+ {
+ if (szErrMessage[i])
+ {
+ if (!_istspace(szErrMessage[i]))
+ {
+ i_last = i + 1;
+ }
+ }
+ else
+ {
+ szErrMessage[i_last] = 0;
+ break;
+ }
+ }
+ MsiRecordSetString(hRecordProg, 4, szErrMessage);
+ LocalFree(szErrMessage);
+ }
+ }
+
+ MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR, hRecordProg);
+ MsiCloseHandle(hRecordProg);
+}
diff --git a/src/openvpnmsica/msica_arg.c b/src/openvpnmsica/msica_arg.c
new file mode 100644
index 0000000..cde0577
--- /dev/null
+++ b/src/openvpnmsica/msica_arg.c
@@ -0,0 +1,139 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 "msica_arg.h"
+#include "../tapctl/error.h"
+#include "../tapctl/tap.h"
+
+#include <windows.h>
+#include <malloc.h>
+
+
+void
+msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq)
+{
+ seq->head = NULL;
+ seq->tail = NULL;
+}
+
+
+void
+msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq)
+{
+ while (seq->head)
+ {
+ struct msica_arg *p = seq->head;
+ seq->head = seq->head->next;
+ free(p);
+ }
+ seq->tail = NULL;
+}
+
+
+void
+msica_arg_seq_add_head(
+ _Inout_ struct msica_arg_seq *seq,
+ _In_z_ LPCTSTR argument)
+{
+ size_t argument_size = (_tcslen(argument) + 1) * sizeof(TCHAR);
+ struct msica_arg *p = malloc(sizeof(struct msica_arg) + argument_size);
+ if (p == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct msica_arg) + argument_size);
+ }
+ memcpy(p->val, argument, argument_size);
+ p->next = seq->head;
+ seq->head = p;
+ if (seq->tail == NULL)
+ {
+ seq->tail = p;
+ }
+}
+
+
+void
+msica_arg_seq_add_tail(
+ _Inout_ struct msica_arg_seq *seq,
+ _Inout_ LPCTSTR argument)
+{
+ size_t argument_size = (_tcslen(argument) + 1) * sizeof(TCHAR);
+ struct msica_arg *p = malloc(sizeof(struct msica_arg) + argument_size);
+ if (p == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct msica_arg) + argument_size);
+ }
+ memcpy(p->val, argument, argument_size);
+ p->next = NULL;
+ *(seq->tail ? &seq->tail->next : &seq->head) = p;
+ seq->tail = p;
+}
+
+
+LPTSTR
+msica_arg_seq_join(_In_ const struct msica_arg_seq *seq)
+{
+ /* Count required space. */
+ size_t size = 2 /*x + zero-terminator*/;
+ for (struct msica_arg *p = seq->head; p != NULL; p = p->next)
+ {
+ size += _tcslen(p->val) + 1 /*space delimiter|zero-terminator*/;
+ }
+ size *= sizeof(TCHAR);
+
+ /* Allocate. */
+ LPTSTR str = malloc(size);
+ if (str == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, size);
+ return NULL;
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4996) /* Using unsafe string functions: The space in s and termination of p->val has been implicitly verified at the beginning of this function. */
+#endif
+
+ /* Dummy argv[0] (i.e. executable name), for CommandLineToArgvW to work correctly when parsing this string. */
+ _tcscpy(str, TEXT("x"));
+
+ /* Join. */
+ LPTSTR s = str + 1 /*x*/;
+ for (struct msica_arg *p = seq->head; p != NULL; p = p->next)
+ {
+ /* Convert zero-terminator into space delimiter. */
+ s[0] = TEXT(' ');
+ s++;
+ /* Append argument. */
+ _tcscpy(s, p->val);
+ s += _tcslen(p->val);
+ }
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ return str;
+}
diff --git a/src/openvpnmsica/msica_arg.h b/src/openvpnmsica/msica_arg.h
new file mode 100644
index 0000000..4bf3c09
--- /dev/null
+++ b/src/openvpnmsica/msica_arg.h
@@ -0,0 +1,112 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 MSICA_ARG_H
+#define MSICA_ARG_H
+
+#include <windows.h>
+#include <tchar.h>
+#include "../tapctl/basic.h"
+
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4200) /* Using zero-sized arrays in struct/union. */
+#endif
+
+
+/**
+ * Argument list
+ */
+struct msica_arg
+{
+ struct msica_arg *next; /** Pointer to the next argument in the sequence */
+ TCHAR val[]; /** Zero terminated argument string */
+};
+
+
+/**
+ * Argument sequence
+ */
+struct msica_arg_seq
+{
+ struct msica_arg *head; /** Pointer to the first argument in the sequence */
+ struct msica_arg *tail; /** Pointer to the last argument in the sequence */
+};
+
+
+/**
+ * Initializes argument sequence
+ *
+ * @param seq Pointer to uninitialized argument sequence
+ */
+void
+msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq);
+
+
+/**
+ * Frees argument sequence
+ *
+ * @param seq Pointer to the argument sequence
+ */
+void
+msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq);
+
+
+/**
+ * Inserts argument to the beginning of the argument sequence
+ *
+ * @param seq Pointer to the argument sequence
+ *
+ * @param argument Zero-terminated argument string to insert.
+ */
+void
+msica_arg_seq_add_head(
+ _Inout_ struct msica_arg_seq *seq,
+ _In_z_ LPCTSTR argument);
+
+
+/**
+ * Appends argument to the end of the argument sequence
+ *
+ * @param seq Pointer to the argument sequence
+ *
+ * @param argument Zero-terminated argument string to append.
+ */
+void
+msica_arg_seq_add_tail(
+ _Inout_ struct msica_arg_seq *seq,
+ _Inout_ LPCTSTR argument);
+
+/**
+ * Join arguments of the argument sequence into a space delimited string
+ *
+ * @param seq Pointer to the argument sequence
+ *
+ * @return Joined argument string. Must be released with free() after use.
+ */
+LPTSTR
+msica_arg_seq_join(_In_ const struct msica_arg_seq *seq);
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif /* ifndef MSICA_ARG_H */
diff --git a/src/openvpnmsica/msiex.c b/src/openvpnmsica/msiex.c
new file mode 100644
index 0000000..54b2b97
--- /dev/null
+++ b/src/openvpnmsica/msiex.c
@@ -0,0 +1,265 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 "msiex.h"
+#include "../tapctl/error.h"
+
+#include <windows.h>
+#include <malloc.h>
+#include <memory.h>
+#include <msiquery.h>
+#ifdef _MSC_VER
+#pragma comment(lib, "msi.lib")
+#endif
+
+
+UINT
+msi_get_string(
+ _In_ MSIHANDLE hInstall,
+ _In_z_ LPCTSTR szName,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_get_record_string(
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_format_record(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ TCHAR szBufStack[128];
+ DWORD dwLength = _countof(szBufStack);
+ UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ /* Copy from stack. */
+ *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (*pszValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR));
+ return ERROR_SUCCESS;
+ }
+ else if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate on heap and retry. */
+ LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR));
+ if (szBufHeap == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR));
+ return ERROR_OUTOFMEMORY;
+ }
+
+ uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength);
+ if (uiResult == ERROR_SUCCESS)
+ {
+ *pszValue = szBufHeap;
+ }
+ else
+ {
+ free(szBufHeap);
+ }
+ return uiResult;
+ }
+ else
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
+ return uiResult;
+ }
+}
+
+
+UINT
+msi_format_field(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Read string to format. */
+ LPTSTR szValue = NULL;
+ UINT uiResult = msi_get_record_string(hRecord, iField, &szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ return uiResult;
+ }
+ if (szValue[0] == 0)
+ {
+ /* The string is empty. There's nothing left to do. */
+ *pszValue = szValue;
+ return ERROR_SUCCESS;
+ }
+
+ /* Create a temporary record. */
+ MSIHANDLE hRecordEx = MsiCreateRecord(1);
+ if (!hRecordEx)
+ {
+ uiResult = ERROR_INVALID_HANDLE;
+ msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
+ goto cleanup_szValue;
+ }
+
+ /* Populate the record with data. */
+ uiResult = MsiRecordSetString(hRecordEx, 0, szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
+ goto cleanup_hRecordEx;
+ }
+
+ /* Do the formatting. */
+ uiResult = msi_format_record(hInstall, hRecordEx, pszValue);
+
+cleanup_hRecordEx:
+ MsiCloseHandle(hRecordEx);
+cleanup_szValue:
+ free(szValue);
+ return uiResult;
+}
diff --git a/src/openvpnmsica/msiex.h b/src/openvpnmsica/msiex.h
new file mode 100644
index 0000000..cae4298
--- /dev/null
+++ b/src/openvpnmsica/msiex.h
@@ -0,0 +1,112 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 MSIHLP_H
+#define MSIHLP_H
+
+#include <windows.h>
+#include <msi.h>
+#include "../tapctl/basic.h"
+
+
+/**
+ * Gets MSI property value
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @param szName Property name
+ *
+ * @param pszValue Pointer to string to retrieve property value. The string must
+ * be released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+UINT
+msi_get_string(
+ _In_ MSIHANDLE hInstall,
+ _In_z_ LPCTSTR szName,
+ _Out_ LPTSTR *pszValue);
+
+
+/**
+ * Gets MSI record string value
+ *
+ * @param hRecord Handle to the record
+ *
+ * @param iField Field index
+ *
+ * @param pszValue Pointer to string to retrieve field value. The string must be
+ * released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+UINT
+msi_get_record_string(
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue);
+
+
+/**
+ * Formats MSI record
+ *
+ * @param hInstall Handle to the installation. This may be omitted, in which case only the
+ * record field parameters are processed and properties are not available
+ * for substitution.
+ *
+ * @param hRecord Handle to the record to format. The template string must be stored in
+ * record field 0 followed by referenced data parameters.
+ *
+ * @param pszValue Pointer to string to retrieve formatted value. The string must be
+ * released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+UINT
+msi_format_record(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _Out_ LPTSTR *pszValue);
+
+
+/**
+ * Formats MSI record field
+ *
+ * @param hInstall Handle to the installation. This may be omitted, in which case only the
+ * record field parameters are processed and properties are not available
+ * for substitution.
+ *
+ * @param hRecord Handle to the field record
+ *
+ * @param iField Field index
+ *
+ * @param pszValue Pointer to string to retrieve formatted value. The string must be
+ * released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+UINT
+msi_format_field(
+ _In_ MSIHANDLE hInstall,
+ _In_ MSIHANDLE hRecord,
+ _In_ unsigned int iField,
+ _Out_ LPTSTR *pszValue);
+
+#endif /* ifndef MSIHLP_H */
diff --git a/src/openvpnmsica/openvpnmsica-Debug.props b/src/openvpnmsica/openvpnmsica-Debug.props
new file mode 100644
index 0000000..43532cf
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica-Debug.props
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets">
+ <Import Project="openvpnmsica.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/openvpnmsica/openvpnmsica-Release.props b/src/openvpnmsica/openvpnmsica-Release.props
new file mode 100644
index 0000000..848fda8
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica-Release.props
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets">
+ <Import Project="openvpnmsica.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/openvpnmsica/openvpnmsica.c b/src/openvpnmsica/openvpnmsica.c
new file mode 100644
index 0000000..98111fb
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica.c
@@ -0,0 +1,1297 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 <winsock2.h> /* Must be included _before_ <windows.h> */
+
+#include "openvpnmsica.h"
+#include "msica_arg.h"
+#include "msiex.h"
+
+#include "../tapctl/basic.h"
+#include "../tapctl/error.h"
+#include "../tapctl/tap.h"
+
+#include <windows.h>
+#include <iphlpapi.h>
+#include <malloc.h>
+#include <memory.h>
+#include <msiquery.h>
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <tchar.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "advapi32.lib")
+#pragma comment(lib, "iphlpapi.lib")
+#pragma comment(lib, "shell32.lib")
+#pragma comment(lib, "shlwapi.lib")
+#pragma comment(lib, "version.lib")
+#endif
+
+
+/**
+ * Local constants
+ */
+
+#define MSICA_ADAPTER_TICK_SIZE (16*1024) /** Amount of tick space to reserve for one TAP/TUN adapter creation/deletition. */
+
+#define FILE_NEED_REBOOT L".ovpn_need_reboot"
+
+/**
+ * Joins an argument sequence and sets it to the MSI property.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @param szProperty MSI property name to set to the joined argument sequence.
+ *
+ * @param seq The argument sequence.
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ */
+static UINT
+setup_sequence(
+ _In_ MSIHANDLE hInstall,
+ _In_z_ LPCTSTR szProperty,
+ _In_ struct msica_arg_seq *seq)
+{
+ UINT uiResult;
+ LPTSTR szSequence = msica_arg_seq_join(seq);
+ uiResult = MsiSetProperty(hInstall, szProperty, szSequence);
+ free(szSequence);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szProperty);
+ return uiResult;
+ }
+ return ERROR_SUCCESS;
+}
+
+
+#ifdef _DEBUG
+
+/**
+ * Pops up a message box creating a time window to attach a debugger to the installer process in
+ * order to debug custom actions.
+ *
+ * @param szFunctionName Function name that triggered the pop-up. Displayed in message box's
+ * title.
+ */
+static void
+_debug_popup(_In_z_ LPCTSTR szFunctionName)
+{
+ TCHAR szTitle[0x100], szMessage[0x100+MAX_PATH], szProcessPath[MAX_PATH];
+
+ /* Compose pop-up title. The dialog title will contain function name to ease the process
+ * locating. Mind that Visual Studio displays window titles on the process list. */
+ _stprintf_s(szTitle, _countof(szTitle), TEXT("%s v%s"), szFunctionName, TEXT(PACKAGE_VERSION));
+
+ /* Get process name. */
+ GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath));
+ LPCTSTR szProcessName = _tcsrchr(szProcessPath, TEXT('\\'));
+ szProcessName = szProcessName ? szProcessName + 1 : szProcessPath;
+
+ /* Compose the pop-up message. */
+ _stprintf_s(
+ szMessage, _countof(szMessage),
+ TEXT("The %s process (PID: %u) has started to execute the %s custom action.\r\n")
+ TEXT("\r\n")
+ TEXT("If you would like to debug the custom action, attach a debugger to this process and set breakpoints before dismissing this dialog.\r\n")
+ TEXT("\r\n")
+ TEXT("If you are not debugging this custom action, you can safely ignore this message."),
+ szProcessName,
+ GetCurrentProcessId(),
+ szFunctionName);
+
+ MessageBox(NULL, szMessage, szTitle, MB_OK);
+}
+
+#define debug_popup(f) _debug_popup(f)
+#else /* ifdef _DEBUG */
+#define debug_popup(f)
+#endif /* ifdef _DEBUG */
+
+
+/**
+ * Detects if the OpenVPNService service is in use (running or paused) and sets
+ * OPENVPNSERVICE to the service process PID, or its path if it is set to
+ * auto-start, but not running.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+static UINT
+set_openvpnserv_state(_In_ MSIHANDLE hInstall)
+{
+ UINT uiResult;
+
+ /* Get Service Control Manager handle. */
+ SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
+ if (hSCManager == NULL)
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: OpenSCManager() failed", __FUNCTION__);
+ return uiResult;
+ }
+
+ /* Get OpenVPNService service handle. */
+ SC_HANDLE hService = OpenService(hSCManager, TEXT("OpenVPNService"), SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
+ if (hService == NULL)
+ {
+ uiResult = GetLastError();
+ if (uiResult == ERROR_SERVICE_DOES_NOT_EXIST)
+ {
+ /* This is not actually an error. */
+ goto cleanup_OpenSCManager;
+ }
+ msg(M_NONFATAL | M_ERRNO, "%s: OpenService(\"OpenVPNService\") failed", __FUNCTION__);
+ goto cleanup_OpenSCManager;
+ }
+
+ /* Query service status. */
+ SERVICE_STATUS_PROCESS ssp;
+ DWORD dwBufSize;
+ if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBufSize))
+ {
+ switch (ssp.dwCurrentState)
+ {
+ case SERVICE_START_PENDING:
+ case SERVICE_RUNNING:
+ case SERVICE_STOP_PENDING:
+ case SERVICE_PAUSE_PENDING:
+ case SERVICE_PAUSED:
+ case SERVICE_CONTINUE_PENDING:
+ {
+ /* Service is started (kind of). Set OPENVPNSERVICE property to service PID. */
+ TCHAR szPID[10 /*MAXDWORD in decimal*/ + 1 /*terminator*/];
+ _stprintf_s(
+ szPID, _countof(szPID),
+ TEXT("%u"),
+ ssp.dwProcessId);
+
+ uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), szPID);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
+ }
+
+ /* We know user is using the service. Skip auto-start setting check. */
+ goto cleanup_OpenService;
+ }
+ break;
+ }
+ }
+ else
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"OpenVPNService\") failed", __FUNCTION__);
+ }
+
+ /* Service is not started. Is it set to auto-start? */
+ /* MSDN describes the maximum buffer size for QueryServiceConfig() to be 8kB. */
+ /* This is small enough to fit on stack. */
+ BYTE _buffer_8k[8192];
+ LPQUERY_SERVICE_CONFIG pQsc = (LPQUERY_SERVICE_CONFIG)_buffer_8k;
+ dwBufSize = sizeof(_buffer_8k);
+ if (!QueryServiceConfig(hService, pQsc, dwBufSize, &dwBufSize))
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"QueryServiceConfig\") failed", __FUNCTION__);
+ goto cleanup_OpenService;
+ }
+
+ if (pQsc->dwStartType <= SERVICE_AUTO_START)
+ {
+ /* Service is set to auto-start. Set OPENVPNSERVICE property to its path. */
+ uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), pQsc->lpBinaryPathName);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
+ goto cleanup_OpenService;
+ }
+ }
+
+ uiResult = ERROR_SUCCESS;
+
+cleanup_OpenService:
+ CloseServiceHandle(hService);
+cleanup_OpenSCManager:
+ CloseServiceHandle(hSCManager);
+ return uiResult;
+}
+
+
+static void
+find_adapters(
+ _In_ MSIHANDLE hInstall,
+ _In_z_ LPCTSTR szzHardwareIDs,
+ _In_z_ LPCTSTR szAdaptersPropertyName,
+ _In_z_ LPCTSTR szActiveAdaptersPropertyName)
+{
+ UINT uiResult;
+
+ /* Get network adapters with given hardware ID. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ uiResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ return;
+ }
+ else if (pAdapterList == NULL)
+ {
+ /* No adapters - no fun. */
+ return;
+ }
+
+ /* Get IPv4/v6 info for all network adapters. Actually, we're interested in link status only: up/down? */
+ PIP_ADAPTER_ADDRESSES pAdapterAdresses = NULL;
+ ULONG ulAdapterAdressesSize = 16*1024;
+ for (size_t iteration = 0; iteration < 2; iteration++)
+ {
+ pAdapterAdresses = (PIP_ADAPTER_ADDRESSES)malloc(ulAdapterAdressesSize);
+ if (pAdapterAdresses == NULL)
+ {
+ msg(M_NONFATAL, "%s: malloc(%u) failed", __FUNCTION__, ulAdapterAdressesSize);
+ uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterList;
+ }
+
+ ULONG ulResult = GetAdaptersAddresses(
+ AF_UNSPEC,
+ GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_ALL_INTERFACES,
+ NULL,
+ pAdapterAdresses,
+ &ulAdapterAdressesSize);
+
+ if (ulResult == ERROR_SUCCESS)
+ {
+ break;
+ }
+
+ free(pAdapterAdresses);
+ if (ulResult != ERROR_BUFFER_OVERFLOW)
+ {
+ SetLastError(ulResult); /* MSDN does not mention GetAdaptersAddresses() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: GetAdaptersAddresses() failed", __FUNCTION__);
+ uiResult = ulResult; goto cleanup_pAdapterList;
+ }
+ }
+
+ /* Count adapters. */
+ size_t adapter_count = 0;
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ adapter_count++;
+ }
+
+ /* Prepare semicolon delimited list of TAP adapter ID(s) and active TAP adapter ID(s). */
+ LPTSTR
+ szAdapters = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
+ szAdaptersTail = szAdapters;
+ if (szAdapters == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
+ uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterAdresses;
+ }
+
+ LPTSTR
+ szAdaptersActive = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
+ szAdaptersActiveTail = szAdaptersActive;
+ if (szAdaptersActive == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
+ uiResult = ERROR_OUTOFMEMORY; goto cleanup_szAdapters;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ /* Convert adapter GUID to UTF-16 string. (LPOLESTR defaults to LPWSTR) */
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+
+ /* Append to the list of TAP adapter ID(s). */
+ if (szAdapters < szAdaptersTail)
+ {
+ *(szAdaptersTail++) = TEXT(';');
+ }
+ memcpy(szAdaptersTail, szAdapterId, 38 * sizeof(TCHAR));
+ szAdaptersTail += 38;
+
+ /* If this adapter is active (connected), add it to the list of active TAP adapter ID(s). */
+ for (PIP_ADAPTER_ADDRESSES p = pAdapterAdresses; p; p = p->Next)
+ {
+ OLECHAR szId[38 /*GUID*/ + 1 /*terminator*/];
+ GUID guid;
+ if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->AdapterName, -1, szId, _countof(szId)) > 0
+ && SUCCEEDED(IIDFromString(szId, &guid))
+ && memcmp(&guid, &pAdapter->guid, sizeof(GUID)) == 0)
+ {
+ if (p->OperStatus == IfOperStatusUp)
+ {
+ /* This TAP adapter is active (connected). */
+ if (szAdaptersActive < szAdaptersActiveTail)
+ {
+ *(szAdaptersActiveTail++) = TEXT(';');
+ }
+ memcpy(szAdaptersActiveTail, szAdapterId, 38 * sizeof(TCHAR));
+ szAdaptersActiveTail += 38;
+ }
+ break;
+ }
+ }
+ CoTaskMemFree(szAdapterId);
+ }
+ szAdaptersTail [0] = 0;
+ szAdaptersActiveTail[0] = 0;
+
+ /* Set Installer properties. */
+ uiResult = MsiSetProperty(hInstall, szAdaptersPropertyName, szAdapters);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szAdaptersPropertyName);
+ goto cleanup_szAdaptersActive;
+ }
+ uiResult = MsiSetProperty(hInstall, szActiveAdaptersPropertyName, szAdaptersActive);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szActiveAdaptersPropertyName);
+ goto cleanup_szAdaptersActive;
+ }
+
+cleanup_szAdaptersActive:
+ free(szAdaptersActive);
+cleanup_szAdapters:
+ free(szAdapters);
+cleanup_pAdapterAdresses:
+ free(pAdapterAdresses);
+cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+}
+
+
+UINT __stdcall
+FindSystemInfo(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+ OPENVPNMSICA_SAVE_MSI_SESSION(hInstall);
+
+ set_openvpnserv_state(hInstall);
+ find_adapters(
+ hInstall,
+ TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0"),
+ TEXT("TAPWINDOWS6ADAPTERS"),
+ TEXT("ACTIVETAPWINDOWS6ADAPTERS"));
+ find_adapters(
+ hInstall,
+ TEXT("Wintun") TEXT("\0"),
+ TEXT("WINTUNADAPTERS"),
+ TEXT("ACTIVEWINTUNADAPTERS"));
+
+ if (bIsCoInitialized)
+ {
+ CoUninitialize();
+ }
+ return ERROR_SUCCESS;
+}
+
+
+UINT __stdcall
+CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+ UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report errors, access properties, tables, etc.). */
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ /* Find OpenVPN GUI window. */
+ HWND hWnd = FindWindow(TEXT("OpenVPN-GUI"), NULL);
+ if (hWnd)
+ {
+ /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent OpenVPN GUI that do not run elevated. */
+ SendMessage(hWnd, WM_CLOSE, 0, 0);
+ Sleep(100);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+UINT __stdcall
+StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ UINT uiResult;
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+ OPENVPNMSICA_SAVE_MSI_SESSION(hInstall);
+
+ /* Create and populate a MSI record. */
+ MSIHANDLE hRecord = MsiCreateRecord(1);
+ if (!hRecord)
+ {
+ uiResult = ERROR_INVALID_HANDLE;
+ msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
+ goto cleanup_CoInitialize;
+ }
+ uiResult = MsiRecordSetString(hRecord, 0, TEXT("\"[#bin.openvpn_gui.exe]\""));
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
+ goto cleanup_MsiCreateRecord;
+ }
+
+ /* Format string. */
+ TCHAR szStackBuf[MAX_PATH];
+ DWORD dwPathSize = _countof(szStackBuf);
+ LPTSTR szPath = szStackBuf;
+ uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
+ if (uiResult == ERROR_MORE_DATA)
+ {
+ /* Allocate buffer on heap (+1 for terminator), and retry. */
+ szPath = (LPTSTR)malloc((++dwPathSize) * sizeof(TCHAR));
+ if (szPath == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwPathSize * sizeof(TCHAR));
+ uiResult = ERROR_OUTOFMEMORY; goto cleanup_MsiCreateRecord;
+ }
+
+ uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
+ }
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
+ goto cleanup_malloc_szPath;
+ }
+
+ /* Launch the OpenVPN GUI. */
+ SHELLEXECUTEINFO sei = {
+ .cbSize = sizeof(SHELLEXECUTEINFO),
+ .fMask = SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */
+ .lpFile = szPath,
+ .nShow = SW_SHOWNORMAL
+ };
+ if (!ShellExecuteEx(&sei))
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
+ goto cleanup_malloc_szPath;
+ }
+
+ uiResult = ERROR_SUCCESS;
+
+cleanup_malloc_szPath:
+ if (szPath != szStackBuf)
+ {
+ free(szPath);
+ }
+cleanup_MsiCreateRecord:
+ MsiCloseHandle(hRecord);
+cleanup_CoInitialize:
+ if (bIsCoInitialized)
+ {
+ CoUninitialize();
+ }
+ return uiResult;
+}
+
+
+/**
+ * Schedules adapter creation.
+ *
+ * When the rollback is enabled, the adapter deletition is scheduled on rollback.
+ *
+ * @param seq The argument sequence to pass to InstallTUNTAPAdapters custom action
+ *
+ * @param seqRollback The argument sequence to pass to InstallTUNTAPAdaptersRollback custom
+ * action. NULL when rollback is disabled.
+ *
+ * @param szDisplayName Adapter display name
+ *
+ * @param szHardwareId Adapter hardware ID
+ *
+ * @param iTicks Pointer to an integer that represents amount of work (on progress
+ * indicator) the InstallTUNTAPAdapters will take. This function increments it
+ * by MSICA_ADAPTER_TICK_SIZE for each adapter to create.
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ */
+static DWORD
+schedule_adapter_create(
+ _Inout_ struct msica_arg_seq *seq,
+ _Inout_opt_ struct msica_arg_seq *seqRollback,
+ _In_z_ LPCTSTR szDisplayName,
+ _In_z_ LPCTSTR szHardwareId,
+ _Inout_ int *iTicks)
+{
+ /* Get existing network adapters. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ return dwResult;
+ }
+
+ /* Does adapter exist? */
+ for (struct tap_adapter_node *pAdapterOther = pAdapterList;; pAdapterOther = pAdapterOther->pNext)
+ {
+ if (pAdapterOther == NULL)
+ {
+ /* No adapter with a same name found. */
+ TCHAR szArgument[10 /*create=""|deleteN=""*/ + MAX_PATH /*szDisplayName*/ + 1 /*|*/ + MAX_PATH /*szHardwareId*/ + 1 /*terminator*/];
+
+ /* InstallTUNTAPAdapters will create the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("create=\"%.*s|%.*s\""),
+ MAX_PATH, szDisplayName,
+ MAX_PATH, szHardwareId);
+ msica_arg_seq_add_tail(seq, szArgument);
+
+ if (seqRollback)
+ {
+ /* InstallTUNTAPAdaptersRollback will delete the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("deleteN=\"%.*s\""),
+ MAX_PATH, szDisplayName);
+ msica_arg_seq_add_head(seqRollback, szArgument);
+ }
+
+ *iTicks += MSICA_ADAPTER_TICK_SIZE;
+ break;
+ }
+ else if (_tcsicmp(szDisplayName, pAdapterOther->szName) == 0)
+ {
+ /* Adapter with a same name found. */
+ for (LPCTSTR hwid = pAdapterOther->szzHardwareIDs;; hwid += _tcslen(hwid) + 1)
+ {
+ if (hwid[0] == 0)
+ {
+ /* This adapter has a different hardware ID. */
+ msg(M_NONFATAL, "%s: Adapter with name \"%" PRIsLPTSTR "\" already exists", __FUNCTION__, pAdapterOther->szName);
+ dwResult = ERROR_ALREADY_EXISTS;
+ goto cleanup_pAdapterList;
+ }
+ else if (_tcsicmp(hwid, szHardwareId) == 0)
+ {
+ /* This is an adapter with the requested hardware ID. We already have what we want! */
+ break;
+ }
+ }
+ break; /* Adapter names are unique. There should be no other adapter with this name. */
+ }
+ }
+
+cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+ return dwResult;
+}
+
+
+/**
+ * Schedules adapter deletion.
+ *
+ * When the rollback is enabled, the adapter deletition is scheduled as: disable in
+ * UninstallTUNTAPAdapters, enable on rollback, delete on commit.
+ *
+ * When rollback is disabled, the adapter deletition is scheduled as delete in
+ * UninstallTUNTAPAdapters.
+ *
+ * @param seq The argument sequence to pass to UninstallTUNTAPAdapters custom action
+ *
+ * @param seqCommit The argument sequence to pass to UninstallTUNTAPAdaptersCommit custom
+ * action. NULL when rollback is disabled.
+ *
+ * @param seqRollback The argument sequence to pass to UninstallTUNTAPAdaptersRollback custom
+ * action. NULL when rollback is disabled.
+ *
+ * @param szDisplayName Adapter display name
+ *
+ * @param szzHardwareIDs String of strings with acceptable adapter hardware IDs
+ *
+ * @param iTicks Pointer to an integer that represents amount of work (on progress
+ * indicator) the UninstallTUNTAPAdapters will take. This function increments
+ * it by MSICA_ADAPTER_TICK_SIZE for each adapter to delete.
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ */
+static DWORD
+schedule_adapter_delete(
+ _Inout_ struct msica_arg_seq *seq,
+ _Inout_opt_ struct msica_arg_seq *seqCommit,
+ _Inout_opt_ struct msica_arg_seq *seqRollback,
+ _In_z_ LPCTSTR szDisplayName,
+ _In_z_ LPCTSTR szzHardwareIDs,
+ _Inout_ int *iTicks)
+{
+ /* Get adapters with given hardware ID. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ return dwResult;
+ }
+
+ /* Does adapter exist? */
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext)
+ {
+ if (_tcsicmp(szDisplayName, pAdapter->szName) == 0)
+ {
+ /* Adapter found. */
+ TCHAR szArgument[8 /*disable=|enable=|delete=*/ + 38 /*{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}*/ + 1 /*terminator*/];
+ if (seqCommit && seqRollback)
+ {
+ /* UninstallTUNTAPAdapters will disable the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("disable=") TEXT(PRIXGUID),
+ PRIGUID_PARAM(pAdapter->guid));
+ msica_arg_seq_add_tail(seq, szArgument);
+
+ /* UninstallTUNTAPAdaptersRollback will re-enable the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("enable=") TEXT(PRIXGUID),
+ PRIGUID_PARAM(pAdapter->guid));
+ msica_arg_seq_add_head(seqRollback, szArgument);
+
+ /* UninstallTUNTAPAdaptersCommit will delete the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("delete=") TEXT(PRIXGUID),
+ PRIGUID_PARAM(pAdapter->guid));
+ msica_arg_seq_add_tail(seqCommit, szArgument);
+ }
+ else
+ {
+ /* UninstallTUNTAPAdapters will delete the adapter. */
+ _stprintf_s(
+ szArgument, _countof(szArgument),
+ TEXT("delete=") TEXT(PRIXGUID),
+ PRIGUID_PARAM(pAdapter->guid));
+ msica_arg_seq_add_tail(seq, szArgument);
+ }
+
+ iTicks += MSICA_ADAPTER_TICK_SIZE;
+ break; /* Adapter names are unique. There should be no other adapter with this name. */
+ }
+ }
+
+ tap_free_adapter_list(pAdapterList);
+ return dwResult;
+}
+
+
+UINT __stdcall
+EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ UINT uiResult;
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+ OPENVPNMSICA_SAVE_MSI_SESSION(hInstall);
+
+ struct msica_arg_seq
+ seqInstall,
+ seqInstallCommit,
+ seqInstallRollback,
+ seqUninstall,
+ seqUninstallCommit,
+ seqUninstallRollback;
+ msica_arg_seq_init(&seqInstall);
+ msica_arg_seq_init(&seqInstallCommit);
+ msica_arg_seq_init(&seqInstallRollback);
+ msica_arg_seq_init(&seqUninstall);
+ msica_arg_seq_init(&seqUninstallCommit);
+ msica_arg_seq_init(&seqUninstallRollback);
+
+ /* Check rollback state. */
+ bool bRollbackEnabled = MsiEvaluateCondition(hInstall, TEXT("RollbackDisabled")) != MSICONDITION_TRUE;
+
+ /* Open MSI database. */
+ MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
+ if (hDatabase == 0)
+ {
+ msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__);
+ uiResult = ERROR_INVALID_HANDLE;
+ goto cleanup_exec_seq;
+ }
+
+ /* Check if TUNTAPAdapter table exists. If it doesn't exist, there's nothing to do. */
+ switch (MsiDatabaseIsTablePersistent(hDatabase, TEXT("TUNTAPAdapter")))
+ {
+ case MSICONDITION_FALSE:
+ case MSICONDITION_TRUE: break;
+
+ default:
+ uiResult = ERROR_SUCCESS;
+ goto cleanup_hDatabase;
+ }
+
+ /* Prepare a query to get a list/view of adapters. */
+ MSIHANDLE hViewST = 0;
+ LPCTSTR szQuery = TEXT("SELECT `Adapter`,`DisplayName`,`Condition`,`Component_`,`HardwareId` FROM `TUNTAPAdapter`");
+ uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
+ goto cleanup_hDatabase;
+ }
+
+ /* Execute query! */
+ uiResult = MsiViewExecute(hViewST, 0);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiViewExecute() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
+ goto cleanup_hViewST;
+ }
+
+ /* Create a record to report progress with. */
+ MSIHANDLE hRecordProg = MsiCreateRecord(2);
+ if (!hRecordProg)
+ {
+ uiResult = ERROR_INVALID_HANDLE;
+ msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
+ goto cleanup_hViewST_close;
+ }
+
+ for (;; )
+ {
+ /* Fetch one record from the view. */
+ MSIHANDLE hRecord = 0;
+ uiResult = MsiViewFetch(hViewST, &hRecord);
+ if (uiResult == ERROR_NO_MORE_ITEMS)
+ {
+ uiResult = ERROR_SUCCESS;
+ break;
+ }
+ else if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
+ goto cleanup_hRecordProg;
+ }
+
+ INSTALLSTATE iInstalled, iAction;
+ {
+ /* Read adapter component ID (`Component_` is field #4). */
+ LPTSTR szValue = NULL;
+ uiResult = msi_get_record_string(hRecord, 4, &szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_hRecord;
+ }
+
+ /* Get the component state. */
+ uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
+ free(szValue);
+ goto cleanup_hRecord;
+ }
+ free(szValue);
+ }
+
+ /* Get adapter display name (`DisplayName` is field #2). */
+ LPTSTR szDisplayName = NULL;
+ uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_hRecord;
+ }
+ /* `DisplayName` field type is [Filename](https://docs.microsoft.com/en-us/windows/win32/msi/filename), which is either "8.3|long name" or "8.3". */
+ LPTSTR szDisplayNameEx = _tcschr(szDisplayName, TEXT('|'));
+ szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName;
+
+ /* Get adapter hardware ID (`HardwareId` is field #5). */
+ TCHAR szzHardwareIDs[0x100] = { 0 };
+ {
+ LPTSTR szHwId = NULL;
+ uiResult = msi_get_record_string(hRecord, 5, &szHwId);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_szDisplayName;
+ }
+ memcpy_s(szzHardwareIDs, sizeof(szzHardwareIDs) - 2*sizeof(TCHAR) /*requires double zero termination*/, szHwId, _tcslen(szHwId)*sizeof(TCHAR));
+ free(szHwId);
+ }
+
+ if (iAction > INSTALLSTATE_BROKEN)
+ {
+ int iTicks = 0;
+
+ if (iAction >= INSTALLSTATE_LOCAL)
+ {
+ /* Read and evaluate adapter condition (`Condition` is field #3). */
+ LPTSTR szValue = NULL;
+ uiResult = msi_get_record_string(hRecord, 3, &szValue);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_szDisplayName;
+ }
+#ifdef __GNUC__
+/*
+ * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
+ * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+ switch (MsiEvaluateCondition(hInstall, szValue))
+ {
+ case MSICONDITION_FALSE:
+ free(szValue);
+ goto cleanup_szDisplayName;
+
+ case MSICONDITION_ERROR:
+ uiResult = ERROR_INVALID_FIELD;
+ msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
+ free(szValue);
+ goto cleanup_szDisplayName;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ free(szValue);
+
+ /* Component is or should be installed. Schedule adapter creation. */
+ if (schedule_adapter_create(
+ &seqInstall,
+ bRollbackEnabled ? &seqInstallRollback : NULL,
+ szDisplayNameEx,
+ szzHardwareIDs,
+ &iTicks) != ERROR_SUCCESS)
+ {
+ uiResult = ERROR_INSTALL_FAILED;
+ goto cleanup_szDisplayName;
+ }
+ }
+ else
+ {
+ /* Component is installed, but should be degraded to advertised/removed. Schedule adapter deletition.
+ *
+ * Note: On adapter removal (product is being uninstalled), we tolerate dwResult error.
+ * Better a partial uninstallation than no uninstallation at all.
+ */
+ schedule_adapter_delete(
+ &seqUninstall,
+ bRollbackEnabled ? &seqUninstallCommit : NULL,
+ bRollbackEnabled ? &seqUninstallRollback : NULL,
+ szDisplayNameEx,
+ szzHardwareIDs,
+ &iTicks);
+ }
+
+ /* Arrange the amount of tick space to add to the progress indicator.
+ * Do this within the loop to poll for user cancellation. */
+ MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
+ MsiRecordSetInteger(hRecordProg, 2, iTicks);
+ if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
+ {
+ uiResult = ERROR_INSTALL_USEREXIT;
+ goto cleanup_szDisplayName;
+ }
+ }
+
+cleanup_szDisplayName:
+ free(szDisplayName);
+cleanup_hRecord:
+ MsiCloseHandle(hRecord);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_hRecordProg;
+ }
+ }
+
+ /* save path to user's temp dir to be used later by deferred actions */
+ TCHAR tmpDir[MAX_PATH];
+ GetTempPath(MAX_PATH, tmpDir);
+
+ TCHAR str[MAX_PATH + 7];
+ _stprintf_s(str, _countof(str), TEXT("tmpdir=%") TEXT(PRIsLPTSTR), tmpDir);
+ msica_arg_seq_add_tail(&seqInstall, str);
+ msica_arg_seq_add_tail(&seqInstallCommit, str);
+ msica_arg_seq_add_tail(&seqInstallRollback, str);
+ msica_arg_seq_add_tail(&seqUninstall, str);
+ msica_arg_seq_add_tail(&seqUninstallCommit, str);
+ msica_arg_seq_add_tail(&seqUninstallRollback, str);
+
+ /* Store deferred custom action parameters. */
+ if ((uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdapters" ), &seqInstall )) != ERROR_SUCCESS
+ || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersCommit" ), &seqInstallCommit )) != ERROR_SUCCESS
+ || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersRollback" ), &seqInstallRollback )) != ERROR_SUCCESS
+ || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdapters" ), &seqUninstall )) != ERROR_SUCCESS
+ || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersCommit" ), &seqUninstallCommit )) != ERROR_SUCCESS
+ || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersRollback"), &seqUninstallRollback)) != ERROR_SUCCESS)
+ {
+ goto cleanup_hRecordProg;
+ }
+
+ uiResult = ERROR_SUCCESS;
+
+cleanup_hRecordProg:
+ MsiCloseHandle(hRecordProg);
+cleanup_hViewST_close:
+ MsiViewClose(hViewST);
+cleanup_hViewST:
+ MsiCloseHandle(hViewST);
+cleanup_hDatabase:
+ MsiCloseHandle(hDatabase);
+cleanup_exec_seq:
+ msica_arg_seq_free(&seqInstall);
+ msica_arg_seq_free(&seqInstallCommit);
+ msica_arg_seq_free(&seqInstallRollback);
+ msica_arg_seq_free(&seqUninstall);
+ msica_arg_seq_free(&seqUninstallCommit);
+ msica_arg_seq_free(&seqUninstallRollback);
+ if (bIsCoInitialized)
+ {
+ CoUninitialize();
+ }
+ return uiResult;
+}
+
+
+/**
+ * Parses string encoded GUID.
+ *
+ * @param szArg Zero terminated string where the GUID string starts
+ *
+ * @param guid Pointer to GUID that receives parsed value
+ *
+ * @return TRUE on success; FALSE otherwise
+ */
+static BOOL
+parse_guid(
+ _In_z_ LPCWSTR szArg,
+ _Out_ GUID *guid)
+{
+ if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11)
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * Create empty file in user's temp directory. The existence of this file
+ * is checked in the end of installation by ScheduleReboot immediate custom action
+ * which schedules reboot.
+ *
+ * @param szTmpDir path to user's temp dirctory
+ *
+ */
+static void
+CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
+{
+ TCHAR path[MAX_PATH];
+ swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT);
+
+ msg(M_WARN, "%s: Reboot required, create reboot indication file \"%" PRIsLPTSTR "\"", __FUNCTION__, path);
+
+ HANDLE file = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, path);
+ }
+ else
+ {
+ CloseHandle(file);
+ }
+}
+
+UINT __stdcall
+ProcessDeferredAction(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ UINT uiResult;
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+ WCHAR tmpDir[MAX_PATH] = {0};
+
+ OPENVPNMSICA_SAVE_MSI_SESSION(hInstall);
+
+ BOOL bIsCleanup = MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
+
+ /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only. */
+ LPWSTR szSequence = NULL;
+ uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence);
+ if (uiResult != ERROR_SUCCESS)
+ {
+ goto cleanup_CoInitialize;
+ }
+ int nArgs;
+ LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs);
+ if (szArg == NULL)
+ {
+ uiResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__, szSequence);
+ goto cleanup_szSequence;
+ }
+
+ /* Tell the installer to use explicit progress messages. */
+ MSIHANDLE hRecordProg = MsiCreateRecord(3);
+ MsiRecordSetInteger(hRecordProg, 1, 1);
+ MsiRecordSetInteger(hRecordProg, 2, 1);
+ MsiRecordSetInteger(hRecordProg, 3, 0);
+ MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
+
+ /* Prepare hRecordProg for progress messages. */
+ MsiRecordSetInteger(hRecordProg, 1, 2);
+ MsiRecordSetInteger(hRecordProg, 3, 0);
+
+ BOOL bRebootRequired = FALSE;
+
+ for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i)
+ {
+ DWORD dwResult = ERROR_SUCCESS;
+
+ if (wcsncmp(szArg[i], L"create=", 7) == 0)
+ {
+ /* Create an adapter with a given name and hardware ID. */
+ LPWSTR szName = szArg[i] + 7;
+ LPWSTR szHardwareId = wcschr(szName, L'|');
+ if (szHardwareId == NULL)
+ {
+ goto invalid_argument;
+ }
+ szHardwareId[0] = 0;
+ ++szHardwareId;
+
+ {
+ /* Report the name of the adapter to installer. */
+ MSIHANDLE hRecord = MsiCreateRecord(4);
+ MsiRecordSetString(hRecord, 1, TEXT("Creating adapter"));
+ MsiRecordSetString(hRecord, 2, szName);
+ MsiRecordSetString(hRecord, 3, szHardwareId);
+ int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
+ MsiCloseHandle(hRecord);
+ if (iResult == IDCANCEL)
+ {
+ uiResult = ERROR_INSTALL_USEREXIT;
+ goto cleanup;
+ }
+ }
+
+ GUID guidAdapter;
+ dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter);
+ if (dwResult == ERROR_SUCCESS)
+ {
+ /* Set adapter name. May fail on some machines, but that is not critical - use silent
+ flag to mute messagebox and print error only to log */
+ tap_set_adapter_name(&guidAdapter, szName, TRUE);
+ }
+ }
+ else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0)
+ {
+ /* Delete the adapter by name. */
+ LPCWSTR szName = szArg[i] + 8;
+
+ {
+ /* Report the name of the adapter to installer. */
+ MSIHANDLE hRecord = MsiCreateRecord(3);
+ MsiRecordSetString(hRecord, 1, TEXT("Deleting adapter"));
+ MsiRecordSetString(hRecord, 2, szName);
+ int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
+ MsiCloseHandle(hRecord);
+ if (iResult == IDCANCEL)
+ {
+ uiResult = ERROR_INSTALL_USEREXIT;
+ goto cleanup;
+ }
+ }
+
+ /* Get existing adapters. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult == ERROR_SUCCESS)
+ {
+ /* Does the adapter exist? */
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext)
+ {
+ if (_tcsicmp(szName, pAdapter->szName) == 0)
+ {
+ /* Adapter found. */
+ dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired);
+ break;
+ }
+ }
+
+ tap_free_adapter_list(pAdapterList);
+ }
+ }
+ else if (wcsncmp(szArg[i], L"delete=", 7) == 0)
+ {
+ /* Delete the adapter by GUID. */
+ GUID guid;
+ if (!parse_guid(szArg[i] + 7, &guid))
+ {
+ goto invalid_argument;
+ }
+ dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired);
+ }
+ else if (wcsncmp(szArg[i], L"enable=", 7) == 0)
+ {
+ /* Enable the adapter. */
+ GUID guid;
+ if (!parse_guid(szArg[i] + 7, &guid))
+ {
+ goto invalid_argument;
+ }
+ dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired);
+ }
+ else if (wcsncmp(szArg[i], L"disable=", 8) == 0)
+ {
+ /* Disable the adapter. */
+ GUID guid;
+ if (!parse_guid(szArg[i] + 8, &guid))
+ {
+ goto invalid_argument;
+ }
+ dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired);
+ }
+ else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0)
+ {
+ wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7);
+ }
+ else
+ {
+ goto invalid_argument;
+ }
+
+ if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */)
+ {
+ uiResult = ERROR_INSTALL_FAILURE;
+ goto cleanup;
+ }
+
+ /* Report progress and check for user cancellation. */
+ MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE);
+ if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
+ {
+ dwResult = ERROR_INSTALL_USEREXIT;
+ goto cleanup;
+ }
+
+ continue;
+
+invalid_argument:
+ msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]);
+ }
+
+cleanup:
+ if (bRebootRequired && wcslen(tmpDir) > 0)
+ {
+ CreateRebootFile(tmpDir);
+ }
+ MsiCloseHandle(hRecordProg);
+ LocalFree(szArg);
+cleanup_szSequence:
+ free(szSequence);
+cleanup_CoInitialize:
+ if (bIsCoInitialized)
+ {
+ CoUninitialize();
+ }
+ return uiResult;
+}
+
+UINT __stdcall
+CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+ debug_popup(TEXT(__FUNCTION__));
+
+ UINT ret = ERROR_SUCCESS;
+ BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+ OPENVPNMSICA_SAVE_MSI_SESSION(hInstall);
+
+ /* get user-specific temp path, to where we create reboot indication file */
+ TCHAR tempPath[MAX_PATH];
+ GetTempPath(MAX_PATH, tempPath);
+
+ /* check if reboot file exists */
+ TCHAR path[MAX_PATH];
+ _stprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT);
+ WIN32_FIND_DATA data = { 0 };
+ HANDLE searchHandle = FindFirstFile(path, &data);
+ if (searchHandle != INVALID_HANDLE_VALUE)
+ {
+ msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__);
+
+ FindClose(searchHandle);
+ DeleteFile(path);
+
+ MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
+ }
+
+ if (bIsCoInitialized)
+ {
+ CoUninitialize();
+ }
+ return ret;
+}
diff --git a/src/openvpnmsica/openvpnmsica.h b/src/openvpnmsica/openvpnmsica.h
new file mode 100644
index 0000000..bfc40ea
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica.h
@@ -0,0 +1,166 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 MSICA_H
+#define MSICA_H
+
+#include <windows.h>
+#include <msi.h>
+#include "../tapctl/basic.h"
+
+
+/*
+ * Error codes (next unused 2552L)
+ */
+#define ERROR_MSICA 2550L
+#define ERROR_MSICA_ERRNO 2551L
+
+
+/**
+ * Thread local storage data
+ */
+struct openvpnmsica_thread_data
+{
+ MSIHANDLE hInstall; /** Handle to the installation session. */
+};
+
+
+/**
+ * MSI session handle thread local storage index
+ */
+extern DWORD openvpnmsica_thread_data_idx;
+
+
+/**
+ * Set MSI session handle in thread local storage.
+ */
+#define OPENVPNMSICA_SAVE_MSI_SESSION(hInstall) \
+{ \
+ struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)TlsGetValue(openvpnmsica_thread_data_idx); \
+ s->hInstall = (hInstall); \
+}
+
+
+/*
+ * Exported DLL Functions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __GNUC__
+#define DLLEXP_DECL __declspec(dllexport)
+#else
+#define DLLEXP_DECL
+#define DLLEXP_EXPORT "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__
+#endif
+
+
+/**
+ * Determines Windows information:
+ *
+ * - Sets `OPENVPNSERVICE` MSI property to PID of OpenVPN Service if running, or its EXE path if
+ * configured for auto-start.
+ *
+ * - Finds existing TAP-Windows6 adapters and set TAPWINDOWS6ADAPTERS and
+ * ACTIVETAPWINDOWS6ADAPTERS properties with semicolon delimited list of all installed adapter
+ * GUIDs and active adapter GUIDs respectively.
+ *
+ * - Finds existing Wintun adapters and set WINTUNADAPTERS and ACTIVEWINTUNADAPTERS properties
+ * with semicolon delimited list of all installed adapter GUIDs and active adapter GUIDs
+ * respectively.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+FindSystemInfo(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Find OpenVPN GUI window and send it a WM_CLOSE message.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+CloseOpenVPNGUI(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Launches OpenVPN GUI. It's path is obtained by expanding the `[#bin.openvpn_gui.exe]`
+ * therefore, its Id field in File table must be "bin.openvpn_gui.exe".
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+StartOpenVPNGUI(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Evaluate the TUNTAPAdapter table of the MSI package database and prepare a list of TAP
+ * adapters to install/remove.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Perform scheduled deferred action.
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+ProcessDeferredAction(_In_ MSIHANDLE hInstall);
+
+
+/**
+ * Schedule reboot after installation if reboot
+ * indication file is found in user's temp directory
+ *
+ * @param hInstall Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+DLLEXP_DECL UINT __stdcall
+CheckAndScheduleReboot(_In_ MSIHANDLE hInstall);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef MSICA_H */
diff --git a/src/openvpnmsica/openvpnmsica.props b/src/openvpnmsica/openvpnmsica.props
new file mode 100644
index 0000000..074635d
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica.props
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets" />
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <TargetName>lib$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=_WIN32_WINNT_VISTA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/openvpnmsica/openvpnmsica.vcxproj b/src/openvpnmsica/openvpnmsica.vcxproj
new file mode 100644
index 0000000..c39b124
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica.vcxproj
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{D41AA9D6-B818-476E-992E-0E16EB86BEE2}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>openvpnmsica</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="openvpnmsica-Debug.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="openvpnmsica-Debug.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="openvpnmsica-Debug.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="openvpnmsica-Release.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="openvpnmsica-Release.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="openvpnmsica-Release.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <ItemGroup>
+ <ClCompile Include="..\tapctl\error.c" />
+ <ClCompile Include="..\tapctl\tap.c" />
+ <ClCompile Include="dllmain.c" />
+ <ClCompile Include="msiex.c" />
+ <ClCompile Include="msica_arg.c" />
+ <ClCompile Include="openvpnmsica.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\tapctl\basic.h" />
+ <ClInclude Include="..\tapctl\error.h" />
+ <ClInclude Include="..\tapctl\tap.h" />
+ <ClInclude Include="msiex.h" />
+ <ClInclude Include="msica_arg.h" />
+ <ClInclude Include="openvpnmsica.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="openvpnmsica_resources.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\build\msvc\msvc-generate\msvc-generate.vcxproj">
+ <Project>{8598c2c8-34c4-47a1-99b0-7c295a890615}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/src/openvpnmsica/openvpnmsica.vcxproj.filters b/src/openvpnmsica/openvpnmsica.vcxproj.filters
new file mode 100644
index 0000000..cb050f9
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica.vcxproj.filters
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tapctl\error.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="msiex.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="openvpnmsica.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="msica_arg.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tapctl\tap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="openvpnmsica.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="msiex.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="msica_arg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\tapctl\tap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\tapctl\error.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\tapctl\basic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="openvpnmsica_resources.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/openvpnmsica/openvpnmsica_resources.rc b/src/openvpnmsica/openvpnmsica_resources.rc
new file mode 100644
index 0000000..323f0e7
--- /dev/null
+++ b/src/openvpnmsica/openvpnmsica_resources.rc
@@ -0,0 +1,62 @@
+/*
+ * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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>
+#else
+#include <config-msvc-version.h>
+#endif
+#include <winresrc.h>
+
+#pragma code_page(65001) /* UTF8 */
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION OPENVPN_VERSION_RESOURCE
+ PRODUCTVERSION OPENVPN_VERSION_RESOURCE
+ FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PATCHED | VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "The OpenVPN Project"
+ VALUE "FileDescription", "Custom Action DLL to provide OpenVPN-specific support to MSI packages"
+ VALUE "FileVersion", PACKAGE_VERSION ".0"
+ VALUE "InternalName", "OpenVPN"
+ VALUE "LegalCopyright", "Copyright © The OpenVPN Project"
+ VALUE "OriginalFilename", "libopenvpnmsica.dll"
+ VALUE "ProductName", "OpenVPN"
+ VALUE "ProductVersion", PACKAGE_VERSION ".0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/openvpnserv/Makefile.am b/src/openvpnserv/Makefile.am
index bc65070..b067fb9 100644
--- a/src/openvpnserv/Makefile.am
+++ b/src/openvpnserv/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -36,4 +36,5 @@ openvpnserv_SOURCES = \
service.c service.h \
validate.c validate.h \
$(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \
- openvpnserv_resources.rc
+ openvpnserv_resources.rc \
+ $(top_srcdir)/src/openvpn/ring_buffer.h
diff --git a/src/openvpnserv/Makefile.in b/src/openvpnserv/Makefile.in
index 90a9abe..18eb55e 100644
--- a/src/openvpnserv/Makefile.in
+++ b/src/openvpnserv/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -226,7 +226,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -240,6 +241,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -267,7 +269,6 @@ 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@
@@ -318,6 +319,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -381,6 +384,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -417,7 +421,8 @@ openvpnserv_SOURCES = \
service.c service.h \
validate.c validate.h \
$(top_srcdir)/src/openvpn/block_dns.c $(top_srcdir)/src/openvpn/block_dns.h \
- openvpnserv_resources.rc
+ openvpnserv_resources.rc \
+ $(top_srcdir)/src/openvpn/ring_buffer.h
all: all-am
diff --git a/src/openvpnserv/automatic.c b/src/openvpnserv/automatic.c
index 5569ce9..3f2ca34 100644
--- a/src/openvpnserv/automatic.c
+++ b/src/openvpnserv/automatic.c
@@ -36,13 +36,9 @@
#include <stdio.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <process.h>
-/* bool definitions */
-#define bool int
-#define true 1
-#define false 0
-
static SERVICE_STATUS_HANDLE service;
static SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
@@ -115,41 +111,36 @@ close_if_open(HANDLE h)
static bool
match(const WIN32_FIND_DATA *find, LPCTSTR ext)
{
- int i;
-
if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
return false;
}
- if (!_tcslen(ext))
+ if (*ext == TEXT('\0'))
{
return true;
}
- i = _tcslen(find->cFileName) - _tcslen(ext) - 1;
- if (i < 1)
- {
- return false;
- }
+ /* find the pointer to that last '.' in filename and match ext against the rest */
- return find->cFileName[i] == '.' && !_tcsicmp(find->cFileName + i + 1, ext);
+ const TCHAR *p = _tcsrchr(find->cFileName, TEXT('.'));
+ return p && p != find->cFileName && _tcsicmp(p + 1, ext) == 0;
}
/*
* Modify the extension on a filename.
*/
static bool
-modext(LPTSTR dest, int size, LPCTSTR src, LPCTSTR newext)
+modext(LPTSTR dest, size_t size, LPCTSTR src, LPCTSTR newext)
{
- int i;
+ size_t i;
if (size > 0 && (_tcslen(src) + 1) <= size)
{
_tcscpy(dest, src);
dest [size - 1] = TEXT('\0');
i = _tcslen(dest);
- while (--i >= 0)
+ while (i-- > 0)
{
if (dest[i] == TEXT('\\'))
{
diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c
index eb718d4..f7b061c 100644
--- a/src/openvpnserv/common.c
+++ b/src/openvpnserv/common.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2011-2018 Heiko Hund <heiko.hund@sophos.com>
+ * Copyright (C) 2011-2021 Heiko Hund <heiko.hund@sophos.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
@@ -31,7 +31,7 @@ static wchar_t win_sys_path[MAX_PATH];
* These are necessary due to certain buggy implementations of (v)snprintf,
* that don't guarantee null termination for size > 0.
*/
-int
+BOOL
openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
{
int len = -1;
@@ -40,20 +40,36 @@ openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist)
len = _vsntprintf(str, size, format, arglist);
str[size - 1] = 0;
}
- return (len >= 0 && len < size);
+ return (len >= 0 && (size_t)len < size);
}
-int
+
+BOOL
openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...)
{
va_list arglist;
+ BOOL res = FALSE;
+ if (size > 0)
+ {
+ va_start(arglist, format);
+ res = openvpn_vsntprintf(str, size, format, arglist);
+ va_end(arglist);
+ }
+ return res;
+}
+
+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 = openvpn_vsntprintf(str, size, format, arglist);
+ len = vswprintf(str, size, format, arglist);
va_end(arglist);
+ str[size - 1] = L'\0';
}
- return len;
+ return (len >= 0 && len < size);
}
static DWORD
@@ -65,7 +81,7 @@ GetRegString(HKEY key, LPCTSTR value, LPTSTR data, DWORD size, LPCTSTR default_v
if (status == ERROR_FILE_NOT_FOUND && default_value)
{
size_t len = size/sizeof(data[0]);
- if (openvpn_sntprintf(data, len, default_value) > 0)
+ if (openvpn_sntprintf(data, len, default_value))
{
status = ERROR_SUCCESS;
}
@@ -212,12 +228,14 @@ out:
LPCTSTR
GetLastErrorText()
{
+ DWORD error;
static TCHAR buf[256];
DWORD len;
LPTSTR tmp = NULL;
+ error = GetLastError();
len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
- NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
+ NULL, error, LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL);
if (len == 0 || (long) _countof(buf) < (long) len + 14)
{
@@ -226,7 +244,7 @@ GetLastErrorText()
else
{
tmp[_tcslen(tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */
- openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, GetLastError());
+ openvpn_sntprintf(buf, _countof(buf), TEXT("%s (0x%x)"), tmp, error);
}
if (tmp)
diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c
index aecbd84..de36e85 100644
--- a/src/openvpnserv/interactive.c
+++ b/src/openvpnserv/interactive.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2012-2018 Heiko Hund <heiko.hund@sophos.com>
+ * Copyright (C) 2012-2021 Heiko Hund <heiko.hund@sophos.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
@@ -24,7 +24,6 @@
#include "service.h"
-#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <userenv.h>
@@ -44,13 +43,14 @@
#include "openvpn-msg.h"
#include "validate.h"
#include "block_dns.h"
+#include "ring_buffer.h"
#define IO_TIMEOUT 2000 /*ms*/
-#define ERROR_OPENVPN_STARTUP 0x20000000
-#define ERROR_STARTUP_DATA 0x20000001
-#define ERROR_MESSAGE_DATA 0x20000002
-#define ERROR_MESSAGE_TYPE 0x20000003
+#define ERROR_OPENVPN_STARTUP 0x20000000
+#define ERROR_STARTUP_DATA 0x20000001
+#define ERROR_MESSAGE_DATA 0x20000002
+#define ERROR_MESSAGE_TYPE 0x20000003
static SERVICE_STATUS_HANDLE service;
static SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_SHARE_PROCESS };
@@ -59,6 +59,7 @@ static settings_t settings;
static HANDLE rdns_semaphore = NULL;
#define RDNS_TIMEOUT 600 /* seconds to wait for the semaphore */
+#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
openvpn_service_t interactive_service = {
interactive,
@@ -90,6 +91,7 @@ typedef enum {
block_dns,
undo_dns4,
undo_dns6,
+ undo_domain,
_undo_type_max
} undo_type_t;
typedef list_item_t *undo_lists_t[_undo_type_max];
@@ -101,6 +103,14 @@ typedef struct {
int metric_v6;
} block_dns_data_t;
+typedef struct {
+ HANDLE send_ring_handle;
+ HANDLE receive_ring_handle;
+ HANDLE send_tail_moved;
+ HANDLE receive_tail_moved;
+ HANDLE device;
+} ring_buffer_handles_t;
+
static DWORD
AddListItem(list_item_t **pfirst, LPVOID data)
@@ -155,6 +165,26 @@ CloseHandleEx(LPHANDLE handle)
return INVALID_HANDLE_VALUE;
}
+static HANDLE
+OvpnUnmapViewOfFile(LPHANDLE handle)
+{
+ if (handle && *handle && *handle != INVALID_HANDLE_VALUE)
+ {
+ UnmapViewOfFile(*handle);
+ *handle = INVALID_HANDLE_VALUE;
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+static void
+CloseRingBufferHandles(ring_buffer_handles_t *ring_buffer_handles)
+{
+ CloseHandleEx(&ring_buffer_handles->device);
+ CloseHandleEx(&ring_buffer_handles->receive_tail_moved);
+ CloseHandleEx(&ring_buffer_handles->send_tail_moved);
+ OvpnUnmapViewOfFile(&ring_buffer_handles->send_ring_handle);
+ OvpnUnmapViewOfFile(&ring_buffer_handles->receive_ring_handle);
+}
static HANDLE
InitOverlapped(LPOVERLAPPED overlapped)
@@ -188,7 +218,7 @@ typedef enum {
static DWORD
AsyncPipeOp(async_op_t op, HANDLE pipe, LPVOID buffer, DWORD size, DWORD count, LPHANDLE events)
{
- int i;
+ DWORD i;
BOOL success;
HANDLE io_event;
DWORD res, bytes = 0;
@@ -277,10 +307,9 @@ ReturnProcessId(HANDLE pipe, DWORD pid, DWORD count, LPHANDLE events)
* Same format as error messages (3 line string) with error = 0 in
* 0x%08x format, PID on line 2 and a description "Process ID" on line 3
*/
- swprintf(buf, _countof(buf), L"0x%08x\n0x%08x\n%s", 0, pid, msg);
- buf[_countof(buf) - 1] = '\0';
+ openvpn_swprintf(buf, _countof(buf), L"0x%08x\n0x%08x\n%s", 0, pid, msg);
- WritePipeAsync(pipe, buf, wcslen(buf) * 2, count, events);
+ WritePipeAsync(pipe, buf, (DWORD)(wcslen(buf) * 2), count, events);
}
static VOID
@@ -308,7 +337,7 @@ ReturnError(HANDLE pipe, DWORD error, LPCWSTR func, DWORD count, LPHANDLE events
L"0x%1!08x!\n%2!s!\n%3!s!", 0, 0,
(LPWSTR) &result, 0, (va_list *) args);
- WritePipeAsync(pipe, result, wcslen(result) * 2, count, events);
+ WritePipeAsync(pipe, result, (DWORD)(wcslen(result) * 2), count, events);
#ifdef UNICODE
MsgToEventLog(MSG_FLAGS_ERROR, result);
#else
@@ -332,31 +361,6 @@ ReturnLastError(HANDLE pipe, LPCWSTR func)
ReturnError(pipe, GetLastError(), func, 1, &exit_event);
}
-
-static VOID
-ReturnOpenvpnOutput(HANDLE pipe, HANDLE ovpn_output, DWORD count, LPHANDLE events)
-{
- WCHAR *wide_output = NULL;
- CHAR output[512];
- DWORD size;
-
- ReadFile(ovpn_output, output, sizeof(output), &size, NULL);
- if (size == 0)
- {
- return;
- }
-
- wide_output = malloc((size) * sizeof(WCHAR));
- if (wide_output)
- {
- MultiByteToWideChar(CP_UTF8, 0, output, size, wide_output, size);
- wide_output[size - 1] = 0;
- }
-
- ReturnError(pipe, ERROR_OPENVPN_STARTUP, wide_output, count, events);
- free(wide_output);
-}
-
/*
* Validate options against a white list. Also check the config_file is
* inside the config_dir. The white list is defined in validate.c
@@ -381,10 +385,9 @@ ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *
if (!argv)
{
- swprintf(errmsg, capacity,
- L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
- GetLastError());
- errmsg[capacity-1] = L'\0';
+ openvpn_swprintf(errmsg, capacity,
+ L"Cannot validate options: CommandLineToArgvW failed with error = 0x%08x",
+ GetLastError());
goto out;
}
@@ -404,9 +407,8 @@ ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *
if (!CheckOption(workdir, 2, argv_tmp, &settings))
{
- swprintf(errmsg, capacity, msg1, argv[0], workdir,
- settings.ovpn_admin_group);
- errmsg[capacity-1] = L'\0';
+ openvpn_swprintf(errmsg, capacity, msg1, argv[0], workdir,
+ settings.ovpn_admin_group);
}
goto out;
}
@@ -422,15 +424,14 @@ ValidateOptions(HANDLE pipe, const WCHAR *workdir, const WCHAR *options, WCHAR *
{
if (wcscmp(L"--config", argv[i]) == 0 && argc-i > 1)
{
- swprintf(errmsg, capacity, msg1, argv[i+1], workdir,
- settings.ovpn_admin_group);
+ openvpn_swprintf(errmsg, capacity, msg1, argv[i+1], workdir,
+ settings.ovpn_admin_group);
}
else
{
- swprintf(errmsg, capacity, msg2, argv[i],
- settings.ovpn_admin_group);
+ openvpn_swprintf(errmsg, capacity, msg2, argv[i],
+ settings.ovpn_admin_group);
}
- errmsg[capacity-1] = L'\0';
goto out;
}
}
@@ -449,9 +450,9 @@ out:
static BOOL
GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
{
- size_t len;
+ size_t size, len;
WCHAR *data = NULL;
- DWORD size, bytes, read;
+ DWORD bytes, read;
bytes = PeekNamedPipeAsync(pipe, 1, &exit_event);
if (bytes == 0)
@@ -516,7 +517,7 @@ GetStartupData(HANDLE pipe, STARTUP_DATA *sud)
return TRUE;
err:
- sud->directory = NULL; /* caller must not free() */
+ sud->directory = NULL; /* caller must not free() */
free(data);
return FALSE;
}
@@ -564,6 +565,24 @@ InterfaceLuid(const char *iface_name, PNET_LUID luid)
return status;
}
+static DWORD
+ConvertInterfaceNameToIndex(const wchar_t *ifname, NET_IFINDEX *index)
+{
+ NET_LUID luid;
+ DWORD err;
+
+ err = ConvertInterfaceAliasToLuid(ifname, &luid);
+ if (err == ERROR_SUCCESS)
+ {
+ err = ConvertInterfaceLuidToIndex(&luid, index);
+ }
+ if (err != ERROR_SUCCESS)
+ {
+ MsgToEventLog(M_ERR, L"Failed to find interface index for <%s>", ifname);
+ }
+ return err;
+}
+
static BOOL
CmpAddress(LPVOID item, LPVOID address)
{
@@ -930,7 +949,7 @@ static DWORD WINAPI
RegisterDNS(LPVOID unused)
{
DWORD err;
- DWORD i;
+ size_t i;
DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
/* path of ipconfig command */
@@ -945,17 +964,15 @@ RegisterDNS(LPVOID unused)
{ ipcfg, L"ipconfig /flushdns", timeout },
{ ipcfg, L"ipconfig /registerdns", timeout },
};
- int ncmds = sizeof(cmds) / sizeof(cmds[0]);
HANDLE wait_handles[2] = {rdns_semaphore, exit_event};
- swprintf(ipcfg, _countof(ipcfg), L"%s\\%s", get_win_sys_path(), L"ipconfig.exe");
- ipcfg[_countof(ipcfg) - 1] = L'\0';
+ openvpn_swprintf(ipcfg, MAX_PATH, L"%s\\%s", get_win_sys_path(), L"ipconfig.exe");
if (WaitForMultipleObjects(2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
{
/* Semaphore locked */
- for (i = 0; i < ncmds; ++i)
+ for (i = 0; i < _countof(cmds); ++i)
{
ExecCommand(cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
}
@@ -1038,7 +1055,7 @@ netsh_dns_cmd(const wchar_t *action, const wchar_t *proto, const wchar_t *if_nam
const wchar_t *fmt = L"netsh interface %s %s dns \"%s\" %s";
/* max cmdline length in wchars -- include room for worst case and some */
- int ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1;
+ size_t ncmdline = wcslen(fmt) + wcslen(if_name) + wcslen(addr) + 32 + 1;
cmdline = malloc(ncmdline*sizeof(wchar_t));
if (!cmdline)
{
@@ -1059,6 +1076,53 @@ out:
return err;
}
+/**
+ * Run command: wmic nicconfig (InterfaceIndex=$if_index) call $action ($data)
+ * @param if_index "index of interface"
+ * @param action e.g., "SetDNSDomain"
+ * @param data data if required for action
+ * - a single word for SetDNSDomain, empty or NULL to delete
+ * - comma separated values for a list
+ */
+static DWORD
+wmic_nicconfig_cmd(const wchar_t *action, const NET_IFINDEX if_index,
+ const wchar_t *data)
+{
+ DWORD err = 0;
+ wchar_t argv0[MAX_PATH];
+ wchar_t *cmdline = NULL;
+ int timeout = 10000; /* in msec */
+
+ swprintf(argv0, _countof(argv0), L"%s\\%s", get_win_sys_path(), L"wbem\\wmic.exe");
+ argv0[_countof(argv0) - 1] = L'\0';
+
+ const wchar_t *fmt;
+ /* comma separated list must be enclosed in parenthesis */
+ if (data && wcschr(data, L','))
+ {
+ fmt = L"wmic nicconfig where (InterfaceIndex=%ld) call %s (%s)";
+ }
+ else
+ {
+ fmt = L"wmic nicconfig where (InterfaceIndex=%ld) call %s \"%s\"";
+ }
+
+ size_t ncmdline = wcslen(fmt) + 20 + wcslen(action) /* max 20 for ifindex */
+ + (data ? wcslen(data) + 1 : 1);
+ cmdline = malloc(ncmdline*sizeof(wchar_t));
+ if (!cmdline)
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ openvpn_sntprintf(cmdline, ncmdline, fmt, if_index, action,
+ data? data : L"");
+ err = ExecCommand(argv0, cmdline, timeout);
+
+ free(cmdline);
+ return err;
+}
+
/* Delete all IPv4 or IPv6 dns servers for an interface */
static DWORD
DeleteDNS(short family, wchar_t *if_name)
@@ -1081,6 +1145,54 @@ CmpWString(LPVOID item, LPVOID str)
return (wcscmp(item, str) == 0) ? TRUE : FALSE;
}
+/**
+ * Set interface specific DNS domain suffix
+ * @param if_name name of the the interface
+ * @param domain a single domain name
+ * @param lists pointer to the undo lists. If NULL
+ * undo lists are not altered.
+ * Will delete the currently set value if domain is empty.
+ */
+static DWORD
+SetDNSDomain(const wchar_t *if_name, const char *domain, undo_lists_t *lists)
+{
+ NET_IFINDEX if_index;
+
+ DWORD err = ConvertInterfaceNameToIndex(if_name, &if_index);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ wchar_t *wdomain = utf8to16(domain); /* utf8 to wide-char */
+ if (!wdomain)
+ {
+ return ERROR_OUTOFMEMORY;
+ }
+
+ /* free undo list if previously set */
+ if (lists)
+ {
+ free(RemoveListItem(&(*lists)[undo_domain], CmpWString, (void *)if_name));
+ }
+
+ err = wmic_nicconfig_cmd(L"SetDNSDomain", if_index, wdomain);
+
+ /* Add to undo list if domain is non-empty */
+ if (err == 0 && wdomain[0] && lists)
+ {
+ wchar_t *tmp_name = wcsdup(if_name);
+ if (!tmp_name || AddListItem(&(*lists)[undo_domain], tmp_name))
+ {
+ free(tmp_name);
+ err = ERROR_OUTOFMEMORY;
+ }
+ }
+
+ free(wdomain);
+ return err;
+}
+
static DWORD
HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
{
@@ -1100,6 +1212,13 @@ HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
return ERROR_MESSAGE_DATA;
}
+ /* use a non-const reference with limited scope to enforce null-termination of strings from client */
+ {
+ dns_cfg_message_t *msgptr = (dns_cfg_message_t *) msg;
+ msgptr->iface.name[_countof(msg->iface.name)-1] = '\0';
+ msgptr->domains[_countof(msg->domains)-1] = '\0';
+ }
+
wchar_t *wide_name = utf8to16(msg->iface.name); /* utf8 to wide-char */
if (!wide_name)
{
@@ -1119,9 +1238,14 @@ HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
free(RemoveListItem(&(*lists)[undo_type], CmpWString, wide_name));
}
- if (msg->header.type == msg_del_dns_cfg) /* job done */
+ if (msg->header.type == msg_del_dns_cfg)
{
- goto out;
+ if (msg->domains[0])
+ {
+ /* setting an empty domain removes any previous value */
+ err = SetDNSDomain(wide_name, "", lists);
+ }
+ goto out; /* job done */
}
for (int i = 0; i < addr_len; ++i)
@@ -1144,6 +1268,8 @@ HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
*/
}
+ err = 0;
+
if (msg->addr_len > 0)
{
wchar_t *tmp_name = wcsdup(wide_name);
@@ -1156,7 +1282,10 @@ HandleDNSConfigMessage(const dns_cfg_message_t *msg, undo_lists_t *lists)
}
}
- err = 0;
+ if (msg->domains[0])
+ {
+ err = SetDNSDomain(wide_name, msg->domains, lists);
+ }
out:
free(wide_name);
@@ -1202,8 +1331,118 @@ HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp)
return err;
}
+static DWORD
+OvpnDuplicateHandle(HANDLE ovpn_proc, HANDLE orig_handle, HANDLE* new_handle)
+{
+ DWORD err = ERROR_SUCCESS;
+
+ if (!DuplicateHandle(ovpn_proc, orig_handle, GetCurrentProcess(), new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ err = GetLastError();
+ MsgToEventLog(M_SYSERR, TEXT("Could not duplicate handle"));
+ return err;
+ }
+
+ return err;
+}
+
+static DWORD
+DuplicateAndMapRing(HANDLE ovpn_proc, HANDLE orig_handle, HANDLE *new_handle, struct tun_ring **ring)
+{
+ DWORD err = ERROR_SUCCESS;
+
+ err = OvpnDuplicateHandle(ovpn_proc, orig_handle, new_handle);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+ *ring = (struct tun_ring *)MapViewOfFile(*new_handle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(struct tun_ring));
+ if (*ring == NULL)
+ {
+ err = GetLastError();
+ MsgToEventLog(M_SYSERR, TEXT("Could not map shared memory"));
+ return err;
+ }
+
+ return err;
+}
+
+static DWORD
+HandleRegisterRingBuffers(const register_ring_buffers_message_t *rrb, HANDLE ovpn_proc,
+ ring_buffer_handles_t *ring_buffer_handles)
+{
+ DWORD err = 0;
+ struct tun_ring *send_ring;
+ struct tun_ring *receive_ring;
+
+ CloseRingBufferHandles(ring_buffer_handles);
+
+ err = OvpnDuplicateHandle(ovpn_proc, rrb->device, &ring_buffer_handles->device);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ err = DuplicateAndMapRing(ovpn_proc, rrb->send_ring_handle, &ring_buffer_handles->send_ring_handle, &send_ring);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ err = DuplicateAndMapRing(ovpn_proc, rrb->receive_ring_handle, &ring_buffer_handles->receive_ring_handle, &receive_ring);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ err = OvpnDuplicateHandle(ovpn_proc, rrb->send_tail_moved, &ring_buffer_handles->send_tail_moved);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ err = OvpnDuplicateHandle(ovpn_proc, rrb->receive_tail_moved, &ring_buffer_handles->receive_tail_moved);
+ if (err != ERROR_SUCCESS)
+ {
+ return err;
+ }
+
+ if (!register_ring_buffers(ring_buffer_handles->device, send_ring, receive_ring,
+ ring_buffer_handles->send_tail_moved, ring_buffer_handles->receive_tail_moved))
+ {
+ err = GetLastError();
+ MsgToEventLog(M_SYSERR, TEXT("Could not register ring buffers"));
+ }
+
+ return err;
+}
+
+static DWORD
+HandleMTUMessage(const set_mtu_message_t *mtu)
+{
+ DWORD err = 0;
+ MIB_IPINTERFACE_ROW ipiface;
+ InitializeIpInterfaceEntry(&ipiface);
+ ipiface.Family = mtu->family;
+ ipiface.InterfaceIndex = mtu->iface.index;
+ err = GetIpInterfaceEntry(&ipiface);
+ if (err != NO_ERROR)
+ {
+ return err;
+ }
+ if (mtu->family == AF_INET)
+ {
+ ipiface.SitePrefixLength = 0;
+ }
+ ipiface.NlMtu = mtu->mtu;
+
+ err = SetIpInterfaceEntry(&ipiface);
+ return err;
+}
+
static VOID
-HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
+HandleMessage(HANDLE pipe, HANDLE ovpn_proc, ring_buffer_handles_t *ring_buffer_handles,
+ DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
{
DWORD read;
union {
@@ -1214,6 +1453,8 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists
block_dns_message_t block_dns;
dns_cfg_message_t dns;
enable_dhcp_message_t dhcp;
+ register_ring_buffers_message_t rrb;
+ set_mtu_message_t mtu;
} msg;
ack_message_t ack = {
.header = {
@@ -1281,6 +1522,20 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists
}
break;
+ case msg_register_ring_buffers:
+ if (msg.header.size == sizeof(msg.rrb))
+ {
+ ack.error_number = HandleRegisterRingBuffers(&msg.rrb, ovpn_proc, ring_buffer_handles);
+ }
+ break;
+
+ case msg_set_mtu:
+ if (msg.header.size == sizeof(msg.mtu))
+ {
+ ack.error_number = HandleMTUMessage(&msg.mtu);
+ }
+ break;
+
default:
ack.error_number = ERROR_MESSAGE_TYPE;
MsgToEventLog(MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
@@ -1321,8 +1576,12 @@ Undo(undo_lists_t *lists)
DeleteDNS(AF_INET6, item->data);
break;
+ case undo_domain:
+ SetDNSDomain(item->data, "", NULL);
+ break;
+
case block_dns:
- interface_data = (block_dns_data_t*)(item->data);
+ interface_data = (block_dns_data_t *)(item->data);
delete_block_dns_filters(interface_data->engine);
if (interface_data->metric_v4 >= 0)
{
@@ -1364,6 +1623,7 @@ RunOpenvpn(LPVOID p)
WCHAR *cmdline = NULL;
size_t cmdline_size;
undo_lists_t undo_lists;
+ ring_buffer_handles_t ring_buffer_handles;
WCHAR errmsg[512] = L"";
SECURITY_ATTRIBUTES inheritable = {
@@ -1385,6 +1645,7 @@ RunOpenvpn(LPVOID p)
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&undo_lists, sizeof(undo_lists));
ZeroMemory(&proc_info, sizeof(proc_info));
+ ZeroMemory(&ring_buffer_handles, sizeof(ring_buffer_handles));
if (!GetStartupData(pipe, &sud))
{
@@ -1611,7 +1872,7 @@ RunOpenvpn(LPVOID p)
{
DWORD written;
WideCharToMultiByte(CP_UTF8, 0, sud.std_input, -1, input, input_size, NULL, NULL);
- WriteFile(stdin_write, input, strlen(input), &written, NULL);
+ WriteFile(stdin_write, input, (DWORD)strlen(input), &written, NULL);
free(input);
}
@@ -1623,7 +1884,7 @@ RunOpenvpn(LPVOID p)
break;
}
- HandleMessage(ovpn_pipe, bytes, 1, &exit_event, &undo_lists);
+ HandleMessage(ovpn_pipe, proc_info.hProcess, &ring_buffer_handles, bytes, 1, &exit_event, &undo_lists);
}
WaitForSingleObject(proc_info.hProcess, IO_TIMEOUT);
@@ -1635,9 +1896,8 @@ RunOpenvpn(LPVOID p)
else if (exit_code != 0)
{
WCHAR buf[256];
- swprintf(buf, _countof(buf),
- L"OpenVPN exited with error: exit code = %lu", exit_code);
- buf[_countof(buf) - 1] = L'\0';
+ openvpn_swprintf(buf, _countof(buf),
+ L"OpenVPN exited with error: exit code = %lu", exit_code);
ReturnError(pipe, ERROR_OPENVPN_STARTUP, buf, 1, &exit_event);
}
Undo(&undo_lists);
@@ -1651,6 +1911,7 @@ out:
free(cmdline);
DestroyEnvironmentBlock(user_env);
FreeStartupData(&sud);
+ CloseRingBufferHandles(&ring_buffer_handles);
CloseHandleEx(&proc_info.hProcess);
CloseHandleEx(&proc_info.hThread);
CloseHandleEx(&stdin_read);
diff --git a/src/openvpnserv/openvpnserv.vcxproj b/src/openvpnserv/openvpnserv.vcxproj
index c6760da..bcf9d25 100644
--- a/src/openvpnserv/openvpnserv.vcxproj
+++ b/src/openvpnserv/openvpnserv.vcxproj
@@ -1,103 +1,199 @@
<?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|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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>{9C91EE0B-817D-420A-A1E6-15A5A9D98BAD}</ProjectGuid>
<RootNamespace>openvpnserv</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>
- <CharacterSet>MultiByte</CharacterSet>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
- <PlatformToolset>v120</PlatformToolset>
+ <PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
- <CharacterSet>MultiByte</CharacterSet>
- <PlatformToolset>v120</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</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)'=='Release|ARM64'" 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>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" 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 Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
- <Optimization>Disabled</Optimization>
- <AdditionalIncludeDirectories>$(SOURCEBASE);%(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>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>legacy_stdio_definitions.lib;Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
- <ResourceCompile>
- <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
+ <ResourceCompile />
<Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>legacy_stdio_definitions.lib;Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
- <TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
- <Optimization>MaxSpeed</Optimization>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;$(CPPFLAGS);%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <PrecompiledHeader>
- </PrecompiledHeader>
- <WarningLevel>Level3</WarningLevel>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile />
+ <Link>
+ <AdditionalDependencies>legacy_stdio_definitions.lib;Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\openvpn;..\compat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
- <ResourceCompile>
- <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ResourceCompile>
+ <ResourceCompile />
<Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>legacy_stdio_definitions.lib;Userenv.lib;Iphlpapi.lib;ntdll.lib;Fwpuclnt.lib;Netapi32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
- <OptimizeReferences>true</OptimizeReferences>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
- <ClCompile Include="openvpnserv.c" />
+ <ClCompile Include="automatic.c" />
+ <ClCompile Include="common.c" />
+ <ClCompile Include="interactive.c" />
<ClCompile Include="service.c" />
+ <ClCompile Include="validate.c" />
+ <ClCompile Include="..\openvpn\block_dns.c" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\openvpn\ring_buffer.h" />
<ClInclude Include="service.h" />
+ <ClInclude Include="validate.h" />
+ <ClInclude Include="..\openvpn\block_dns.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="openvpnserv_resources.rc" />
@@ -111,4 +207,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/src/openvpnserv/openvpnserv.vcxproj.filters b/src/openvpnserv/openvpnserv.vcxproj.filters
index 0c89b4f..41ad3e8 100644
--- a/src/openvpnserv/openvpnserv.vcxproj.filters
+++ b/src/openvpnserv/openvpnserv.vcxproj.filters
@@ -15,10 +15,22 @@
</Filter>
</ItemGroup>
<ItemGroup>
- <ClCompile Include="openvpnserv.c">
+ <ClCompile Include="service.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="service.c">
+ <ClCompile Include="automatic.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="common.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="interactive.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="validate.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\openvpn\block_dns.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@@ -26,6 +38,15 @@
<ClInclude Include="service.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="validate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\openvpn\block_dns.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\openvpn\ring_buffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="openvpnserv_resources.rc">
diff --git a/src/openvpnserv/service.c b/src/openvpnserv/service.c
index 7157bea..8efe25f 100644
--- a/src/openvpnserv/service.c
+++ b/src/openvpnserv/service.c
@@ -270,8 +270,8 @@ _tmain(int argc, TCHAR *argv[])
else if (argc > i + 2 && _tcsicmp(TEXT("instance"), argv[i] + 1) == 0)
{
dispatchTable = _tcsicmp(TEXT("interactive"), argv[i + 1]) != 0 ?
- dispatchTable_automatic :
- dispatchTable_interactive;
+ dispatchTable_automatic :
+ dispatchTable_interactive;
service_instance = argv[i + 2];
i += 2;
diff --git a/src/openvpnserv/service.h b/src/openvpnserv/service.h
index 23b105f..500f390 100644
--- a/src/openvpnserv/service.h
+++ b/src/openvpnserv/service.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2013-2018 Heiko Hund <heiko.hund@sophos.com>
+ * Copyright (C) 2013-2021 Heiko Hund <heiko.hund@sophos.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
@@ -30,6 +30,7 @@
#include "config-msvc.h"
#endif
+#include <winsock2.h>
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
@@ -76,14 +77,18 @@ extern openvpn_service_t interactive_service;
extern LPCTSTR service_instance;
VOID WINAPI ServiceStartAutomaticOwn(DWORD argc, LPTSTR *argv);
+
VOID WINAPI ServiceStartAutomatic(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceStartInteractiveOwn(DWORD argc, LPTSTR *argv);
+
VOID WINAPI ServiceStartInteractive(DWORD argc, LPTSTR *argv);
-int openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist);
+BOOL openvpn_vsntprintf(LPTSTR str, size_t size, LPCTSTR format, va_list arglist);
+
+BOOL openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...);
-int openvpn_sntprintf(LPTSTR str, size_t size, LPCTSTR format, ...);
+BOOL openvpn_swprintf(wchar_t *const str, const size_t size, const wchar_t *const format, ...);
DWORD GetOpenvpnSettings(settings_t *s);
diff --git a/src/openvpnserv/validate.c b/src/openvpnserv/validate.c
index d35938c..93f92e3 100644
--- a/src/openvpnserv/validate.c
+++ b/src/openvpnserv/validate.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ * Copyright (C) 2016-2021 Selva Nair <selva.nair@gmail.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
@@ -51,6 +51,7 @@ static const WCHAR *white_list[] =
};
static BOOL IsUserInGroup(PSID sid, const PTOKEN_GROUPS groups, const WCHAR *group_name);
+
static PTOKEN_GROUPS GetTokenGroups(const HANDLE token);
/*
@@ -63,12 +64,14 @@ CheckConfigPath(const WCHAR *workdir, const WCHAR *fname, const settings_t *s)
WCHAR tmp[MAX_PATH];
const WCHAR *config_file = NULL;
const WCHAR *config_dir = NULL;
+#ifndef UNICODE
+ WCHAR widepath[MAX_PATH];
+#endif
/* convert fname to full path */
if (PathIsRelativeW(fname) )
{
- swprintf(tmp, _countof(tmp), L"%s\\%s", workdir, fname);
- tmp[_countof(tmp)-1] = L'\0';
+ openvpn_swprintf(tmp, _countof(tmp), L"%s\\%s", workdir, fname);
config_file = tmp;
}
else
@@ -300,12 +303,12 @@ IsUserInGroup(PSID sid, const PTOKEN_GROUPS token_groups, const WCHAR *group_nam
break;
}
/* If a match is already found, ret == TRUE and the loop is skipped */
- for (int i = 0; i < nread && !ret; ++i)
+ for (DWORD i = 0; i < nread && !ret; ++i)
{
ret = EqualSid(members[i].lgrmi0_sid, sid);
}
NetApiBufferFree(members);
- /* MSDN says the lookup should always iterate until err != ERROR_MORE_DATA */
+ /* MSDN says the lookup should always iterate until err != ERROR_MORE_DATA */
} while (err == ERROR_MORE_DATA && nloop++ < 100);
if (err != NERR_Success && err != NERR_GroupNotFound)
diff --git a/src/openvpnserv/validate.h b/src/openvpnserv/validate.h
index cc443e6..710e136 100644
--- a/src/openvpnserv/validate.h
+++ b/src/openvpnserv/validate.h
@@ -6,7 +6,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ * Copyright (C) 2016-2021 Selva Nair <selva.nair@gmail.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
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index f346178..9e98a67 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
diff --git a/src/plugins/Makefile.in b/src/plugins/Makefile.in
index 739191b..f31f296 100644
--- a/src/plugins/Makefile.in
+++ b/src/plugins/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
VPATH = @srcdir@
@@ -209,7 +209,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -223,6 +224,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -250,7 +252,6 @@ 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@
@@ -301,6 +302,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -364,6 +367,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/src/plugins/auth-pam/Makefile.in b/src/plugins/auth-pam/Makefile.in
index 6f2bbdb..52afb94 100644
--- a/src/plugins/auth-pam/Makefile.in
+++ b/src/plugins/auth-pam/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -235,7 +235,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -249,6 +250,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -276,7 +278,6 @@ 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@
@@ -327,6 +328,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -390,6 +393,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/src/plugins/auth-pam/README.auth-pam b/src/plugins/auth-pam/README.auth-pam
index e123690..e3ca027 100644
--- a/src/plugins/auth-pam/README.auth-pam
+++ b/src/plugins/auth-pam/README.auth-pam
@@ -7,7 +7,7 @@ authentication via PAM, and essentially allows any authentication
method supported by PAM (such as LDAP, RADIUS, or Linux Shadow
passwords) to be used with OpenVPN. While PAM supports
username/password authentication, this can be combined with X509
-certificates to provide two indepedent levels of authentication.
+certificates to provide two independent levels of authentication.
This module uses a split privilege execution model which will
function even if you drop openvpn daemon privileges using the user,
@@ -36,19 +36,20 @@ pairs to answer PAM module queries.
For example:
- plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD"
+ plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD pin OTP"
tells auth-pam to (a) use the "login" PAM module, (b) answer a
-"login" query with the username given by the OpenVPN client, and
-(c) answer a "password" query with the password given by the
-OpenVPN client. This provides flexibility in dealing with the different
+"login" query with the username given by the OpenVPN client,
+(c) answer a "password" query with the password, and (d) answer a
+"pin" query with the OTP given by the OpenVPN client.
+This provides flexibility in dealing with different
types of query strings which different PAM modules might generate.
For example, suppose you were using a PAM module called
"test" which queried for "name" rather than "login":
plugin openvpn-auth-pam.so "test name USERNAME password PASSWORD"
-While "USERNAME" "COMMONNAME" and "PASSWORD" are special strings which substitute
+While "USERNAME" "COMMONNAME" "PASSWORD" and "OTP" are special strings which substitute
to client-supplied values, it is also possible to name literal values
to use as PAM module query responses. For example, suppose that the
login module queried for a third parameter, "domain" which
@@ -59,8 +60,12 @@ is to be answered with the constant value "mydomain.com":
The following OpenVPN directives can also influence
the operation of this plugin:
- client-cert-not-required
+ verify-client-cert none
username-as-common-name
+ static-challenge
+
+Use of --static challenege is required to pass a pin (represented by "OTP" in
+parameter substitution) or a second password.
Run OpenVPN with --verb 7 or higher to get debugging output from
this plugin, including the list of queries presented by the
@@ -68,6 +73,41 @@ underlying PAM module. This is a useful debugging tool to figure
out which queries a given PAM module is making, so that you can
craft the appropriate plugin directive to answer it.
+Since running OpenVPN with verb 7 is quite verbose, alternatively
+you can put
+
+ verb 3
+ setenv verb 9
+
+in the openvpn config which will only increase logging for this plugin.
+
+
+ASYNCHRONOUS OPERATION
+
+Sometimes PAM modules take very long to complete (for example, a LDAP
+or Radius query might timeout trying to connect an unreachable external
+server). Normal plugin auth operation will block the whole OpenVPN
+process in this time, that is, all forwarding for all other clients stops.
+
+The auth-pam plugin can operate asynchronously ("deferred authentication")
+to remedy this situation. To enable this, put
+
+ setenv deferred_auth_pam 1
+
+in your openvpn server config. If set, this will make the "PAM background
+process" fork() and do its job detached from OpenVPN. When finished, a
+status file is written, which OpenVPN will then pick up and read the
+success/failure result from it.
+
+While the plugin is working in the background, OpenVPN will continue to
+service other clients normally.
+
+Asynchronous operation is recommended for all PAM queries that could
+"take time" (LDAP, Radius, NIS, ...). If only local files are queried
+(passwd, pam_userdb, ...), synchronous operation has slightly lower
+overhead, so this is still the default mode of operation.
+
+
CAVEATS
This module will only work on *nix systems which support PAM,
diff --git a/src/plugins/auth-pam/auth-pam.c b/src/plugins/auth-pam/auth-pam.c
index 5ba4dc4..235610a 100644
--- a/src/plugins/auth-pam/auth-pam.c
+++ b/src/plugins/auth-pam/auth-pam.c
@@ -5,7 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2016-2021 Selva Nair <selva.nair@gmail.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
@@ -61,9 +62,15 @@
#define RESPONSE_INIT_FAILED 11
#define RESPONSE_VERIFY_SUCCEEDED 12
#define RESPONSE_VERIFY_FAILED 13
+#define RESPONSE_DEFER 14
/* Pointers to functions exported from openvpn */
+static plugin_log_t plugin_log = NULL;
static plugin_secure_memzero_t plugin_secure_memzero = NULL;
+static plugin_base64_decode_t plugin_base64_decode = NULL;
+
+/* module name for plugin_log() */
+static char *MODULE = "AUTH-PAM";
/*
* Plugin state, used by foreground
@@ -87,6 +94,7 @@ struct auth_pam_context
* "USERNAME" -- substitute client-supplied username
* "PASSWORD" -- substitute client-specified password
* "COMMONNAME" -- substitute client certificate common name
+ * "OTP" -- substitute static challenge response if available
*/
#define N_NAME_VALUE 16
@@ -111,6 +119,7 @@ struct user_pass {
char username[128];
char password[128];
char common_name[128];
+ char response[128];
const struct name_value_list *name_value_list;
};
@@ -207,7 +216,7 @@ daemonize(const char *envp[])
}
if (daemon(0, 0) < 0)
{
- fprintf(stderr, "AUTH-PAM: daemonization failed\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "daemonization failed");
}
else if (fd >= 3)
{
@@ -276,6 +285,66 @@ name_value_match(const char *query, const char *match)
return strncasecmp(match, query, strlen(match)) == 0;
}
+/*
+ * Split and decode up->password in the form SCRV1:base64_pass:base64_response
+ * into pass and response and save in up->password and up->response.
+ * If the password is not in the expected format, input is not changed.
+ */
+static void
+split_scrv1_password(struct user_pass *up)
+{
+ const int skip = strlen("SCRV1:");
+ if (strncmp(up->password, "SCRV1:", skip) != 0)
+ {
+ return;
+ }
+
+ char *tmp = strdup(up->password);
+ if (!tmp)
+ {
+ plugin_log(PLOG_ERR, MODULE, "out of memory parsing static challenge password");
+ goto out;
+ }
+
+ char *pass = tmp + skip;
+ char *resp = strchr(pass, ':');
+ if (!resp) /* string not in SCRV1:xx:yy format */
+ {
+ goto out;
+ }
+ *resp++ = '\0';
+
+ int n = plugin_base64_decode(pass, up->password, sizeof(up->password)-1);
+ if (n >= 0)
+ {
+ up->password[n] = '\0';
+ n = plugin_base64_decode(resp, up->response, sizeof(up->response)-1);
+ if (n >= 0)
+ {
+ up->response[n] = '\0';
+ if (DEBUG(up->verb))
+ {
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: parsed static challenge password");
+ }
+ goto out;
+ }
+ }
+
+ /* decode error: reinstate original value of up->password and return */
+ plugin_secure_memzero(up->password, sizeof(up->password));
+ plugin_secure_memzero(up->response, sizeof(up->response));
+ strcpy(up->password, tmp); /* tmp is guaranteed to fit in up->password */
+
+ plugin_log(PLOG_ERR, MODULE, "base64 decode error while parsing static challenge password");
+
+out:
+ if (tmp)
+ {
+ plugin_secure_memzero(tmp, strlen(tmp));
+ free(tmp);
+ }
+}
+
OPENVPN_EXPORT int
openvpn_plugin_open_v3(const int v3structver,
struct openvpn_plugin_args_open_in const *args,
@@ -315,7 +384,9 @@ openvpn_plugin_open_v3(const int v3structver,
ret->type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY);
/* Save global pointers to functions exported from openvpn */
+ plugin_log = args->callbacks->plugin_log;
plugin_secure_memzero = args->callbacks->plugin_secure_memzero;
+ plugin_base64_decode = args->callbacks->plugin_base64_decode;
/*
* Make sure we have two string arguments: the first is the .so name,
@@ -323,7 +394,7 @@ openvpn_plugin_open_v3(const int v3structver,
*/
if (string_array_len(argv) < base_parms)
{
- fprintf(stderr, "AUTH-PAM: need PAM service parameter\n");
+ plugin_log(PLOG_ERR, MODULE, "need PAM service parameter");
goto error;
}
@@ -339,7 +410,7 @@ openvpn_plugin_open_v3(const int v3structver,
if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE)
{
- fprintf(stderr, "AUTH-PAM: bad name/value list length\n");
+ plugin_log(PLOG_ERR, MODULE, "bad name/value list length");
goto error;
}
@@ -369,7 +440,7 @@ openvpn_plugin_open_v3(const int v3structver,
*/
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
{
- fprintf(stderr, "AUTH-PAM: socketpair call failed\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "socketpair call failed");
goto error;
}
@@ -395,7 +466,7 @@ openvpn_plugin_open_v3(const int v3structver,
/* don't let future subprocesses inherit child socket */
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0)
{
- fprintf(stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "Set FD_CLOEXEC flag on socket file descriptor failed");
}
/* wait for background child process to initialize */
@@ -404,6 +475,7 @@ openvpn_plugin_open_v3(const int v3structver,
{
context->foreground_fd = fd[0];
ret->handle = (openvpn_plugin_handle_t *) context;
+ plugin_log( PLOG_NOTE, MODULE, "initialization succeeded (fg)" );
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
}
@@ -453,14 +525,33 @@ openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const cha
const char *password = get_env("password", envp);
const char *common_name = get_env("common_name", envp) ? get_env("common_name", envp) : "";
+ /* should we do deferred auth?
+ * yes, if there is "auth_control_file" and "deferred_auth_pam" env
+ */
+ const char *auth_control_file = get_env("auth_control_file", envp);
+ const char *deferred_auth_pam = get_env("deferred_auth_pam", envp);
+ if (auth_control_file != NULL && deferred_auth_pam != NULL)
+ {
+ if (DEBUG(context->verb))
+ {
+ plugin_log(PLOG_NOTE, MODULE, "do deferred auth '%s'",
+ auth_control_file);
+ }
+ }
+ else
+ {
+ auth_control_file = "";
+ }
+
if (username && strlen(username) > 0 && password)
{
if (send_control(context->foreground_fd, COMMAND_VERIFY) == -1
|| send_string(context->foreground_fd, username) == -1
|| send_string(context->foreground_fd, password) == -1
- || send_string(context->foreground_fd, common_name) == -1)
+ || send_string(context->foreground_fd, common_name) == -1
+ || send_string(context->foreground_fd, auth_control_file) == -1)
{
- fprintf(stderr, "AUTH-PAM: Error sending auth info to background process\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "Error sending auth info to background process");
}
else
{
@@ -469,9 +560,17 @@ openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const cha
{
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
+ if (status == RESPONSE_DEFER)
+ {
+ if (DEBUG(context->verb))
+ {
+ plugin_log(PLOG_NOTE, MODULE, "deferred authentication");
+ }
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
if (status == -1)
{
- fprintf(stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "Error receiving auth confirmation from background process");
}
}
}
@@ -486,7 +585,7 @@ openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
if (DEBUG(context->verb))
{
- fprintf(stderr, "AUTH-PAM: close\n");
+ plugin_log(PLOG_NOTE, MODULE, "close");
}
if (context->foreground_fd >= 0)
@@ -494,7 +593,7 @@ openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
/* tell background process to exit */
if (send_control(context->foreground_fd, COMMAND_EXIT) == -1)
{
- fprintf(stderr, "AUTH-PAM: Error signaling background process to exit\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "Error signaling background process to exit");
}
/* wait for background process to exit */
@@ -556,7 +655,7 @@ my_conv(int n, const struct pam_message **msg_array,
if (DEBUG(up->verb))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n",
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: my_conv[%d] query='%s' style=%d",
i,
msg->msg ? msg->msg : "NULL",
msg->msg_style);
@@ -581,7 +680,7 @@ my_conv(int n, const struct pam_message **msg_array,
if (DEBUG(up->verb))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n",
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'",
msg->msg,
match_name,
match_value);
@@ -599,6 +698,10 @@ my_conv(int n, const struct pam_message **msg_array,
{
aresp[i].resp = searchandreplace(match_value, "COMMONNAME", up->common_name);
}
+ else if (strstr(match_value, "OTP"))
+ {
+ aresp[i].resp = searchandreplace(match_value, "OTP", up->response);
+ }
else
{
aresp[i].resp = strdup(match_value);
@@ -695,7 +798,7 @@ pam_auth(const char *service, const struct user_pass *up)
/* Output error message if failed */
if (!ret)
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n",
+ plugin_log(PLOG_ERR, MODULE, "BACKGROUND: user '%s' failed to authenticate: %s",
up->username,
pam_strerror(pamh, status));
}
@@ -708,12 +811,87 @@ pam_auth(const char *service, const struct user_pass *up)
}
/*
+ * deferred auth handler
+ * - fork() (twice, to avoid the need for async wait / SIGCHLD handling)
+ * - query PAM stack via pam_auth()
+ * - send response back to OpenVPN via "ac_file_name"
+ *
+ * parent process returns "0" for "fork() and wait() succeeded",
+ * "-1" for "something went wrong, abort program"
+ */
+
+static void
+do_deferred_pam_auth(int fd, const char *ac_file_name,
+ const char *service, const struct user_pass *up)
+{
+ if (send_control(fd, RESPONSE_DEFER) == -1)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [4]");
+ return;
+ }
+
+ /* double forking so we do not need to wait() for async auth kids */
+ pid_t p1 = fork();
+
+ if (p1 < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(1) failed");
+ return;
+ }
+ if (p1 != 0) /* parent */
+ {
+ waitpid(p1, NULL, 0);
+ return; /* parent's job succeeded */
+ }
+
+ /* child */
+ close(fd); /* socketpair no longer needed */
+
+ pid_t p2 = fork();
+ if (p2 < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
+ exit(1);
+ }
+
+ if (p2 != 0) /* new parent: exit right away */
+ {
+ exit(0);
+ }
+
+ /* grandchild */
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: deferred auth for '%s', pid=%d",
+ up->username, (int) getpid() );
+
+ /* the rest is very simple: do PAM, write status byte to file, done */
+ int ac_fd = open( ac_file_name, O_WRONLY );
+ if (ac_fd < 0)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "cannot open '%s' for writing",
+ ac_file_name );
+ exit(1);
+ }
+ int pam_success = pam_auth(service, up);
+
+ if (write( ac_fd, pam_success ? "1" : "0", 1 ) != 1)
+ {
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "cannot write to '%s'",
+ ac_file_name );
+ }
+ close(ac_fd);
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: %s: deferred auth: PAM %s",
+ up->username, pam_success ? "succeeded" : "rejected" );
+ exit(0);
+}
+
+/*
* Background process -- runs with privilege.
*/
static void
pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list)
{
struct user_pass up;
+ char ac_file_name[PATH_MAX];
int command;
#ifdef USE_PAM_DLOPEN
static const char pam_so[] = "libpam.so";
@@ -724,7 +902,7 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
*/
if (DEBUG(verb))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service);
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: INIT service='%s'", service);
}
#ifdef USE_PAM_DLOPEN
@@ -733,7 +911,7 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
*/
if (!dlopen_pam(pam_so))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror());
+ plugin_log(PLOG_ERR, MODULE, "BACKGROUND: could not load PAM lib %s: %s", pam_so, dlerror());
send_control(fd, RESPONSE_INIT_FAILED);
goto done;
}
@@ -744,10 +922,12 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
*/
if (send_control(fd, RESPONSE_INIT_SUCCEEDED) == -1)
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [1]");
goto done;
}
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: initialization succeeded");
+
/*
* Event loop
*/
@@ -762,7 +942,7 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
if (DEBUG(verb))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command);
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: received command code: %d", command);
}
switch (command)
@@ -770,9 +950,10 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
case COMMAND_VERIFY:
if (recv_string(fd, up.username, sizeof(up.username)) == -1
|| recv_string(fd, up.password, sizeof(up.password)) == -1
- || recv_string(fd, up.common_name, sizeof(up.common_name)) == -1)
+ || recv_string(fd, up.common_name, sizeof(up.common_name)) == -1
+ || recv_string(fd, ac_file_name, sizeof(ac_file_name)) == -1)
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n",
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: read error on command channel: code=%d, exiting",
command);
goto done;
}
@@ -780,18 +961,33 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
if (DEBUG(verb))
{
#if 0
- fprintf(stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n",
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER/PASS: %s/%s",
up.username, up.password);
#else
- fprintf(stderr, "AUTH-PAM: BACKGROUND: USER: %s\n", up.username);
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER: %s", up.username);
#endif
}
+ /* If password is of the form SCRV1:base64:base64 split it up */
+ split_scrv1_password(&up);
+
+ /* client wants deferred auth
+ */
+ if (strlen(ac_file_name) > 0)
+ {
+ do_deferred_pam_auth(fd, ac_file_name, service, &up);
+ break;
+ }
+
+
+ /* non-deferred auth: wait for pam result and send
+ * result back via control socketpair
+ */
if (pam_auth(service, &up)) /* Succeeded */
{
if (send_control(fd, RESPONSE_VERIFY_SUCCEEDED) == -1)
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [2]");
goto done;
}
}
@@ -799,7 +995,7 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
{
if (send_control(fd, RESPONSE_VERIFY_FAILED) == -1)
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [3]");
goto done;
}
}
@@ -810,24 +1006,25 @@ pam_server(int fd, const char *service, int verb, const struct name_value_list *
goto done;
case -1:
- fprintf(stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n");
+ plugin_log(PLOG_ERR|PLOG_ERRNO, MODULE, "BACKGROUND: read error on command channel");
goto done;
default:
- fprintf(stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n",
+ plugin_log(PLOG_ERR, MODULE, "BACKGROUND: unknown command code: code=%d, exiting",
command);
goto done;
}
+ plugin_secure_memzero(up.response, sizeof(up.response));
}
done:
-
plugin_secure_memzero(up.password, sizeof(up.password));
+ plugin_secure_memzero(up.response, sizeof(up.response));
#ifdef USE_PAM_DLOPEN
dlclose_pam();
#endif
if (DEBUG(verb))
{
- fprintf(stderr, "AUTH-PAM: BACKGROUND: EXIT\n");
+ plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: EXIT");
}
return;
diff --git a/src/plugins/auth-pam/utils.c b/src/plugins/auth-pam/utils.c
index 4e0c5bf..51c6ce3 100644
--- a/src/plugins/auth-pam/utils.c
+++ b/src/plugins/auth-pam/utils.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
diff --git a/src/plugins/auth-pam/utils.h b/src/plugins/auth-pam/utils.h
index 90fff66..33a14d0 100644
--- a/src/plugins/auth-pam/utils.h
+++ b/src/plugins/auth-pam/utils.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 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
@@ -25,7 +25,7 @@
#define _PLUGIN_AUTH_PAM_UTILS__H
/**
- * Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return
+ * Read 'tosearch', replace all occurrences of 'searchfor' with 'replacewith' and return
* a pointer to the NEW string. Does not modify the input strings. Will not enter an
* infinite loop with clever 'searchfor' and 'replacewith' strings.
*
@@ -35,7 +35,7 @@
* @param searchfor needle to search for in the haystack
* @param replacewith when a match is found, replace needle with this string
*
- * @return Retuns NULL when any parameter is NULL or the worst-case result is to large ( >= SIZE_MAX).
+ * @return Returns NULL when any parameter is NULL or the worst-case result is to large ( >= SIZE_MAX).
* Otherwise it returns a pointer to a new buffer containing the modified input
*/
char *
@@ -48,7 +48,7 @@ searchandreplace(const char *tosearch, const char *searchfor, const char *replac
* @param name Environment variable to look up
* @param envp Environment variable table with all key/value pairs
*
- * @return Returns a pointer to the value of the enviroment variable if found, otherwise NULL is returned.
+ * @return Returns a pointer to the value of the environment variable if found, otherwise NULL is returned.
*/
const char *
get_env(const char *name, const char *envp[]);
diff --git a/src/plugins/down-root/Makefile.in b/src/plugins/down-root/Makefile.in
index 299a791..70887a0 100644
--- a/src/plugins/down-root/Makefile.in
+++ b/src/plugins/down-root/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -233,7 +233,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -247,6 +248,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -274,7 +276,6 @@ 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@
@@ -325,6 +326,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -388,6 +391,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/src/plugins/down-root/down-root.c b/src/plugins/down-root/down-root.c
index c5e5023..555b4d5 100644
--- a/src/plugins/down-root/down-root.c
+++ b/src/plugins/down-root/down-root.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2013 David Sommerseth <davids@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tapctl/Makefile.am b/src/tapctl/Makefile.am
new file mode 100644
index 0000000..7ebbbf2
--- /dev/null
+++ b/src/tapctl/Makefile.am
@@ -0,0 +1,51 @@
+#
+# tapctl -- Utility to manipulate TUN/TAP interfaces on Windows
+#
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+#
+# 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.
+#
+
+include $(top_srcdir)/build/ltrc.inc
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+EXTRA_DIST = \
+ tapctl.vcxproj \
+ tapctl.vcxproj.filters \
+ tapctl.props \
+ tapctl.exe.manifest
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/compat
+
+AM_CFLAGS = \
+ $(TAP_CFLAGS)
+
+if WIN32
+sbin_PROGRAMS = tapctl
+tapctl_CFLAGS = \
+ -municode -D_UNICODE \
+ -UNTDDI_VERSION -U_WIN32_WINNT \
+ -D_WIN32_WINNT=_WIN32_WINNT_VISTA
+tapctl_LDADD = -ladvapi32 -lole32 -lsetupapi
+endif
+
+tapctl_SOURCES = \
+ basic.h \
+ error.c error.h \
+ main.c \
+ tap.c tap.h \
+ tapctl_resources.rc
diff --git a/src/tapctl/Makefile.in b/src/tapctl/Makefile.in
new file mode 100644
index 0000000..00e3f08
--- /dev/null
+++ b/src/tapctl/Makefile.in
@@ -0,0 +1,831 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+
+#
+# tapctl -- Utility to manipulate TUN/TAP interfaces on Windows
+#
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+#
+# 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.
+#
+
+#
+# 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@
+@WIN32_TRUE@sbin_PROGRAMS = tapctl$(EXEEXT)
+subdir = src/tapctl
+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_tapctl_OBJECTS = tapctl-error.$(OBJEXT) tapctl-main.$(OBJEXT) \
+ tapctl-tap.$(OBJEXT) tapctl_resources.$(OBJEXT)
+tapctl_OBJECTS = $(am_tapctl_OBJECTS)
+tapctl_DEPENDENCIES =
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+tapctl_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(tapctl_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+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)/tapctl-error.Po \
+ ./$(DEPDIR)/tapctl-main.Po ./$(DEPDIR)/tapctl-tap.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 = $(tapctl_SOURCES)
+DIST_SOURCES = $(tapctl_SOURCES)
+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@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+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@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
+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@
+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@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
+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@
+runstatedir = @runstatedir@
+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 = \
+ tapctl.vcxproj \
+ tapctl.vcxproj.filters \
+ tapctl.props \
+ tapctl.exe.manifest
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include -I$(top_srcdir)/src/compat
+
+AM_CFLAGS = \
+ $(TAP_CFLAGS)
+
+@WIN32_TRUE@tapctl_CFLAGS = \
+@WIN32_TRUE@ -municode -D_UNICODE \
+@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \
+@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA
+
+@WIN32_TRUE@tapctl_LDADD = -ladvapi32 -lole32 -lsetupapi
+tapctl_SOURCES = \
+ basic.h \
+ error.c error.h \
+ main.c \
+ tap.c tap.h \
+ tapctl_resources.rc
+
+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/tapctl/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/tapctl/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
+
+tapctl$(EXEEXT): $(tapctl_OBJECTS) $(tapctl_DEPENDENCIES) $(EXTRA_tapctl_DEPENDENCIES)
+ @rm -f tapctl$(EXEEXT)
+ $(AM_V_CCLD)$(tapctl_LINK) $(tapctl_OBJECTS) $(tapctl_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tapctl-error.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tapctl-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tapctl-tap.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 $@ $<
+
+tapctl-error.o: error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-error.o -MD -MP -MF $(DEPDIR)/tapctl-error.Tpo -c -o tapctl-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-error.Tpo $(DEPDIR)/tapctl-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='tapctl-error.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-error.o `test -f 'error.c' || echo '$(srcdir)/'`error.c
+
+tapctl-error.obj: error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-error.obj -MD -MP -MF $(DEPDIR)/tapctl-error.Tpo -c -o tapctl-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-error.Tpo $(DEPDIR)/tapctl-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='tapctl-error.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-error.obj `if test -f 'error.c'; then $(CYGPATH_W) 'error.c'; else $(CYGPATH_W) '$(srcdir)/error.c'; fi`
+
+tapctl-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-main.o -MD -MP -MF $(DEPDIR)/tapctl-main.Tpo -c -o tapctl-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-main.Tpo $(DEPDIR)/tapctl-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='tapctl-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+tapctl-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-main.obj -MD -MP -MF $(DEPDIR)/tapctl-main.Tpo -c -o tapctl-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-main.Tpo $(DEPDIR)/tapctl-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='tapctl-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+tapctl-tap.o: tap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-tap.o -MD -MP -MF $(DEPDIR)/tapctl-tap.Tpo -c -o tapctl-tap.o `test -f 'tap.c' || echo '$(srcdir)/'`tap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-tap.Tpo $(DEPDIR)/tapctl-tap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tap.c' object='tapctl-tap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-tap.o `test -f 'tap.c' || echo '$(srcdir)/'`tap.c
+
+tapctl-tap.obj: tap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -MT tapctl-tap.obj -MD -MP -MF $(DEPDIR)/tapctl-tap.Tpo -c -o tapctl-tap.obj `if test -f 'tap.c'; then $(CYGPATH_W) 'tap.c'; else $(CYGPATH_W) '$(srcdir)/tap.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tapctl-tap.Tpo $(DEPDIR)/tapctl-tap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tap.c' object='tapctl-tap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tapctl_CFLAGS) $(CFLAGS) -c -o tapctl-tap.obj `if test -f 'tap.c'; then $(CYGPATH_W) 'tap.c'; else $(CYGPATH_W) '$(srcdir)/tap.c'; fi`
+
+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)/tapctl-error.Po
+ -rm -f ./$(DEPDIR)/tapctl-main.Po
+ -rm -f ./$(DEPDIR)/tapctl-tap.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)/tapctl-error.Po
+ -rm -f ./$(DEPDIR)/tapctl-main.Po
+ -rm -f ./$(DEPDIR)/tapctl-tap.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/tapctl/basic.h b/src/tapctl/basic.h
new file mode 100644
index 0000000..3de237d
--- /dev/null
+++ b/src/tapctl/basic.h
@@ -0,0 +1,66 @@
+/*
+ * basic -- Basic macros
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 BASIC_H
+#define BASIC_H
+
+#ifdef _UNICODE
+#define PRIsLPTSTR "ls"
+#define PRIsLPOLESTR "ls"
+#else
+#define PRIsLPTSTR "s"
+#define PRIsLPOLESTR "ls"
+#endif
+#define PRIXGUID "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
+#define PRIGUID_PARAM(g) \
+ (g).Data1, (g).Data2, (g).Data3, (g).Data4[0], (g).Data4[1], (g).Data4[2], (g).Data4[3], (g).Data4[4], (g).Data4[5], (g).Data4[6], (g).Data4[7]
+#define PRIGUID_PARAM_REF(g) \
+ &(g).Data1, &(g).Data2, &(g).Data3, &(g).Data4[0], &(g).Data4[1], &(g).Data4[2], &(g).Data4[3], &(g).Data4[4], &(g).Data4[5], &(g).Data4[6], &(g).Data4[7]
+
+#define __L(q) L ## q
+#define _L(q) __L(q)
+
+#ifndef _In_
+#define _In_
+#endif
+#ifndef _In_opt_
+#define _In_opt_
+#endif
+#ifndef _In_z_
+#define _In_z_
+#endif
+#ifndef _Inout_
+#define _Inout_
+#endif
+#ifndef _Inout_opt_
+#define _Inout_opt_
+#endif
+#ifndef _Out_
+#define _Out_
+#endif
+#ifndef _Out_opt_
+#define _Out_opt_
+#endif
+#ifndef _Out_z_cap_
+#define _Out_z_cap_(n)
+#endif
+
+#endif /* ifndef BASIC_H */
diff --git a/src/tapctl/error.c b/src/tapctl/error.c
new file mode 100644
index 0000000..16662ec
--- /dev/null
+++ b/src/tapctl/error.c
@@ -0,0 +1,36 @@
+/*
+ * error -- OpenVPN compatible error reporting API
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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.
+ */
+
+#include "error.h"
+
+
+/* Globals */
+unsigned int x_debug_level; /* GLOBAL */
+
+
+void
+x_msg(const unsigned int flags, const char *format, ...)
+{
+ va_list arglist;
+ va_start(arglist, format);
+ x_msg_va(flags, format, arglist);
+ va_end(arglist);
+}
diff --git a/src/tapctl/error.h b/src/tapctl/error.h
new file mode 100644
index 0000000..fa6e3ff
--- /dev/null
+++ b/src/tapctl/error.h
@@ -0,0 +1,97 @@
+/*
+ * error -- OpenVPN compatible error reporting API
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 ERROR_H
+#define ERROR_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/*
+ * These globals should not be accessed directly,
+ * but rather through macros or inline functions defined below.
+ */
+extern unsigned int x_debug_level;
+extern int x_msg_line_num;
+
+/* msg() flags */
+
+#define M_DEBUG_LEVEL (0x0F) /* debug level mask */
+
+#define M_FATAL (1<<4) /* exit program */
+#define M_NONFATAL (1<<5) /* non-fatal error */
+#define M_WARN (1<<6) /* call syslog with LOG_WARNING */
+#define M_DEBUG (1<<7)
+
+#define M_ERRNO (1<<8) /* show errno description */
+
+#define M_NOMUTE (1<<11) /* don't do mute processing */
+#define M_NOPREFIX (1<<12) /* don't show date/time prefix */
+#define M_USAGE_SMALL (1<<13) /* fatal options error, call usage_small */
+#define M_MSG_VIRT_OUT (1<<14) /* output message through msg_status_output callback */
+#define M_OPTERR (1<<15) /* print "Options error:" prefix */
+#define M_NOLF (1<<16) /* don't print new line */
+#define M_NOIPREFIX (1<<17) /* don't print instance prefix */
+
+/* flag combinations which are frequently used */
+#define M_ERR (M_FATAL | M_ERRNO)
+#define M_USAGE (M_USAGE_SMALL | M_NOPREFIX | M_OPTERR)
+#define M_CLIENT (M_MSG_VIRT_OUT | M_NOMUTE | M_NOIPREFIX)
+
+
+/** Check muting filter */
+bool dont_mute(unsigned int flags);
+
+/* Macro to ensure (and teach static analysis tools) we exit on fatal errors */
+#ifdef _MSC_VER
+#pragma warning(disable: 4127) /* EXIT_FATAL(flags) macro raises "warning C4127: conditional expression is constant" on each non M_FATAL invocation. */
+#endif
+#define EXIT_FATAL(flags) do { if ((flags) & M_FATAL) {_exit(1);}} while (false)
+
+#define HAVE_VARARG_MACROS
+#define msg(flags, ...) do { if (msg_test(flags)) {x_msg((flags), __VA_ARGS__);} EXIT_FATAL(flags); } while (false)
+#ifdef ENABLE_DEBUG
+#define dmsg(flags, ...) do { if (msg_test(flags)) {x_msg((flags), __VA_ARGS__);} EXIT_FATAL(flags); } while (false)
+#else
+#define dmsg(flags, ...)
+#endif
+
+void x_msg(const unsigned int flags, const char *format, ...); /* should be called via msg above */
+
+void x_msg_va(const unsigned int flags, const char *format, va_list arglist);
+
+/* Inline functions */
+
+static inline bool
+check_debug_level(unsigned int level)
+{
+ return (level & M_DEBUG_LEVEL) <= x_debug_level;
+}
+
+/** Return true if flags represent and enabled, not muted log level */
+static inline bool
+msg_test(unsigned int flags)
+{
+ return check_debug_level(flags) && dont_mute(flags);
+}
+
+#endif /* ifndef ERROR_H */
diff --git a/src/tapctl/main.c b/src/tapctl/main.c
new file mode 100644
index 0000000..73ec40b
--- /dev/null
+++ b/src/tapctl/main.c
@@ -0,0 +1,444 @@
+/*
+ * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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
+#ifdef HAVE_CONFIG_VERSION_H
+#include <config-version.h>
+#endif
+
+#include "tap.h"
+#include "error.h"
+
+#include <objbase.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <tchar.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "setupapi.lib")
+#endif
+
+
+const TCHAR title_string[] =
+ TEXT(PACKAGE_NAME) TEXT(" ") TEXT(PACKAGE_VERSION)
+ TEXT(" built on ") TEXT(__DATE__)
+;
+
+static const TCHAR usage_message[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl <command> [<command specific options>]\n")
+ TEXT("\n")
+ TEXT("Commands:\n")
+ TEXT("\n")
+ TEXT("create Create a new TUN/TAP adapter\n")
+ TEXT("list List TUN/TAP adapters\n")
+ TEXT("delete Delete specified network adapter\n")
+ TEXT("help Display this text\n")
+ TEXT("\n")
+ TEXT("Hint: Use \"tapctl help <command>\" to display help for particular command.\n")
+;
+
+static const TCHAR usage_message_create[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Creates a new TUN/TAP adapter\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl create [<options>]\n")
+ TEXT("\n")
+ TEXT("Options:\n")
+ TEXT("\n")
+ TEXT("--name <name> Set TUN/TAP adapter name. Should the adapter with given name \n")
+ TEXT(" already exist, an error is returned. If this option is not \n")
+ TEXT(" specified, a default adapter name is chosen by Windows. \n")
+ TEXT(" Note: This name can also be specified as OpenVPN's --dev-node \n")
+ TEXT(" option. \n")
+ TEXT("--hwid <hwid> Adapter hardware ID. Default value is root\\tap0901, which \n")
+ TEXT(" describes tap-windows6 driver. To work with wintun driver, \n")
+ TEXT(" specify 'wintun'. \n")
+ TEXT("\n")
+ TEXT("Output:\n")
+ TEXT("\n")
+ TEXT("This command prints newly created TUN/TAP adapter's GUID to stdout. \n")
+;
+
+static const TCHAR usage_message_list[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Lists TUN/TAP adapters\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl list\n")
+ TEXT("\n")
+ TEXT("Options:\n")
+ TEXT("\n")
+ TEXT("--hwid <hwid> Adapter hardware ID. By default, root\\tap0901, tap0901 and \n")
+ TEXT(" wintun adapters are listed. Use this switch to limit the list. \n")
+ TEXT("\n")
+ TEXT("Output:\n")
+ TEXT("\n")
+ TEXT("This command prints all TUN/TAP adapters to stdout. \n")
+;
+
+static const TCHAR usage_message_delete[] =
+ TEXT("%s\n")
+ TEXT("\n")
+ TEXT("Deletes the specified network adapter\n")
+ TEXT("\n")
+ TEXT("Usage:\n")
+ TEXT("\n")
+ TEXT("tapctl delete <adapter GUID | adapter name>\n")
+;
+
+
+/**
+ * Print the help message.
+ */
+static void
+usage(void)
+{
+ _ftprintf(stderr,
+ usage_message,
+ title_string);
+}
+
+
+/**
+ * Program entry point
+ */
+int __cdecl
+_tmain(int argc, LPCTSTR argv[])
+{
+ int iResult;
+ BOOL bRebootRequired = FALSE;
+
+ /* Ask SetupAPI to keep quiet. */
+ SetupSetNonInteractiveMode(TRUE);
+
+ if (argc < 2)
+ {
+ usage();
+ return 1;
+ }
+ else if (_tcsicmp(argv[1], TEXT("help")) == 0)
+ {
+ /* Output help. */
+ if (argc < 3)
+ {
+ usage();
+ }
+ else if (_tcsicmp(argv[2], TEXT("create")) == 0)
+ {
+ _ftprintf(stderr, usage_message_create, title_string);
+ }
+ else if (_tcsicmp(argv[2], TEXT("list")) == 0)
+ {
+ _ftprintf(stderr, usage_message_list, title_string);
+ }
+ else if (_tcsicmp(argv[2], TEXT("delete")) == 0)
+ {
+ _ftprintf(stderr, usage_message_delete, title_string);
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[2]);
+ }
+
+ return 1;
+ }
+ else if (_tcsicmp(argv[1], TEXT("create")) == 0)
+ {
+ LPCTSTR szName = NULL;
+ LPCTSTR szHwId = TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID);
+
+ /* Parse options. */
+ for (int i = 2; i < argc; i++)
+ {
+ if (_tcsicmp(argv[i], TEXT("--name")) == 0)
+ {
+ szName = argv[++i];
+ }
+ else
+ if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
+ {
+ szHwId = argv[++i];
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help create\" to list supported options. Ignored.\n"), argv[i]);
+ }
+ }
+
+ /* Create TUN/TAP adapter. */
+ GUID guidAdapter;
+ LPOLESTR szAdapterId = NULL;
+ DWORD dwResult = tap_create_adapter(
+ NULL,
+ TEXT("Virtual Ethernet"),
+ szHwId,
+ &bRebootRequired,
+ &guidAdapter);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Creating TUN/TAP adapter failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ if (szName)
+ {
+ /* Get existing network adapters. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto create_delete_adapter;
+ }
+
+ /* Check for duplicates. */
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ if (_tcsicmp(szName, pAdapter->szName) == 0)
+ {
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+ _ftprintf(stderr, TEXT("Adapter \"%s\" already exists (GUID %") TEXT(PRIsLPOLESTR) TEXT(").\n"), pAdapter->szName, szAdapterId);
+ CoTaskMemFree(szAdapterId);
+ iResult = 1; goto create_cleanup_pAdapterList;
+ }
+ }
+
+ /* Rename the adapter. */
+ dwResult = tap_set_adapter_name(&guidAdapter, szName, FALSE);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ _ftprintf(stderr, TEXT("Renaming TUN/TAP adapter %") TEXT(PRIsLPOLESTR) TEXT(" to \"%s\" failed (error 0x%x).\n"), szAdapterId, szName, dwResult);
+ CoTaskMemFree(szAdapterId);
+ iResult = 1; goto quit;
+ }
+
+ iResult = 0;
+
+create_cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+ if (iResult)
+ {
+ goto create_delete_adapter;
+ }
+ }
+
+ /* Output adapter GUID. */
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+ _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\n"), szAdapterId);
+ CoTaskMemFree(szAdapterId);
+
+ iResult = 0; goto quit;
+
+create_delete_adapter:
+ tap_delete_adapter(
+ NULL,
+ &guidAdapter,
+ &bRebootRequired);
+ iResult = 1; goto quit;
+ }
+ else if (_tcsicmp(argv[1], TEXT("list")) == 0)
+ {
+ TCHAR szzHwId[0x100] =
+ TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
+ TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0")
+ TEXT("Wintun\0");
+
+ /* Parse options. */
+ for (int i = 2; i < argc; i++)
+ {
+ if (_tcsicmp(argv[i], TEXT("--hwid")) == 0)
+ {
+ memset(szzHwId, 0, sizeof(szzHwId));
+ ++i;
+ memcpy_s(szzHwId, sizeof(szzHwId) - 2*sizeof(TCHAR) /*requires double zero termination*/, argv[i], _tcslen(argv[i])*sizeof(TCHAR));
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown option \"%s\". Please, use \"tapctl help list\" to list supported options. Ignored.\n"), argv[i]);
+ }
+ }
+
+ /* Output list of adapters with given hardware ID. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, szzHwId, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
+ {
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
+ _ftprintf(stdout, TEXT("%") TEXT(PRIsLPOLESTR) TEXT("\t%") TEXT(PRIsLPTSTR) TEXT("\n"), szAdapterId, pAdapter->szName);
+ CoTaskMemFree(szAdapterId);
+ }
+
+ iResult = 0;
+ tap_free_adapter_list(pAdapterList);
+ }
+ else if (_tcsicmp(argv[1], TEXT("delete")) == 0)
+ {
+ if (argc < 3)
+ {
+ _ftprintf(stderr, TEXT("Missing adapter GUID or name. Please, use \"tapctl help delete\" for usage info.\n"));
+ return 1;
+ }
+
+ GUID guidAdapter;
+ if (FAILED(IIDFromString(argv[2], (LPIID)&guidAdapter)))
+ {
+ /* The argument failed to covert to GUID. Treat it as the adapter name. */
+ struct tap_adapter_node *pAdapterList = NULL;
+ DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Enumerating TUN/TAP adapters failed (error 0x%x).\n"), dwResult);
+ iResult = 1; goto quit;
+ }
+
+ for (struct tap_adapter_node *pAdapter = pAdapterList;; pAdapter = pAdapter->pNext)
+ {
+ if (pAdapter == NULL)
+ {
+ _ftprintf(stderr, TEXT("\"%s\" adapter not found.\n"), argv[2]);
+ iResult = 1; goto delete_cleanup_pAdapterList;
+ }
+ else if (_tcsicmp(argv[2], pAdapter->szName) == 0)
+ {
+ memcpy(&guidAdapter, &pAdapter->guid, sizeof(GUID));
+ break;
+ }
+ }
+
+ iResult = 0;
+
+delete_cleanup_pAdapterList:
+ tap_free_adapter_list(pAdapterList);
+ if (iResult)
+ {
+ goto quit;
+ }
+ }
+
+ /* Delete the network adapter. */
+ DWORD dwResult = tap_delete_adapter(
+ NULL,
+ &guidAdapter,
+ &bRebootRequired);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ _ftprintf(stderr, TEXT("Deleting adapter \"%s\" failed (error 0x%x).\n"), argv[2], dwResult);
+ iResult = 1; goto quit;
+ }
+
+ iResult = 0; goto quit;
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Unknown command \"%s\". Please, use \"tapctl help\" to list supported commands.\n"), argv[1]);
+ return 1;
+ }
+
+quit:
+ if (bRebootRequired)
+ {
+ _ftprintf(stderr, TEXT("A system reboot is required.\n"));
+ }
+
+ return iResult;
+}
+
+
+bool
+dont_mute(unsigned int flags)
+{
+ UNREFERENCED_PARAMETER(flags);
+
+ return true;
+}
+
+
+void
+x_msg_va(const unsigned int flags, const char *format, va_list arglist)
+{
+ /* Output message string. Note: Message strings don't contain line terminators. */
+ vfprintf(stderr, format, arglist);
+ _ftprintf(stderr, TEXT("\n"));
+
+ if ((flags & M_ERRNO) != 0)
+ {
+ /* Output system error message (if possible). */
+ DWORD dwResult = GetLastError();
+ LPTSTR szErrMessage = NULL;
+ if (FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ dwResult,
+ 0,
+ (LPTSTR)&szErrMessage,
+ 0,
+ NULL) && szErrMessage)
+ {
+ /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
+ for (size_t i = 0, i_last = 0;; i++)
+ {
+ if (szErrMessage[i])
+ {
+ if (!_istspace(szErrMessage[i]))
+ {
+ i_last = i + 1;
+ }
+ }
+ else
+ {
+ szErrMessage[i_last] = 0;
+ break;
+ }
+ }
+
+ /* Output error message. */
+ _ftprintf(stderr, TEXT("Error 0x%x: %s\n"), dwResult, szErrMessage);
+
+ LocalFree(szErrMessage);
+ }
+ else
+ {
+ _ftprintf(stderr, TEXT("Error 0x%x\n"), dwResult);
+ }
+ }
+}
diff --git a/src/tapctl/tap.c b/src/tapctl/tap.c
new file mode 100644
index 0000000..dd4a10a
--- /dev/null
+++ b/src/tapctl/tap.c
@@ -0,0 +1,1367 @@
+/*
+ * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2018-2020 Simon Rozman <simon@rozman.si>
+ *
+ * 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 "tap.h"
+#include "error.h"
+
+#include <windows.h>
+#include <cfgmgr32.h>
+#include <objbase.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <tchar.h>
+#include <newdev.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "advapi32.lib")
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "setupapi.lib")
+#pragma comment(lib, "newdev.lib")
+#endif
+
+
+const static GUID GUID_DEVCLASS_NET = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
+
+const static TCHAR szAdapterRegKeyPathTemplate[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\%") TEXT(PRIsLPOLESTR) TEXT("\\%") TEXT(PRIsLPOLESTR) TEXT("\\Connection");
+#define ADAPTER_REGKEY_PATH_MAX (_countof(TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\")) - 1 + 38 + _countof(TEXT("\\")) - 1 + 38 + _countof(TEXT("\\Connection")))
+
+/**
+ * Dynamically load a library and find a function in it
+ *
+ * @param libname Name of the library to load
+ * @param funcname Name of the function to find
+ * @param m Pointer to a module. On return this is set to the
+ * the handle to the loaded library. The caller must
+ * free it by calling FreeLibrary() if not NULL.
+ *
+ * @return Pointer to the function
+ * NULL on error -- use GetLastError() to find the error code.
+ *
+ **/
+static void *
+find_function(const WCHAR *libname, const char *funcname, HMODULE *m)
+{
+ WCHAR libpath[MAX_PATH];
+ void *fptr = NULL;
+
+ /* Make sure the dll is loaded from the system32 folder */
+ if (!GetSystemDirectoryW(libpath, _countof(libpath)))
+ {
+ return NULL;
+ }
+
+ size_t len = _countof(libpath) - wcslen(libpath) - 1;
+ if (len < wcslen(libname) + 1)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return NULL;
+ }
+ wcsncat(libpath, L"\\", len);
+ wcsncat(libpath, libname, len-1);
+
+ *m = LoadLibraryW(libpath);
+ if (*m == NULL)
+ {
+ return NULL;
+ }
+ fptr = GetProcAddress(*m, funcname);
+ if (!fptr)
+ {
+ FreeLibrary(*m);
+ *m = NULL;
+ return NULL;
+ }
+ return fptr;
+}
+
+/**
+ * Returns length of string of strings
+ *
+ * @param szz Pointer to a string of strings (terminated by an empty string)
+ *
+ * @return Number of characters not counting the final zero terminator
+ **/
+static inline size_t
+_tcszlen(_In_z_ LPCTSTR szz)
+{
+ LPCTSTR s;
+ for (s = szz; s[0]; s += _tcslen(s) + 1)
+ {
+ }
+ return s - szz;
+}
+
+
+/**
+ * Checks if string is contained in the string of strings. Comparison is made case-insensitive.
+ *
+ * @param szzHay Pointer to a string of strings (terminated by an empty string) we are
+ * looking in
+ *
+ * @param szNeedle The string we are searching for
+ *
+ * @return Pointer to the string in szzHay that matches szNeedle is found; NULL otherwise
+ */
+static LPCTSTR
+_tcszistr(_In_z_ LPCTSTR szzHay, _In_z_ LPCTSTR szNeedle)
+{
+ for (LPCTSTR s = szzHay; s[0]; s += _tcslen(s) + 1)
+ {
+ if (_tcsicmp(s, szNeedle) == 0)
+ {
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Function that performs a specific task on a device
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+typedef DWORD (*devop_func_t)(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _Inout_ LPBOOL pbRebootRequired);
+
+
+/**
+ * Checks device install parameters if a system reboot is required.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+check_reboot(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ if (pbRebootRequired == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ SP_DEVINSTALL_PARAMS devinstall_params = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS) };
+ if (!SetupDiGetDeviceInstallParams(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ &devinstall_params))
+ {
+ DWORD dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceInstallParams failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ if ((devinstall_params.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0)
+ {
+ *pbRebootRequired = TRUE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+/**
+ * Deletes the device.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+delete_device(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ SP_REMOVEDEVICE_PARAMS params =
+ {
+ .ClassInstallHeader =
+ {
+ .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_REMOVE,
+ },
+ .Scope = DI_REMOVEDEVICE_GLOBAL,
+ .HwProfile = 0,
+ };
+
+ /* Set class installer parameters for DIF_REMOVE. */
+ if (!SetupDiSetClassInstallParams(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ &params.ClassInstallHeader,
+ sizeof(SP_REMOVEDEVICE_PARAMS)))
+ {
+ DWORD dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Call appropriate class installer. */
+ if (!SetupDiCallClassInstaller(
+ DIF_REMOVE,
+ hDeviceInfoSet,
+ pDeviceInfoData))
+ {
+ DWORD dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Check if a system reboot is required. */
+ check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
+ return ERROR_SUCCESS;
+}
+
+
+/**
+ * Changes the device state.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param bEnable TRUE to enable the device; FALSE to disable.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+change_device_state(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _In_ BOOL bEnable,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ SP_PROPCHANGE_PARAMS params =
+ {
+ .ClassInstallHeader =
+ {
+ .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_PROPERTYCHANGE,
+ },
+ .StateChange = bEnable ? DICS_ENABLE : DICS_DISABLE,
+ .Scope = DICS_FLAG_GLOBAL,
+ .HwProfile = 0,
+ };
+
+ /* Set class installer parameters for DIF_PROPERTYCHANGE. */
+ if (!SetupDiSetClassInstallParams(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ &params.ClassInstallHeader,
+ sizeof(SP_PROPCHANGE_PARAMS)))
+ {
+ DWORD dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Call appropriate class installer. */
+ if (!SetupDiCallClassInstaller(
+ DIF_PROPERTYCHANGE,
+ hDeviceInfoSet,
+ pDeviceInfoData))
+ {
+ DWORD dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_PROPERTYCHANGE) failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Check if a system reboot is required. */
+ check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
+ return ERROR_SUCCESS;
+}
+
+
+/**
+ * Enables the device.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+enable_device(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ return change_device_state(hDeviceInfoSet, pDeviceInfoData, TRUE, pbRebootRequired);
+}
+
+
+/**
+ * Disables the device.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+disable_device(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ return change_device_state(hDeviceInfoSet, pDeviceInfoData, FALSE, pbRebootRequired);
+}
+
+
+/**
+ * Reads string value from registry key.
+ *
+ * @param hKey Handle of the registry key to read from. Must be opened with read
+ * access.
+ *
+ * @param szName Name of the value to read.
+ *
+ * @param pszValue Pointer to string to retrieve registry value. If the value type is
+ * REG_EXPAND_SZ the value is expanded using ExpandEnvironmentStrings().
+ * The string must be released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+static DWORD
+get_reg_string(
+ _In_ HKEY hKey,
+ _In_ LPCTSTR szName,
+ _Out_ LPTSTR *pszValue)
+{
+ if (pszValue == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ DWORD dwValueType = REG_NONE, dwSize = 0;
+ DWORD dwResult = RegQueryValueEx(
+ hKey,
+ szName,
+ NULL,
+ &dwValueType,
+ NULL,
+ &dwSize);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: enumerating \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
+ return dwResult;
+ }
+
+ switch (dwValueType)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ {
+ /* Read value. */
+ LPTSTR szValue = (LPTSTR)malloc(dwSize);
+ if (szValue == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSize);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ dwResult = RegQueryValueEx(
+ hKey,
+ szName,
+ NULL,
+ NULL,
+ (LPBYTE)szValue,
+ &dwSize);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: reading \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
+ free(szValue);
+ return dwResult;
+ }
+
+ if (dwValueType == REG_EXPAND_SZ)
+ {
+ /* Expand the environment strings. */
+ DWORD
+ dwSizeExp = dwSize * 2,
+ dwCountExp =
+#ifdef UNICODE
+ dwSizeExp / sizeof(TCHAR);
+#else
+ dwSizeExp / sizeof(TCHAR) - 1; /* Note: ANSI version requires one extra char. */
+#endif
+ LPTSTR szValueExp = (LPTSTR)malloc(dwSizeExp);
+ if (szValueExp == NULL)
+ {
+ free(szValue);
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ DWORD dwCountExpResult = ExpandEnvironmentStrings(
+ szValue,
+ szValueExp, dwCountExp
+ );
+ if (dwCountExpResult == 0)
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: expanding \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
+ free(szValueExp);
+ free(szValue);
+ return dwResult;
+ }
+ else if (dwCountExpResult <= dwCountExp)
+ {
+ /* The buffer was big enough. */
+ free(szValue);
+ *pszValue = szValueExp;
+ return ERROR_SUCCESS;
+ }
+ else
+ {
+ /* Retry with a bigger buffer. */
+ free(szValueExp);
+#ifdef UNICODE
+ dwSizeExp = dwCountExpResult * sizeof(TCHAR);
+#else
+ /* Note: ANSI version requires one extra char. */
+ dwSizeExp = (dwCountExpResult + 1) * sizeof(TCHAR);
+#endif
+ dwCountExp = dwCountExpResult;
+ szValueExp = (LPTSTR)malloc(dwSizeExp);
+ if (szValueExp == NULL)
+ {
+ free(szValue);
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ dwCountExpResult = ExpandEnvironmentStrings(
+ szValue,
+ szValueExp, dwCountExp);
+ free(szValue);
+ *pszValue = szValueExp;
+ return ERROR_SUCCESS;
+ }
+ }
+ else
+ {
+ *pszValue = szValue;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ default:
+ msg(M_NONFATAL, "%s: \"%" PRIsLPTSTR "\" registry value is not string (type %u)", __FUNCTION__, dwValueType);
+ return ERROR_UNSUPPORTED_TYPE;
+ }
+}
+
+
+/**
+ * Returns network adapter ID.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param iNumAttempts After the device is created, it might take some time before the
+ * registry key is populated. This parameter specifies the number of
+ * attempts to read NetCfgInstanceId value from registry. A 1sec sleep
+ * is inserted between retry attempts.
+ *
+ * @param pguidAdapter A pointer to GUID that receives network adapter ID.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+get_net_adapter_guid(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _In_ int iNumAttempts,
+ _Out_ LPGUID pguidAdapter)
+{
+ DWORD dwResult = ERROR_BAD_ARGUMENTS;
+
+ if (pguidAdapter == NULL || iNumAttempts < 1)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key. */
+ HKEY hKey = SetupDiOpenDevRegKey(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DRV,
+ KEY_READ);
+ if (hKey == INVALID_HANDLE_VALUE)
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiOpenDevRegKey failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ while (iNumAttempts > 0)
+ {
+ /* Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the output with error messages while the registry is still being populated. */
+ LPTSTR szCfgGuidString = NULL;
+ dwResult = RegQueryValueEx(hKey, TEXT("NetCfgInstanceId"), NULL, NULL, NULL, NULL);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ if (dwResult == ERROR_FILE_NOT_FOUND && --iNumAttempts > 0)
+ {
+ /* Wait and retry. */
+ Sleep(1000);
+ continue;
+ }
+
+ SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_NONFATAL | M_ERRNO, "%s: querying \"NetCfgInstanceId\" registry value failed", __FUNCTION__);
+ break;
+ }
+
+ /* Read the NetCfgInstanceId value now. */
+ dwResult = get_reg_string(
+ hKey,
+ TEXT("NetCfgInstanceId"),
+ &szCfgGuidString);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ break;
+ }
+
+ dwResult = SUCCEEDED(CLSIDFromString(szCfgGuidString, (LPCLSID)pguidAdapter)) ? ERROR_SUCCESS : ERROR_INVALID_DATA;
+ free(szCfgGuidString);
+ break;
+ }
+
+ RegCloseKey(hKey);
+ return dwResult;
+}
+
+
+/**
+ * Returns a specified Plug and Play device property.
+ *
+ * @param hDeviceInfoSet A handle to a device information set that contains a device
+ * information element that represents the device.
+ *
+ * @param pDeviceInfoData A pointer to an SP_DEVINFO_DATA structure that specifies the
+ * device information element in hDeviceInfoSet.
+ *
+ * @param dwProperty Specifies the property to be retrieved. See
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/ff551967.aspx
+ *
+ * @pdwPropertyRegDataType A pointer to a variable that receives the data type of the
+ * property that is being retrieved. This is one of the standard
+ * registry data types. This parameter is optional and can be NULL.
+ *
+ * @param ppData A pointer to pointer to data that receives the device property. The
+ * data must be released with free() after use.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+get_device_reg_property(
+ _In_ HDEVINFO hDeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA pDeviceInfoData,
+ _In_ DWORD dwProperty,
+ _Out_opt_ LPDWORD pdwPropertyRegDataType,
+ _Out_ LPVOID *ppData)
+{
+ DWORD dwResult = ERROR_BAD_ARGUMENTS;
+
+ if (ppData == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Try with stack buffer first. */
+ BYTE bBufStack[128];
+ DWORD dwRequiredSize = 0;
+ if (SetupDiGetDeviceRegistryProperty(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ dwProperty,
+ pdwPropertyRegDataType,
+ bBufStack,
+ sizeof(bBufStack),
+ &dwRequiredSize))
+ {
+ /* Copy from stack. */
+ *ppData = malloc(dwRequiredSize);
+ if (*ppData == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ memcpy(*ppData, bBufStack, dwRequiredSize);
+ return ERROR_SUCCESS;
+ }
+ else
+ {
+ dwResult = GetLastError();
+ if (dwResult == ERROR_INSUFFICIENT_BUFFER)
+ {
+ /* Allocate on heap and retry. */
+ *ppData = malloc(dwRequiredSize);
+ if (*ppData == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ if (SetupDiGetDeviceRegistryProperty(
+ hDeviceInfoSet,
+ pDeviceInfoData,
+ dwProperty,
+ pdwPropertyRegDataType,
+ *ppData,
+ dwRequiredSize,
+ &dwRequiredSize))
+ {
+ return ERROR_SUCCESS;
+ }
+ else
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
+ return dwResult;
+ }
+ }
+ else
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
+ return dwResult;
+ }
+ }
+}
+
+
+DWORD
+tap_create_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_opt_ LPCTSTR szDeviceDescription,
+ _In_ LPCTSTR szHwId,
+ _Inout_ LPBOOL pbRebootRequired,
+ _Out_ LPGUID pguidAdapter)
+{
+ DWORD dwResult;
+ HMODULE libnewdev = NULL;
+
+ if (szHwId == NULL
+ || pbRebootRequired == NULL
+ || pguidAdapter == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Create an empty device info set for network adapter device class. */
+ HDEVINFO hDevInfoList = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, hwndParent);
+ if (hDevInfoList == INVALID_HANDLE_VALUE)
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfoList failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Get the device class name from GUID. */
+ TCHAR szClassName[MAX_CLASS_NAME_LEN];
+ if (!SetupDiClassNameFromGuid(
+ &GUID_DEVCLASS_NET,
+ szClassName,
+ _countof(szClassName),
+ NULL))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiClassNameFromGuid failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Create a new device info element and add it to the device info set. */
+ SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ if (!SetupDiCreateDeviceInfo(
+ hDevInfoList,
+ szClassName,
+ &GUID_DEVCLASS_NET,
+ szDeviceDescription,
+ hwndParent,
+ DICD_GENERATE_ID,
+ &devinfo_data))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfo failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Set a device information element as the selected member of a device information set. */
+ if (!SetupDiSetSelectedDevice(
+ hDevInfoList,
+ &devinfo_data))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiSetSelectedDevice failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Set Plug&Play device hardware ID property. */
+ if (!SetupDiSetDeviceRegistryProperty(
+ hDevInfoList,
+ &devinfo_data,
+ SPDRP_HARDWAREID,
+ (const BYTE *)szHwId, (DWORD)((_tcslen(szHwId) + 1) * sizeof(TCHAR))))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiSetDeviceRegistryProperty failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Register the device instance with the PnP Manager */
+ if (!SetupDiCallClassInstaller(
+ DIF_REGISTERDEVICE,
+ hDevInfoList,
+ &devinfo_data))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Install the device using DiInstallDevice()
+ * We instruct the system to use the best driver in the driver store
+ * by setting the drvinfo argument of DiInstallDevice as NULL. This
+ * assumes a driver is already installed in the driver store.
+ */
+#ifdef HAVE_DIINSTALLDEVICE
+ if (!DiInstallDevice(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
+#else
+ /* mingw does not resolve DiInstallDevice, so load it at run time. */
+ typedef BOOL (WINAPI *DiInstallDeviceFn) (HWND, HDEVINFO, SP_DEVINFO_DATA *,
+ SP_DRVINFO_DATA *, DWORD, BOOL *);
+ DiInstallDeviceFn installfn
+ = find_function (L"newdev.dll", "DiInstallDevice", &libnewdev);
+
+ if (!installfn)
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: Failed to locate DiInstallDevice()", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ if (!installfn(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
+#endif
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL | M_ERRNO, "%s: DiInstallDevice failed", __FUNCTION__);
+ goto cleanup_remove_device;
+ }
+
+ /* Get network adapter ID from registry. Retry for max 30sec. */
+ dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 30, pguidAdapter);
+
+cleanup_remove_device:
+ if (dwResult != ERROR_SUCCESS)
+ {
+ /* The adapter was installed. But, the adapter ID was unobtainable. Clean-up. */
+ SP_REMOVEDEVICE_PARAMS removedevice_params =
+ {
+ .ClassInstallHeader =
+ {
+ .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_REMOVE,
+ },
+ .Scope = DI_REMOVEDEVICE_GLOBAL,
+ .HwProfile = 0,
+ };
+
+ /* Set class installer parameters for DIF_REMOVE. */
+ if (SetupDiSetClassInstallParams(
+ hDevInfoList,
+ &devinfo_data,
+ &removedevice_params.ClassInstallHeader,
+ sizeof(SP_REMOVEDEVICE_PARAMS)))
+ {
+ /* Call appropriate class installer. */
+ if (SetupDiCallClassInstaller(
+ DIF_REMOVE,
+ hDevInfoList,
+ &devinfo_data))
+ {
+ /* Check if a system reboot is required. */
+ check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
+ }
+ else
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
+ }
+ }
+ else
+ {
+ msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
+ }
+ }
+
+cleanup_hDevInfoList:
+ if (libnewdev)
+ {
+ FreeLibrary(libnewdev);
+ }
+ SetupDiDestroyDeviceInfoList(hDevInfoList);
+ return dwResult;
+}
+
+
+/**
+ * Performs a given task on an adapter.
+ *
+ * @param hwndParent A handle to the top-level window to use for any user adapter that is
+ * related to non-device-specific actions (such as a select-device dialog
+ * box that uses the global class driver list). This handle is optional
+ * and can be NULL. If a specific top-level window is not required, set
+ * hwndParent to NULL.
+ *
+ * @param pguidAdapter A pointer to GUID that contains network adapter ID.
+ *
+ * @param funcOperation A pointer for the function to perform specific task on the adapter.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+static DWORD
+execute_on_first_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_ LPCGUID pguidAdapter,
+ _In_ devop_func_t funcOperation,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ DWORD dwResult;
+
+ if (pguidAdapter == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Create a list of network devices. */
+ HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
+ &GUID_DEVCLASS_NET,
+ NULL,
+ hwndParent,
+ DIGCF_PRESENT,
+ NULL,
+ NULL,
+ NULL);
+ if (hDevInfoList == INVALID_HANDLE_VALUE)
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Retrieve information associated with a device information set. */
+ SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
+ if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Iterate. */
+ for (DWORD dwIndex = 0;; dwIndex++)
+ {
+ /* Get the device from the list. */
+ SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ if (!SetupDiEnumDeviceInfo(
+ hDevInfoList,
+ dwIndex,
+ &devinfo_data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ {
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)pguidAdapter, &szAdapterId);
+ msg(M_NONFATAL, "%s: Adapter %" PRIsLPOLESTR " not found", __FUNCTION__, szAdapterId);
+ CoTaskMemFree(szAdapterId);
+ dwResult = ERROR_FILE_NOT_FOUND;
+ goto cleanup_hDevInfoList;
+ }
+ else
+ {
+ /* Something is wrong with this device. Skip it. */
+ msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
+ continue;
+ }
+ }
+
+ /* Get adapter GUID. */
+ GUID guidAdapter;
+ dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ /* Something is wrong with this device. Skip it. */
+ continue;
+ }
+
+ /* Compare GUIDs. */
+ if (memcmp(pguidAdapter, &guidAdapter, sizeof(GUID)) == 0)
+ {
+ dwResult = funcOperation(hDevInfoList, &devinfo_data, pbRebootRequired);
+ break;
+ }
+ }
+
+cleanup_hDevInfoList:
+ SetupDiDestroyDeviceInfoList(hDevInfoList);
+ return dwResult;
+}
+
+
+DWORD
+tap_delete_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_ LPCGUID pguidAdapter,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ return execute_on_first_adapter(hwndParent, pguidAdapter, delete_device, pbRebootRequired);
+}
+
+
+DWORD
+tap_enable_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_ LPCGUID pguidAdapter,
+ _In_ BOOL bEnable,
+ _Inout_ LPBOOL pbRebootRequired)
+{
+ return execute_on_first_adapter(hwndParent, pguidAdapter, bEnable ? enable_device : disable_device, pbRebootRequired);
+}
+
+/* stripped version of ExecCommand in interactive.c */
+static DWORD
+ExecCommand(const WCHAR* cmdline)
+{
+ DWORD exit_code;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
+ WCHAR* cmdline_dup = NULL;
+
+ ZeroMemory(&si, sizeof(si));
+ ZeroMemory(&pi, sizeof(pi));
+
+ si.cb = sizeof(si);
+
+ /* CreateProcess needs a modifiable cmdline: make a copy */
+ cmdline_dup = _wcsdup(cmdline);
+ if (cmdline_dup && CreateProcessW(NULL, cmdline_dup, NULL, NULL, FALSE,
+ proc_flags, NULL, NULL, &si, &pi))
+ {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ if (!GetExitCodeProcess(pi.hProcess, &exit_code))
+ {
+ exit_code = GetLastError();
+ }
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ else
+ {
+ exit_code = GetLastError();
+ }
+
+ free(cmdline_dup);
+ return exit_code;
+}
+
+DWORD
+tap_set_adapter_name(
+ _In_ LPCGUID pguidAdapter,
+ _In_ LPCTSTR szName,
+ _In_ BOOL bSilent)
+{
+ DWORD dwResult;
+ int msg_flag = bSilent ? M_WARN : M_NONFATAL;
+ msg_flag |= M_ERRNO;
+
+ if (pguidAdapter == NULL || szName == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Get the device class GUID as string. */
+ LPOLESTR szDevClassNetId = NULL;
+ StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
+
+ /* Get the adapter GUID as string. */
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)pguidAdapter, &szAdapterId);
+
+ /* Render registry key path. */
+ TCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
+ _stprintf_s(
+ szRegKey, _countof(szRegKey),
+ szAdapterRegKeyPathTemplate,
+ szDevClassNetId,
+ szAdapterId);
+
+ /* Open network adapter registry key. */
+ HKEY hKey = NULL;
+ dwResult = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ szRegKey,
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(msg_flag, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
+ goto cleanup_szAdapterId;
+ }
+
+ LPTSTR szOldName = NULL;
+ dwResult = get_reg_string(hKey, TEXT("Name"), &szOldName);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult);
+ msg(msg_flag, "%s: Error reading adapter name", __FUNCTION__);
+ goto cleanup_hKey;
+ }
+
+ /* rename adapter via netsh call */
+ const TCHAR* szFmt = _T("netsh interface set interface name=\"%s\" newname=\"%s\"");
+ size_t ncmdline = _tcslen(szFmt) + _tcslen(szOldName) + _tcslen(szName) + 1;
+ WCHAR* szCmdLine = malloc(ncmdline * sizeof(TCHAR));
+ _stprintf_s(szCmdLine, ncmdline, szFmt, szOldName, szName);
+
+ free(szOldName);
+
+ dwResult = ExecCommand(szCmdLine);
+ free(szCmdLine);
+
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult);
+ msg(msg_flag, "%s: Error renaming adapter", __FUNCTION__);
+ goto cleanup_hKey;
+ }
+
+cleanup_hKey:
+ RegCloseKey(hKey);
+cleanup_szAdapterId:
+ CoTaskMemFree(szAdapterId);
+ CoTaskMemFree(szDevClassNetId);
+ return dwResult;
+}
+
+
+DWORD
+tap_list_adapters(
+ _In_opt_ HWND hwndParent,
+ _In_opt_ LPCTSTR szzHwIDs,
+ _Out_ struct tap_adapter_node **ppAdapter)
+{
+ DWORD dwResult;
+
+ if (ppAdapter == NULL)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ /* Create a list of network devices. */
+ HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
+ &GUID_DEVCLASS_NET,
+ NULL,
+ hwndParent,
+ DIGCF_PRESENT,
+ NULL,
+ NULL,
+ NULL);
+ if (hDevInfoList == INVALID_HANDLE_VALUE)
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
+ return dwResult;
+ }
+
+ /* Retrieve information associated with a device information set. */
+ SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
+ if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
+ {
+ dwResult = GetLastError();
+ msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
+ goto cleanup_hDevInfoList;
+ }
+
+ /* Get the device class GUID as string. */
+ LPOLESTR szDevClassNetId = NULL;
+ StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
+
+ /* Iterate. */
+ *ppAdapter = NULL;
+ struct tap_adapter_node *pAdapterTail = NULL;
+ for (DWORD dwIndex = 0;; dwIndex++)
+ {
+ /* Get the device from the list. */
+ SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ if (!SetupDiEnumDeviceInfo(
+ hDevInfoList,
+ dwIndex,
+ &devinfo_data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ {
+ break;
+ }
+ else
+ {
+ /* Something is wrong with this device. Skip it. */
+ msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
+ continue;
+ }
+ }
+
+ /* Get device hardware ID(s). */
+ DWORD dwDataType = REG_NONE;
+ LPTSTR szzDeviceHardwareIDs = NULL;
+ dwResult = get_device_reg_property(
+ hDevInfoList,
+ &devinfo_data,
+ SPDRP_HARDWAREID,
+ &dwDataType,
+ (LPVOID)&szzDeviceHardwareIDs);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ /* Something is wrong with this device. Skip it. */
+ continue;
+ }
+
+ /* Check that hardware ID is REG_SZ/REG_MULTI_SZ, and optionally if it matches ours. */
+ if (dwDataType == REG_SZ)
+ {
+ if (szzHwIDs && !_tcszistr(szzHwIDs, szzDeviceHardwareIDs))
+ {
+ /* This is not our device. Skip it. */
+ goto cleanup_szzDeviceHardwareIDs;
+ }
+ }
+ else if (dwDataType == REG_MULTI_SZ)
+ {
+ if (szzHwIDs)
+ {
+ for (LPTSTR s = szzDeviceHardwareIDs;; s += _tcslen(s) + 1)
+ {
+ if (s[0] == 0)
+ {
+ /* This is not our device. Skip it. */
+ goto cleanup_szzDeviceHardwareIDs;
+ }
+ else if (_tcszistr(szzHwIDs, s))
+ {
+ /* This is our device. */
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Unexpected hardware ID format. Skip device. */
+ goto cleanup_szzDeviceHardwareIDs;
+ }
+
+ /* Get adapter GUID. */
+ GUID guidAdapter;
+ dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ /* Something is wrong with this device. Skip it. */
+ goto cleanup_szzDeviceHardwareIDs;
+ }
+
+ /* Get the adapter GUID as string. */
+ LPOLESTR szAdapterId = NULL;
+ StringFromIID((REFIID)&guidAdapter, &szAdapterId);
+
+ /* Render registry key path. */
+ TCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
+ _stprintf_s(
+ szRegKey, _countof(szRegKey),
+ szAdapterRegKeyPathTemplate,
+ szDevClassNetId,
+ szAdapterId);
+
+ /* Open network adapter registry key. */
+ HKEY hKey = NULL;
+ dwResult = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ szRegKey,
+ 0,
+ KEY_READ,
+ &hKey);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
+ msg(M_WARN | M_ERRNO, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
+ goto cleanup_szAdapterId;
+ }
+
+ /* Read adapter name. */
+ LPTSTR szName = NULL;
+ dwResult = get_reg_string(
+ hKey,
+ TEXT("Name"),
+ &szName);
+ if (dwResult != ERROR_SUCCESS)
+ {
+ SetLastError(dwResult);
+ msg(M_WARN | M_ERRNO, "%s: Cannot determine %" PRIsLPOLESTR " adapter name", __FUNCTION__, szAdapterId);
+ goto cleanup_hKey;
+ }
+
+ /* Append to the list. */
+ size_t hwid_size = (_tcszlen(szzDeviceHardwareIDs) + 1) * sizeof(TCHAR);
+ size_t name_size = (_tcslen(szName) + 1) * sizeof(TCHAR);
+ struct tap_adapter_node *node = (struct tap_adapter_node *)malloc(sizeof(struct tap_adapter_node) + hwid_size + name_size);
+ if (node == NULL)
+ {
+ msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct tap_adapter_node) + hwid_size + name_size);
+ dwResult = ERROR_OUTOFMEMORY; goto cleanup_szName;
+ }
+
+ memcpy(&node->guid, &guidAdapter, sizeof(GUID));
+ node->szzHardwareIDs = (LPTSTR)(node + 1);
+ memcpy(node->szzHardwareIDs, szzDeviceHardwareIDs, hwid_size);
+ node->szName = (LPTSTR)((LPBYTE)node->szzHardwareIDs + hwid_size);
+ memcpy(node->szName, szName, name_size);
+ node->pNext = NULL;
+ if (pAdapterTail)
+ {
+ pAdapterTail->pNext = node;
+ pAdapterTail = node;
+ }
+ else
+ {
+ *ppAdapter = pAdapterTail = node;
+ }
+
+cleanup_szName:
+ free(szName);
+cleanup_hKey:
+ RegCloseKey(hKey);
+cleanup_szAdapterId:
+ CoTaskMemFree(szAdapterId);
+cleanup_szzDeviceHardwareIDs:
+ free(szzDeviceHardwareIDs);
+ }
+
+ dwResult = ERROR_SUCCESS;
+
+ CoTaskMemFree(szDevClassNetId);
+cleanup_hDevInfoList:
+ SetupDiDestroyDeviceInfoList(hDevInfoList);
+ return dwResult;
+}
+
+
+void
+tap_free_adapter_list(
+ _In_ struct tap_adapter_node *pAdapterList)
+{
+ /* Iterate over all nodes of the list. */
+ while (pAdapterList)
+ {
+ struct tap_adapter_node *node = pAdapterList;
+ pAdapterList = pAdapterList->pNext;
+
+ /* Free the adapter node. */
+ free(node);
+ }
+}
diff --git a/src/tapctl/tap.h b/src/tapctl/tap.h
new file mode 100644
index 0000000..847040c
--- /dev/null
+++ b/src/tapctl/tap.h
@@ -0,0 +1,181 @@
+/*
+ * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ * https://community.openvpn.net/openvpn/wiki/Tapctl
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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 TAP_H
+#define TAP_H
+
+#include <windows.h>
+#include "basic.h"
+
+
+/**
+ * Creates a TUN/TAP adapter.
+ *
+ * @param hwndParent A handle to the top-level window to use for any user adapter that is
+ * related to non-device-specific actions (such as a select-device dialog
+ * box that uses the global class driver list). This handle is optional
+ * and can be NULL. If a specific top-level window is not required, set
+ * hwndParent to NULL.
+ *
+ * @param szDeviceDescription A pointer to a NULL-terminated string that supplies the text
+ * description of the device. This pointer is optional and can be NULL.
+ *
+ * @param szHwId A pointer to a NULL-terminated string that supplies the hardware id
+ * of the device (e.g. "root\\tap0901", "Wintun").
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @param pguidAdapter A pointer to GUID that receives network adapter ID.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+DWORD
+tap_create_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_opt_ LPCTSTR szDeviceDescription,
+ _In_ LPCTSTR szHwId,
+ _Inout_ LPBOOL pbRebootRequired,
+ _Out_ LPGUID pguidAdapter);
+
+
+/**
+ * Deletes an adapter.
+ *
+ * @param hwndParent A handle to the top-level window to use for any user adapter that is
+ * related to non-device-specific actions (such as a select-device dialog
+ * box that uses the global class driver list). This handle is optional
+ * and can be NULL. If a specific top-level window is not required, set
+ * hwndParent to NULL.
+ *
+ * @param pguidAdapter A pointer to GUID that contains network adapter ID.
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+DWORD
+tap_delete_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_ LPCGUID pguidAdapter,
+ _Inout_ LPBOOL pbRebootRequired);
+
+
+/**
+ * Enables or disables an adapter.
+ *
+ * @param hwndParent A handle to the top-level window to use for any user adapter that is
+ * related to non-device-specific actions (such as a select-device dialog
+ * box that uses the global class driver list). This handle is optional
+ * and can be NULL. If a specific top-level window is not required, set
+ * hwndParent to NULL.
+ *
+ * @param pguidAdapter A pointer to GUID that contains network adapter ID.
+ *
+ * @param bEnable TRUE to enable; FALSE to disable
+ *
+ * @param pbRebootRequired A pointer to a BOOL flag. If the device requires a system restart,
+ * this flag is set to TRUE. Otherwise, the flag is left unmodified. This
+ * allows the flag to be globally initialized to FALSE and reused for multiple
+ * adapter manipulations.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+DWORD
+tap_enable_adapter(
+ _In_opt_ HWND hwndParent,
+ _In_ LPCGUID pguidAdapter,
+ _In_ BOOL bEnable,
+ _Inout_ LPBOOL pbRebootRequired);
+
+
+/**
+ * Sets adapter name.
+ *
+ * @param pguidAdapter A pointer to GUID that contains network adapter ID.
+ *
+ * @param szName New adapter name - must be unique
+ *
+ * @param bSilent If true, MSI installer won't display message box and
+ * only print error to log.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ **/
+DWORD
+tap_set_adapter_name(
+ _In_ LPCGUID pguidAdapter,
+ _In_ LPCTSTR szName,
+ _In_ BOOL bSilent);
+
+
+/**
+ * Network adapter list node
+ */
+struct tap_adapter_node
+{
+ GUID guid; /** Adapter GUID */
+ LPTSTR szzHardwareIDs; /** Device hardware ID(s) */
+ LPTSTR szName; /** Adapter name */
+
+ struct tap_adapter_node *pNext; /** Pointer to next adapter */
+};
+
+
+/**
+ * Creates a list of existing network adapters.
+ *
+ * @param hwndParent A handle to the top-level window to use for any user adapter that is
+ * related to non-device-specific actions (such as a select-device dialog
+ * box that uses the global class driver list). This handle is optional
+ * and can be NULL. If a specific top-level window is not required, set
+ * hwndParent to NULL.
+ *
+ * @param szzHwIDs A string of strings that supplies the list of hardware IDs of the device.
+ * This pointer is optional and can be NULL. When NULL, all network adapters
+ * found are added to the list.
+ *
+ * @param ppAdapterList A pointer to the list to receive pointer to the first adapter in
+ * the list. After the list is no longer required, free it using
+ * tap_free_adapter_list().
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise
+ */
+DWORD
+tap_list_adapters(
+ _In_opt_ HWND hwndParent,
+ _In_opt_ LPCTSTR szzHwIDs,
+ _Out_ struct tap_adapter_node **ppAdapterList);
+
+
+/**
+ * Frees a list of network adapters.
+ *
+ * @param pAdapterList A pointer to the first adapter in the list to free.
+ */
+void
+tap_free_adapter_list(
+ _In_ struct tap_adapter_node *pAdapterList);
+
+#endif /* ifndef TAP_H */
diff --git a/src/tapctl/tapctl.exe.manifest b/src/tapctl/tapctl.exe.manifest
new file mode 100644
index 0000000..1eb5ea8
--- /dev/null
+++ b/src/tapctl/tapctl.exe.manifest
@@ -0,0 +1,10 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/src/tapctl/tapctl.props b/src/tapctl/tapctl.props
new file mode 100644
index 0000000..0257b9f
--- /dev/null
+++ b/src/tapctl/tapctl.props
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets" />
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <PreprocessorDefinitions>_CONSOLE;_WIN32_WINNT=_WIN32_WINNT_VISTA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project> \ No newline at end of file
diff --git a/src/tapctl/tapctl.vcxproj b/src/tapctl/tapctl.vcxproj
new file mode 100644
index 0000000..ad96f02
--- /dev/null
+++ b/src/tapctl/tapctl.vcxproj
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <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|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</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">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{A06436E7-D576-490D-8BA0-0751D920334A}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>tapctl</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Debug.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\compat\Release.props" />
+ <Import Project="tapctl.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <ItemGroup>
+ <ClCompile Include="error.c" />
+ <ClCompile Include="tap.c" />
+ <ClCompile Include="main.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="basic.h" />
+ <ClInclude Include="error.h" />
+ <ClInclude Include="tap.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="tapctl_resources.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\build\msvc\msvc-generate\msvc-generate.vcxproj">
+ <Project>{8598c2c8-34c4-47a1-99b0-7c295a890615}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Manifest Include="tapctl.exe.manifest" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/src/tapctl/tapctl.vcxproj.filters b/src/tapctl/tapctl.vcxproj.filters
new file mode 100644
index 0000000..c7f71e9
--- /dev/null
+++ b/src/tapctl/tapctl.vcxproj.filters
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="tap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="error.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="tap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="error.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="basic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="tapctl_resources.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Manifest Include="tapctl.exe.manifest">
+ <Filter>Resource Files</Filter>
+ </Manifest>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/tapctl/tapctl_resources.rc b/src/tapctl/tapctl_resources.rc
new file mode 100644
index 0000000..a195396
--- /dev/null
+++ b/src/tapctl/tapctl_resources.rc
@@ -0,0 +1,64 @@
+/*
+ * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
+ *
+ * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
+ *
+ * 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>
+#else
+#include <config-msvc-version.h>
+#endif
+#include <winresrc.h>
+
+#pragma code_page(65001) /* UTF8 */
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION OPENVPN_VERSION_RESOURCE
+ PRODUCTVERSION OPENVPN_VERSION_RESOURCE
+ FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PATCHED | VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "The OpenVPN Project"
+ VALUE "FileDescription", "Utility to manipulate TUN/TAP adapters on Windows"
+ VALUE "FileVersion", PACKAGE_VERSION ".0"
+ VALUE "InternalName", "OpenVPN"
+ VALUE "LegalCopyright", "Copyright © The OpenVPN Project"
+ VALUE "OriginalFilename", "tapctl.exe"
+ VALUE "ProductName", "OpenVPN"
+ VALUE "ProductVersion", PACKAGE_VERSION ".0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+1 RT_MANIFEST "tapctl.exe.manifest"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2087dd0..bb23a9c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,7 +5,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -14,9 +14,9 @@ MAINTAINERCLEANFILES = \
SUBDIRS = unit_tests
-test_scripts = t_client.sh
-if ENABLE_CRYPTO
-test_scripts += t_lpback.sh t_cltsrv.sh
+test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh
+if HAVE_SITNL
+test_scripts += t_net.sh
endif
TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)"
diff --git a/tests/Makefile.in b/tests/Makefile.in
index 97ac62a..7d0b927 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@
# packet encryption, packet authentication, and
# packet compression.
#
-# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
# Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
#
@@ -100,7 +100,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-@ENABLE_CRYPTO_TRUE@am__append_1 = t_lpback.sh t_cltsrv.sh
+@HAVE_SITNL_TRUE@am__append_1 = t_net.sh
subdir = tests
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
@@ -120,7 +120,7 @@ CONFIG_HEADER = $(top_builddir)/config.h \
CONFIG_CLEAN_FILES = t_client.sh
CONFIG_CLEAN_VPATH_FILES =
am__dist_noinst_SCRIPTS_DIST = t_client.sh t_lpback.sh t_cltsrv.sh \
- t_cltsrv-down.sh update_t_client_ips.sh
+ t_net.sh t_cltsrv-down.sh update_t_client_ips.sh
SCRIPTS = $(dist_noinst_SCRIPTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -239,7 +239,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -253,6 +254,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -280,7 +282,6 @@ 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@
@@ -331,6 +332,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -394,6 +397,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -409,7 +413,7 @@ MAINTAINERCLEANFILES = \
$(srcdir)/Makefile.in
SUBDIRS = unit_tests
-test_scripts = t_client.sh $(am__append_1)
+test_scripts = t_client.sh t_lpback.sh t_cltsrv.sh $(am__append_1)
TESTS_ENVIRONMENT = top_srcdir="$(top_srcdir)"
TESTS = $(test_scripts)
dist_noinst_SCRIPTS = \
diff --git a/tests/t_client.sh b/tests/t_client.sh
index b51813e..9024a06 100755
--- a/tests/t_client.sh
+++ b/tests/t_client.sh
@@ -298,6 +298,7 @@ do
echo -e "OK.\n"
else
echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
+ SUMMARY_FAIL="$SUMMARY_FAIL $SUF"
exit_code=31
continue
fi
@@ -417,7 +418,7 @@ done
if [ -z "$SUMMARY_OK" ] ; then SUMMARY_OK=" none"; fi
if [ -z "$SUMMARY_FAIL" ] ; then SUMMARY_FAIL=" none"; fi
-echo "Test sets succeded:$SUMMARY_OK."
+echo "Test sets succeeded:$SUMMARY_OK."
echo "Test sets failed:$SUMMARY_FAIL."
# remove trap handler
diff --git a/tests/t_client.sh.in b/tests/t_client.sh.in
index b92cb65..294546b 100755
--- a/tests/t_client.sh.in
+++ b/tests/t_client.sh.in
@@ -298,6 +298,7 @@ do
echo -e "OK.\n"
else
echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
+ SUMMARY_FAIL="$SUMMARY_FAIL $SUF"
exit_code=31
continue
fi
@@ -417,7 +418,7 @@ done
if [ -z "$SUMMARY_OK" ] ; then SUMMARY_OK=" none"; fi
if [ -z "$SUMMARY_FAIL" ] ; then SUMMARY_FAIL=" none"; fi
-echo "Test sets succeded:$SUMMARY_OK."
+echo "Test sets succeeded:$SUMMARY_OK."
echo "Test sets failed:$SUMMARY_FAIL."
# remove trap handler
diff --git a/tests/t_lpback.sh b/tests/t_lpback.sh
index bb8a1d5..6206899 100755
--- a/tests/t_lpback.sh
+++ b/tests/t_lpback.sh
@@ -21,8 +21,8 @@
set -eu
top_builddir="${top_builddir:-..}"
-trap "rm -f key.$$ log.$$ ; trap 0 ; exit 77" 1 2 15
-trap "rm -f key.$$ log.$$ ; exit 1" 0 3
+trap "rm -f key.$$ tc-server-key.$$ tc-client-key.$$ log.$$ ; trap 0 ; exit 77" 1 2 15
+trap "rm -f key.$$ tc-server-key.$$ tc-client-key.$$ log.$$ ; exit 1" 0 3
# Get list of supported ciphers from openvpn --show-ciphers output
CIPHERS=$(${top_builddir}/src/openvpn/openvpn --show-ciphers | \
@@ -38,13 +38,13 @@ CIPHERS=$(echo "$CIPHERS" | egrep -v '^(DES-EDE3-CFB1|DES-CFB1|RC5-)' )
# Also test cipher 'none'
CIPHERS=${CIPHERS}$(printf "\nnone")
-"${top_builddir}/src/openvpn/openvpn" --genkey --secret key.$$
+"${top_builddir}/src/openvpn/openvpn" --genkey secret key.$$
set +e
e=0
for cipher in ${CIPHERS}
do
- echo -n "Testing cipher ${cipher}... "
+ printf "Testing cipher ${cipher}... "
( "${top_builddir}/src/openvpn/openvpn" --test-crypto --secret key.$$ --cipher ${cipher} ) >log.$$ 2>&1
if [ $? != 0 ] ; then
echo "FAILED"
@@ -55,6 +55,47 @@ do
fi
done
-rm key.$$ log.$$
+printf "Testing tls-crypt-v2 server key generation... "
+"${top_builddir}/src/openvpn/openvpn" \
+ --genkey tls-crypt-v2-server tc-server-key.$$ >log.$$ 2>&1
+if [ $? != 0 ] ; then
+ echo "FAILED"
+ cat log.$$
+ e=1
+else
+ echo "OK"
+fi
+
+printf "Testing tls-crypt-v2 key generation (no metadata)... "
+"${top_builddir}/src/openvpn/openvpn" --tls-crypt-v2 tc-server-key.$$ \
+ --genkey tls-crypt-v2-client tc-client-key.$$ >log.$$ 2>&1
+if [ $? != 0 ] ; then
+ echo "FAILED"
+ cat log.$$
+ e=1
+else
+ echo "OK"
+fi
+
+# Generate max-length base64 metadata ('A' is 0b000000 in base64)
+METADATA=""
+i=0
+while [ $i -lt 732 ]; do
+ METADATA="${METADATA}A"
+ i=$(expr $i + 1)
+done
+printf "Testing tls-crypt-v2 key generation (max length metadata)... "
+"${top_builddir}/src/openvpn/openvpn" --tls-crypt-v2 tc-server-key.$$ \
+ --genkey tls-crypt-v2-client tc-client-key.$$ "${METADATA}" \
+ >log.$$ 2>&1
+if [ $? != 0 ] ; then
+ echo "FAILED"
+ cat log.$$
+ e=1
+else
+ echo "OK"
+fi
+
+rm key.$$ tc-server-key.$$ tc-client-key.$$ log.$$
trap 0
exit $e
diff --git a/tests/t_net.sh b/tests/t_net.sh
new file mode 100755
index 0000000..f9dba40
--- /dev/null
+++ b/tests/t_net.sh
@@ -0,0 +1,171 @@
+#!/usr/bin/env bash
+
+IFACE="ovpn-dummy0"
+UNIT_TEST="./unit_tests/openvpn/networking_testdriver"
+MAX_TEST=${1:-7}
+
+srcdir="${srcdir:-.}"
+top_builddir="${top_builddir:-..}"
+openvpn="${top_builddir}/src/openvpn/openvpn"
+
+
+# bail out right away on non-linux. NetLink (the object of this test) is only
+# used on Linux, therefore testing other platform is not needed.
+#
+# Note: statements in the rest of the script may not even pass syntax check on
+# solaris/bsd. It uses /bin/bash
+if [ "$(uname -s)" != "Linux" ]; then
+ echo "$0: this test runs only on Linux. SKIPPING TEST."
+ exit 77
+fi
+
+# Commands used to retrieve the network state.
+# State is retrieved after running sitnl and after running
+# iproute commands. The two are then compared and expected to be equal.
+typeset -a GET_STATE
+GET_STATE[0]="ip link show dev $IFACE | sed 's/^[0-9]\+: //'"
+GET_STATE[1]="ip addr show dev $IFACE | sed 's/^[0-9]\+: //'"
+GET_STATE[2]="ip route show dev $IFACE"
+GET_STATE[3]="ip -6 route show dev $IFACE"
+
+LAST_STATE=$((${#GET_STATE[@]} - 1))
+
+reload_dummy()
+{
+ $RUN_SUDO ip link del $IFACE
+ $RUN_SUDO ip link add $IFACE address 00:11:22:33:44:55 type dummy
+ $RUN_SUDO ip link set dev $IFACE state up
+
+ if [ $? -ne 0 ]; then
+ echo "can't create interface $IFACE"
+ exit 1
+ fi
+}
+
+run_test()
+{
+ # run all test cases from 0 to $1 in sequence
+ CMD=
+ for k in $(seq 0 $1); do
+ # the unit-test prints to stdout the iproute command corresponding
+ # to the sitnl operation being executed.
+ # Format is "CMD: <commandhere>"
+ OUT=$($RUN_SUDO $UNIT_TEST $k $IFACE)
+ # ensure unit test worked properly
+ if [ $? -ne 0 ]; then
+ echo "unit-test $k errored out:"
+ echo "$OUT"
+ exit 1
+ fi
+
+ NEW=$(echo "$OUT" | sed -n 's/CMD: //p')
+ CMD="$CMD $RUN_SUDO $NEW ;"
+ done
+
+ # collect state for later comparison
+ for k in $(seq 0 $LAST_STATE); do
+ STATE_TEST[$k]="$(eval ${GET_STATE[$k]})"
+ done
+}
+
+
+## execution starts here
+
+# t_client.rc required only for RUN_SUDO definition
+if [ -r "${top_builddir}"/t_client.rc ]; then
+ . "${top_builddir}"/t_client.rc
+elif [ -r "${srcdir}"/t_client.rc ]; then
+ . "${srcdir}"/t_client.rc
+fi
+
+if [ ! -x "$openvpn" ]; then
+ echo "no (executable) openvpn binary in current build tree. FAIL." >&2
+ exit 1
+fi
+
+if [ ! -x "$UNIT_TEST" ]; then
+ echo "no test_networking driver available. SKIPPING TEST." >&2
+ exit 77
+fi
+
+
+# Ensure PREFER_KSU is in a known state
+PREFER_KSU="${PREFER_KSU:-0}"
+
+# make sure we have permissions to run the networking unit-test
+ID=`id`
+if expr "$ID" : "uid=0" >/dev/null
+then :
+else
+ if [ "${PREFER_KSU}" -eq 1 ];
+ then
+ # Check if we have a valid kerberos ticket
+ klist -l 1>/dev/null 2>/dev/null
+ if [ $? -ne 0 ];
+ then
+ # No kerberos ticket found, skip ksu and fallback to RUN_SUDO
+ PREFER_KSU=0
+ echo "$0: No Kerberos ticket available. Will not use ksu."
+ else
+ RUN_SUDO="ksu -q -e"
+ fi
+ fi
+
+ if [ -z "$RUN_SUDO" ]
+ then
+ echo "$0: no RUN_SUDO=... in t_client.rc or environment, defaulting to 'sudo'." >&2
+ echo " if that does not work, set RUN_SUDO= correctly for your system." >&2
+ RUN_SUDO="sudo"
+ fi
+
+ # check that we can run the unit-test binary with sudo
+ if $RUN_SUDO $UNIT_TEST test
+ then
+ echo "$0: $RUN_SUDO $UNIT_TEST succeeded, good."
+ else
+ echo "$0: $RUN_SUDO $UNIT_TEST failed, cannot go on. SKIP." >&2
+ exit 77
+ fi
+fi
+
+for i in $(seq 0 $MAX_TEST); do
+ # reload dummy module to cleanup state
+ reload_dummy
+ typeset -a STATE_TEST
+ run_test $i
+
+ # reload dummy module to cleanup state before running iproute commands
+ reload_dummy
+
+ # CMD has been set by the unit test
+ eval $CMD
+ if [ $? -ne 0 ]; then
+ echo "error while executing:"
+ echo "$CMD"
+ exit 1
+ fi
+
+ # collect state after running manual ip command
+ for k in $(seq 0 $LAST_STATE); do
+ STATE_IP[$k]="$(eval ${GET_STATE[$k]})"
+ done
+
+ # ensure states after running unit test matches the one after running
+ # manual iproute commands
+ for j in $(seq 0 $LAST_STATE); do
+ if [ "${STATE_TEST[$j]}" != "${STATE_IP[$j]}" ]; then
+ echo "state $j mismatching after '$CMD'"
+ echo "after unit-test:"
+ echo "${STATE_TEST[$j]}"
+ echo "after iproute command:"
+ echo "${STATE_IP[$j]}"
+ exit 1
+ fi
+ done
+ echo "Test $i: OK"
+done
+
+# remove interface for good
+$RUN_SUDO $openvpn --dev $IFACE --dev-type tun --rmtun >/dev/null
+
+exit 0
diff --git a/tests/unit_tests/Makefile.am b/tests/unit_tests/Makefile.am
index 31d37b8..f27cd90 100644
--- a/tests/unit_tests/Makefile.am
+++ b/tests/unit_tests/Makefile.am
@@ -1,5 +1,8 @@
AUTOMAKE_OPTIONS = foreign
-if CMOCKA_INITIALIZED
+if ENABLE_UNITTESTS
SUBDIRS = example_test openvpn plugins
+if OPENSSL_ENGINE
+SUBDIRS += engine-key
+endif
endif
diff --git a/tests/unit_tests/Makefile.in b/tests/unit_tests/Makefile.in
index 1ae5b1c..964ff1a 100644
--- a/tests/unit_tests/Makefile.in
+++ b/tests/unit_tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -87,6 +87,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
+@ENABLE_UNITTESTS_TRUE@@OPENSSL_ENGINE_TRUE@am__append_1 = engine-key
subdir = tests/unit_tests
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
@@ -158,7 +159,7 @@ am__define_uniq_tagged_files = \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-DIST_SUBDIRS = example_test openvpn plugins
+DIST_SUBDIRS = example_test openvpn plugins engine-key
am__DIST_COMMON = $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
am__relativize = \
@@ -198,7 +199,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -212,6 +214,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -239,7 +242,6 @@ 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@
@@ -290,6 +292,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -353,6 +357,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -365,7 +370,8 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = foreign
-@CMOCKA_INITIALIZED_TRUE@SUBDIRS = example_test openvpn plugins
+@ENABLE_UNITTESTS_TRUE@SUBDIRS = example_test openvpn plugins \
+@ENABLE_UNITTESTS_TRUE@ $(am__append_1)
all: all-recursive
.SUFFIXES:
diff --git a/tests/unit_tests/engine-key/Makefile.am b/tests/unit_tests/engine-key/Makefile.am
new file mode 100644
index 0000000..2462225
--- /dev/null
+++ b/tests/unit_tests/engine-key/Makefile.am
@@ -0,0 +1,29 @@
+AUTOMAKE_OPTIONS = foreign
+
+check_LTLIBRARIES = libtestengine.la
+conffiles = openssl.cnf
+EXTRA_DIST = \
+ openssl.cnf.in \
+ check_engine_keys.sh
+
+TESTS_ENVIRONMENT = srcdir="$(abs_srcdir)"; \
+ builddir="$(abs_builddir)"; \
+ top_builddir="$(top_builddir)"; \
+ top_srcdir="$(top_srcdir)"; \
+ export srcdir builddir top_builddir top_srcdir;
+
+TESTS = check_engine_keys.sh
+check_engine_keys.sh: $(conffiles)
+
+CLEANFILES = \
+ client.key \
+ passwd \
+ log.txt \
+ $(conffiles)
+
+openssl.cnf: $(srcdir)/openssl.cnf.in
+ sed "s|ABSBUILDDIR|$(abs_builddir)|" < $(srcdir)/openssl.cnf.in > $@
+
+libtestengine_la_SOURCES = libtestengine.c
+libtestengine_la_LDFLAGS = @TEST_LDFLAGS@ -rpath /lib -shrext .so
+libtestengine_la_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
diff --git a/tests/unit_tests/engine-key/Makefile.in b/tests/unit_tests/engine-key/Makefile.in
new file mode 100644
index 0000000..8cd4552
--- /dev/null
+++ b/tests/unit_tests/engine-key/Makefile.in
@@ -0,0 +1,815 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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@
+subdir = tests/unit_tests/engine-key
+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 =
+libtestengine_la_LIBADD =
+am_libtestengine_la_OBJECTS = libtestengine_la-libtestengine.lo
+libtestengine_la_OBJECTS = $(am_libtestengine_la_OBJECTS)
+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 =
+libtestengine_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libtestengine_la_CFLAGS) $(CFLAGS) \
+ $(libtestengine_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+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)/libtestengine_la-libtestengine.Plo
+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 = $(libtestengine_la_SOURCES)
+DIST_SOURCES = $(libtestengine_la_SOURCES)
+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__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__DIST_COMMON = $(srcdir)/Makefile.in $(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@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+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@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
+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@
+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@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
+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@
+runstatedir = @runstatedir@
+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@
+AUTOMAKE_OPTIONS = foreign
+check_LTLIBRARIES = libtestengine.la
+conffiles = openssl.cnf
+EXTRA_DIST = \
+ openssl.cnf.in \
+ check_engine_keys.sh
+
+TESTS_ENVIRONMENT = srcdir="$(abs_srcdir)"; \
+ builddir="$(abs_builddir)"; \
+ top_builddir="$(top_builddir)"; \
+ top_srcdir="$(top_srcdir)"; \
+ export srcdir builddir top_builddir top_srcdir;
+
+TESTS = check_engine_keys.sh
+CLEANFILES = \
+ client.key \
+ passwd \
+ log.txt \
+ $(conffiles)
+
+libtestengine_la_SOURCES = libtestengine.c
+libtestengine_la_LDFLAGS = @TEST_LDFLAGS@ -rpath /lib -shrext .so
+libtestengine_la_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(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 tests/unit_tests/engine-key/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/unit_tests/engine-key/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_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):
+
+clean-checkLTLIBRARIES:
+ -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES)
+ @list='$(check_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libtestengine.la: $(libtestengine_la_OBJECTS) $(libtestengine_la_DEPENDENCIES) $(EXTRA_libtestengine_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libtestengine_la_LINK) $(libtestengine_la_OBJECTS) $(libtestengine_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libtestengine_la-libtestengine.Plo@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 $@ $<
+
+libtestengine_la-libtestengine.lo: libtestengine.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtestengine_la_CFLAGS) $(CFLAGS) -MT libtestengine_la-libtestengine.lo -MD -MP -MF $(DEPDIR)/libtestengine_la-libtestengine.Tpo -c -o libtestengine_la-libtestengine.lo `test -f 'libtestengine.c' || echo '$(srcdir)/'`libtestengine.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtestengine_la-libtestengine.Tpo $(DEPDIR)/libtestengine_la-libtestengine.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtestengine.c' object='libtestengine_la-libtestengine.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtestengine_la_CFLAGS) $(CFLAGS) -c -o libtestengine_la-libtestengine.lo `test -f 'libtestengine.c' || echo '$(srcdir)/'`libtestengine.c
+
+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
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+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
+ $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+installdirs:
+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:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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."
+clean: clean-am
+
+clean-am: clean-checkLTLIBRARIES clean-generic clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libtestengine_la-libtestengine.Plo
+ -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-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)/libtestengine_la-libtestengine.Plo
+ -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:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-checkLTLIBRARIES clean-generic \
+ clean-libtool 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-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
+
+.PRECIOUS: Makefile
+
+check_engine_keys.sh: $(conffiles)
+
+openssl.cnf: $(srcdir)/openssl.cnf.in
+ sed "s|ABSBUILDDIR|$(abs_builddir)|" < $(srcdir)/openssl.cnf.in > $@
+
+# 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/tests/unit_tests/engine-key/check_engine_keys.sh b/tests/unit_tests/engine-key/check_engine_keys.sh
new file mode 100755
index 0000000..7e9a0e8
--- /dev/null
+++ b/tests/unit_tests/engine-key/check_engine_keys.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+OPENSSL_CONF="${builddir}/openssl.cnf"
+export OPENSSL_CONF
+
+password='AT3S4PASSWD'
+
+key="${builddir}/client.key"
+pwdfile="${builddir}/passwd"
+
+# create an engine key for us
+sed 's/PRIVATE KEY/TEST ENGINE KEY/' < ${top_srcdir}/sample/sample-keys/client.key > ${key}
+echo "$password" > $pwdfile
+
+# our version of grep to output log.txt on failure in case it's an openssl
+# error mismatch and the grep expression needs updating
+loggrep() {
+ egrep -q "$1" log.txt || { echo '---- begin log.txt ----'; cat log.txt; echo '--- end log.txt ---'; return 1; }
+}
+
+# note here we've induced a mismatch in the client key and the server
+# cert which openvpn should report and die. Check that it does. Note
+# also that this mismatch depends on openssl not openvpn, so it is
+# somewhat fragile
+${top_builddir}/src/openvpn/openvpn --cd ${top_srcdir}/sample --config sample-config-files/loopback-server --engine testengine --key ${key} --askpass $pwdfile > log.txt 2>&1
+
+# first off check we died because of a key mismatch. If this doesn't
+# pass, suspect openssl of returning different messages and update the
+# test accordingly
+loggrep '(X509_check_private_key:key values mismatch|func\(128\):reason\(116\))' log.txt || { echo "Key mismatch not detected"; exit 1; }
+
+# now look for the engine prints (these are under our control)
+loggrep 'ENGINE: engine_init called' || { echo "Engine initialization not detected"; exit 1; }
+loggrep 'ENGINE: engine_load_key called' || { echo "Key was not loaded from engine"; exit 1; }
+loggrep "ENGINE: engine_load_key got password ${password}" || { echo "Key password was not retrieved by the engine"; exit 1; }
+exit 0
diff --git a/tests/unit_tests/engine-key/libtestengine.c b/tests/unit_tests/engine-key/libtestengine.c
new file mode 100644
index 0000000..8bcfa92
--- /dev/null
+++ b/tests/unit_tests/engine-key/libtestengine.c
@@ -0,0 +1,116 @@
+#include <string.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+
+static char *engine_id = "testengine";
+static char *engine_name = "Engine for testing openvpn engine key support";
+
+static int is_initialized = 0;
+
+static int
+engine_init(ENGINE *e)
+{
+ is_initialized = 1;
+ fprintf(stderr, "ENGINE: engine_init called\n");
+ return 1;
+}
+
+static int
+engine_finish(ENGINE *e)
+{
+ fprintf(stderr, "ENGINE: engine_finsh called\n");
+ is_initialized = 0;
+ return 1;
+}
+
+static EVP_PKEY *
+engine_load_key(ENGINE *e, const char *key_id,
+ UI_METHOD *ui_method, void *cb_data)
+{
+ BIO *b;
+ EVP_PKEY *pkey;
+ PKCS8_PRIV_KEY_INFO *p8inf;
+ UI *ui;
+ char auth[256];
+
+ fprintf(stderr, "ENGINE: engine_load_key called\n");
+
+ if (!is_initialized)
+ {
+ fprintf(stderr, "Load Key called without correct initialization\n");
+ return NULL;
+ }
+ b = BIO_new_file(key_id, "r");
+ if (!b)
+ {
+ fprintf(stderr, "File %s does not exist or cannot be read\n", key_id);
+ return 0;
+ }
+ /* Basically read an EVP_PKEY private key file with different
+ * PEM guards --- we are a test engine */
+ p8inf = PEM_ASN1_read_bio((d2i_of_void *)d2i_PKCS8_PRIV_KEY_INFO,
+ "TEST ENGINE KEY", b,
+ NULL, NULL, NULL);
+ BIO_free(b);
+ if (!p8inf)
+ {
+ fprintf(stderr, "Failed to read engine private key\n");
+ return NULL;
+ }
+ pkey = EVP_PKCS82PKEY(p8inf);
+
+ /* now we have a private key, pretend it had a password
+ * this verifies the password makes it through openvpn OK */
+ ui = UI_new();
+
+ if (ui_method)
+ {
+ UI_set_method(ui, ui_method);
+ }
+
+ UI_add_user_data(ui, cb_data);
+
+ if (UI_add_input_string(ui, "enter test engine key",
+ UI_INPUT_FLAG_DEFAULT_PWD,
+ auth, 0, sizeof(auth)) == 0)
+ {
+ fprintf(stderr, "UI_add_input_string failed\n");
+ goto out;
+ }
+
+ if (UI_process(ui))
+ {
+ fprintf(stderr, "UI_process failed\n");
+ goto out;
+ }
+
+ fprintf(stderr, "ENGINE: engine_load_key got password %s\n", auth);
+
+out:
+ UI_free(ui);
+
+ return pkey;
+}
+
+
+static int
+engine_bind_fn(ENGINE *e, const char *id)
+{
+ if (id && strcmp(id, engine_id) != 0)
+ {
+ return 0;
+ }
+ if (!ENGINE_set_id(e, engine_id)
+ || !ENGINE_set_name(e, engine_name)
+ || !ENGINE_set_init_function(e, engine_init)
+ || !ENGINE_set_finish_function(e, engine_finish)
+ || !ENGINE_set_load_privkey_function(e, engine_load_key))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN(engine_bind_fn)
diff --git a/tests/unit_tests/engine-key/openssl.cnf.in b/tests/unit_tests/engine-key/openssl.cnf.in
new file mode 100644
index 0000000..5eda9fa
--- /dev/null
+++ b/tests/unit_tests/engine-key/openssl.cnf.in
@@ -0,0 +1,12 @@
+HOME = .
+openssl_conf = openssl_init
+
+[req]
+[openssl_init]
+engines = engines_section
+
+[engines_section]
+testengine = testengine_section
+
+[testengine_section]
+dynamic_path = ABSBUILDDIR/.libs/libtestengine.so
diff --git a/tests/unit_tests/example_test/Makefile.in b/tests/unit_tests/example_test/Makefile.in
index 4c6e65a..b949642 100644
--- a/tests/unit_tests/example_test/Makefile.in
+++ b/tests/unit_tests/example_test/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -223,7 +223,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -237,6 +238,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -264,7 +266,6 @@ 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@
@@ -315,6 +316,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -378,6 +381,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/tests/unit_tests/example_test/test.c b/tests/unit_tests/example_test/test.c
index d48e5f5..bc3fdc1 100644
--- a/tests/unit_tests/example_test/test.c
+++ b/tests/unit_tests/example_test/test.c
@@ -7,7 +7,8 @@
#include <cmocka.h>
static int
-setup(void **state) {
+setup(void **state)
+{
int *answer = malloc(sizeof(int));
*answer = 42;
@@ -17,31 +18,36 @@ setup(void **state) {
}
static int
-teardown(void **state) {
+teardown(void **state)
+{
free(*state);
return 0;
}
static void
-null_test_success(void **state) {
+null_test_success(void **state)
+{
(void) state;
}
static void
-int_test_success(void **state) {
+int_test_success(void **state)
+{
int *answer = *state;
assert_int_equal(*answer, 42);
}
static void
-failing_test(void **state) {
+failing_test(void **state)
+{
/* This tests fails to test that make check fails */
assert_int_equal(0, 42);
}
int
-main(void) {
+main(void)
+{
const struct CMUnitTest tests[] = {
cmocka_unit_test(null_test_success),
cmocka_unit_test_setup_teardown(int_test_success, setup, teardown),
diff --git a/tests/unit_tests/example_test/test2.c b/tests/unit_tests/example_test/test2.c
index b5d4fa6..5a186d5 100644
--- a/tests/unit_tests/example_test/test2.c
+++ b/tests/unit_tests/example_test/test2.c
@@ -8,13 +8,15 @@
static void
-test_true(void **state) {
+test_true(void **state)
+{
(void) state;
}
int
-main(void) {
+main(void)
+{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_true),
};
diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am
index 55e29e4..f0880a6 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -1,26 +1,31 @@
AUTOMAKE_OPTIONS = foreign
-check_PROGRAMS=
+test_binaries=
if HAVE_LD_WRAP_SUPPORT
-check_PROGRAMS += argv_testdriver buffer_testdriver
+test_binaries += argv_testdriver buffer_testdriver
endif
-if ENABLE_CRYPTO
-check_PROGRAMS += packet_id_testdriver tls_crypt_testdriver
+test_binaries += crypto_testdriver packet_id_testdriver auth_token_testdriver ncp_testdriver
+if HAVE_LD_WRAP_SUPPORT
+test_binaries += tls_crypt_testdriver
endif
-TESTS = $(check_PROGRAMS)
+TESTS = $(test_binaries)
+check_PROGRAMS = $(test_binaries)
+
+if HAVE_SITNL
+check_PROGRAMS += networking_testdriver
+endif
openvpn_includedir = $(top_srcdir)/include
openvpn_srcdir = $(top_srcdir)/src/openvpn
compat_srcdir = $(top_srcdir)/src/compat
-argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) \
- $(OPTIONAL_CRYPTO_CFLAGS)
-argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line \
- $(OPTIONAL_CRYPTO_LIBS)
+argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
+argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line
argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
$(openvpn_srcdir)/platform.c \
$(openvpn_srcdir)/buffer.c \
$(openvpn_srcdir)/argv.c
@@ -28,26 +33,92 @@ argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \
buffer_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
buffer_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line
buffer_testdriver_SOURCES = test_buffer.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
+ $(openvpn_srcdir)/platform.c
+
+crypto_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+crypto_testdriver_LDFLAGS = @TEST_LDFLAGS@
+crypto_testdriver_SOURCES = test_crypto.c mock_msg.c mock_msg.h \
$(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
$(openvpn_srcdir)/platform.c
packet_id_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+packet_id_testdriver_LDFLAGS = @TEST_LDFLAGS@
+packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
+ $(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
+ $(openvpn_srcdir)/platform.c
+
+tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+ -Wl,--wrap=buffer_read_from_file \
+ -Wl,--wrap=buffer_write_file \
+ -Wl,--wrap=parse_line \
+ -Wl,--wrap=rand_bytes
+tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \
+ $(openvpn_srcdir)/argv.c \
+ $(openvpn_srcdir)/base64.c \
+ $(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
+ $(openvpn_srcdir)/env_set.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
+ $(openvpn_srcdir)/platform.c \
+ $(openvpn_srcdir)/run_command.c
+
+if HAVE_SITNL
+networking_testdriver_CFLAGS = @TEST_CFLAGS@ \
-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
$(OPTIONAL_CRYPTO_CFLAGS)
-packet_id_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+networking_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) \
$(OPTIONAL_CRYPTO_LIBS)
-packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \
+networking_testdriver_SOURCES = test_networking.c mock_msg.c \
+ $(openvpn_srcdir)/networking_sitnl.c \
$(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
$(openvpn_srcdir)/otime.c \
$(openvpn_srcdir)/packet_id.c \
$(openvpn_srcdir)/platform.c
+endif
-tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \
+auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \
-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
$(OPTIONAL_CRYPTO_CFLAGS)
-tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+auth_token_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
$(OPTIONAL_CRYPTO_LIBS)
-tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \
+
+auth_token_testdriver_SOURCES = test_auth_token.c mock_msg.c \
+ $(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
+ $(openvpn_srcdir)/platform.c \
+ $(openvpn_srcdir)/base64.c
+
+
+ncp_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
+ $(OPTIONAL_CRYPTO_CFLAGS)
+ncp_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+ $(OPTIONAL_CRYPTO_LIBS)
+
+ncp_testdriver_SOURCES = test_ncp.c mock_msg.c \
$(openvpn_srcdir)/buffer.c \
$(openvpn_srcdir)/crypto.c \
$(openvpn_srcdir)/crypto_mbedtls.c \
diff --git a/tests/unit_tests/openvpn/Makefile.in b/tests/unit_tests/openvpn/Makefile.in
index 330aa55..d051bda 100644
--- a/tests/unit_tests/openvpn/Makefile.in
+++ b/tests/unit_tests/openvpn/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -87,9 +87,11 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2)
@HAVE_LD_WRAP_SUPPORT_TRUE@am__append_1 = argv_testdriver buffer_testdriver
-@ENABLE_CRYPTO_TRUE@am__append_2 = packet_id_testdriver tls_crypt_testdriver
+@HAVE_LD_WRAP_SUPPORT_TRUE@am__append_2 = tls_crypt_testdriver
+TESTS = $(am__EXEEXT_3)
+check_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4)
+@HAVE_SITNL_TRUE@am__append_3 = networking_testdriver
subdir = tests/unit_tests/openvpn
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \
@@ -109,10 +111,15 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
@HAVE_LD_WRAP_SUPPORT_TRUE@am__EXEEXT_1 = argv_testdriver$(EXEEXT) \
@HAVE_LD_WRAP_SUPPORT_TRUE@ buffer_testdriver$(EXEEXT)
-@ENABLE_CRYPTO_TRUE@am__EXEEXT_2 = packet_id_testdriver$(EXEEXT) \
-@ENABLE_CRYPTO_TRUE@ tls_crypt_testdriver$(EXEEXT)
+@HAVE_LD_WRAP_SUPPORT_TRUE@am__EXEEXT_2 = \
+@HAVE_LD_WRAP_SUPPORT_TRUE@ tls_crypt_testdriver$(EXEEXT)
+am__EXEEXT_3 = $(am__EXEEXT_1) crypto_testdriver$(EXEEXT) \
+ packet_id_testdriver$(EXEEXT) auth_token_testdriver$(EXEEXT) \
+ ncp_testdriver$(EXEEXT) $(am__EXEEXT_2)
+@HAVE_SITNL_TRUE@am__EXEEXT_4 = networking_testdriver$(EXEEXT)
am_argv_testdriver_OBJECTS = argv_testdriver-test_argv.$(OBJEXT) \
argv_testdriver-mock_msg.$(OBJEXT) \
+ argv_testdriver-mock_get_random.$(OBJEXT) \
argv_testdriver-platform.$(OBJEXT) \
argv_testdriver-buffer.$(OBJEXT) \
argv_testdriver-argv.$(OBJEXT)
@@ -126,10 +133,27 @@ argv_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
$(argv_testdriver_CFLAGS) $(CFLAGS) $(argv_testdriver_LDFLAGS) \
$(LDFLAGS) -o $@
+am_auth_token_testdriver_OBJECTS = \
+ auth_token_testdriver-test_auth_token.$(OBJEXT) \
+ auth_token_testdriver-mock_msg.$(OBJEXT) \
+ auth_token_testdriver-buffer.$(OBJEXT) \
+ auth_token_testdriver-crypto.$(OBJEXT) \
+ auth_token_testdriver-crypto_mbedtls.$(OBJEXT) \
+ auth_token_testdriver-crypto_openssl.$(OBJEXT) \
+ auth_token_testdriver-otime.$(OBJEXT) \
+ auth_token_testdriver-packet_id.$(OBJEXT) \
+ auth_token_testdriver-platform.$(OBJEXT) \
+ auth_token_testdriver-base64.$(OBJEXT)
+auth_token_testdriver_OBJECTS = $(am_auth_token_testdriver_OBJECTS)
+auth_token_testdriver_LDADD = $(LDADD)
+auth_token_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(auth_token_testdriver_CFLAGS) $(CFLAGS) \
+ $(auth_token_testdriver_LDFLAGS) $(LDFLAGS) -o $@
am_buffer_testdriver_OBJECTS = \
buffer_testdriver-test_buffer.$(OBJEXT) \
buffer_testdriver-mock_msg.$(OBJEXT) \
- buffer_testdriver-buffer.$(OBJEXT) \
+ buffer_testdriver-mock_get_random.$(OBJEXT) \
buffer_testdriver-platform.$(OBJEXT)
buffer_testdriver_OBJECTS = $(am_buffer_testdriver_OBJECTS)
buffer_testdriver_LDADD = $(LDADD)
@@ -137,9 +161,63 @@ buffer_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
$(buffer_testdriver_CFLAGS) $(CFLAGS) \
$(buffer_testdriver_LDFLAGS) $(LDFLAGS) -o $@
+am_crypto_testdriver_OBJECTS = \
+ crypto_testdriver-test_crypto.$(OBJEXT) \
+ crypto_testdriver-mock_msg.$(OBJEXT) \
+ crypto_testdriver-buffer.$(OBJEXT) \
+ crypto_testdriver-crypto.$(OBJEXT) \
+ crypto_testdriver-crypto_mbedtls.$(OBJEXT) \
+ crypto_testdriver-crypto_openssl.$(OBJEXT) \
+ crypto_testdriver-otime.$(OBJEXT) \
+ crypto_testdriver-packet_id.$(OBJEXT) \
+ crypto_testdriver-platform.$(OBJEXT)
+crypto_testdriver_OBJECTS = $(am_crypto_testdriver_OBJECTS)
+crypto_testdriver_LDADD = $(LDADD)
+crypto_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(crypto_testdriver_CFLAGS) $(CFLAGS) \
+ $(crypto_testdriver_LDFLAGS) $(LDFLAGS) -o $@
+am_ncp_testdriver_OBJECTS = ncp_testdriver-test_ncp.$(OBJEXT) \
+ ncp_testdriver-mock_msg.$(OBJEXT) \
+ ncp_testdriver-buffer.$(OBJEXT) \
+ ncp_testdriver-crypto.$(OBJEXT) \
+ ncp_testdriver-crypto_mbedtls.$(OBJEXT) \
+ ncp_testdriver-crypto_openssl.$(OBJEXT) \
+ ncp_testdriver-otime.$(OBJEXT) \
+ ncp_testdriver-packet_id.$(OBJEXT) \
+ ncp_testdriver-platform.$(OBJEXT)
+ncp_testdriver_OBJECTS = $(am_ncp_testdriver_OBJECTS)
+ncp_testdriver_LDADD = $(LDADD)
+ncp_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(ncp_testdriver_CFLAGS) $(CFLAGS) $(ncp_testdriver_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__networking_testdriver_SOURCES_DIST = test_networking.c mock_msg.c \
+ $(openvpn_srcdir)/networking_sitnl.c \
+ $(openvpn_srcdir)/buffer.c $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c $(openvpn_srcdir)/platform.c
+@HAVE_SITNL_TRUE@am_networking_testdriver_OBJECTS = networking_testdriver-test_networking.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-mock_msg.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-networking_sitnl.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-buffer.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-crypto.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-crypto_mbedtls.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-crypto_openssl.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-otime.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-packet_id.$(OBJEXT) \
+@HAVE_SITNL_TRUE@ networking_testdriver-platform.$(OBJEXT)
+networking_testdriver_OBJECTS = $(am_networking_testdriver_OBJECTS)
+networking_testdriver_LDADD = $(LDADD)
+networking_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(networking_testdriver_CFLAGS) $(CFLAGS) \
+ $(networking_testdriver_LDFLAGS) $(LDFLAGS) -o $@
am_packet_id_testdriver_OBJECTS = \
packet_id_testdriver-test_packet_id.$(OBJEXT) \
packet_id_testdriver-mock_msg.$(OBJEXT) \
+ packet_id_testdriver-mock_get_random.$(OBJEXT) \
packet_id_testdriver-buffer.$(OBJEXT) \
packet_id_testdriver-otime.$(OBJEXT) \
packet_id_testdriver-packet_id.$(OBJEXT) \
@@ -153,13 +231,17 @@ packet_id_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
am_tls_crypt_testdriver_OBJECTS = \
tls_crypt_testdriver-test_tls_crypt.$(OBJEXT) \
tls_crypt_testdriver-mock_msg.$(OBJEXT) \
+ tls_crypt_testdriver-argv.$(OBJEXT) \
+ tls_crypt_testdriver-base64.$(OBJEXT) \
tls_crypt_testdriver-buffer.$(OBJEXT) \
tls_crypt_testdriver-crypto.$(OBJEXT) \
tls_crypt_testdriver-crypto_mbedtls.$(OBJEXT) \
tls_crypt_testdriver-crypto_openssl.$(OBJEXT) \
+ tls_crypt_testdriver-env_set.$(OBJEXT) \
tls_crypt_testdriver-otime.$(OBJEXT) \
tls_crypt_testdriver-packet_id.$(OBJEXT) \
- tls_crypt_testdriver-platform.$(OBJEXT)
+ tls_crypt_testdriver-platform.$(OBJEXT) \
+ tls_crypt_testdriver-run_command.$(OBJEXT)
tls_crypt_testdriver_OBJECTS = $(am_tls_crypt_testdriver_OBJECTS)
tls_crypt_testdriver_LDADD = $(LDADD)
tls_crypt_testdriver_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
@@ -183,27 +265,71 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/argv_testdriver-argv.Po \
./$(DEPDIR)/argv_testdriver-buffer.Po \
+ ./$(DEPDIR)/argv_testdriver-mock_get_random.Po \
./$(DEPDIR)/argv_testdriver-mock_msg.Po \
./$(DEPDIR)/argv_testdriver-platform.Po \
./$(DEPDIR)/argv_testdriver-test_argv.Po \
- ./$(DEPDIR)/buffer_testdriver-buffer.Po \
+ ./$(DEPDIR)/auth_token_testdriver-base64.Po \
+ ./$(DEPDIR)/auth_token_testdriver-buffer.Po \
+ ./$(DEPDIR)/auth_token_testdriver-crypto.Po \
+ ./$(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po \
+ ./$(DEPDIR)/auth_token_testdriver-crypto_openssl.Po \
+ ./$(DEPDIR)/auth_token_testdriver-mock_msg.Po \
+ ./$(DEPDIR)/auth_token_testdriver-otime.Po \
+ ./$(DEPDIR)/auth_token_testdriver-packet_id.Po \
+ ./$(DEPDIR)/auth_token_testdriver-platform.Po \
+ ./$(DEPDIR)/auth_token_testdriver-test_auth_token.Po \
+ ./$(DEPDIR)/buffer_testdriver-mock_get_random.Po \
./$(DEPDIR)/buffer_testdriver-mock_msg.Po \
./$(DEPDIR)/buffer_testdriver-platform.Po \
./$(DEPDIR)/buffer_testdriver-test_buffer.Po \
+ ./$(DEPDIR)/crypto_testdriver-buffer.Po \
+ ./$(DEPDIR)/crypto_testdriver-crypto.Po \
+ ./$(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po \
+ ./$(DEPDIR)/crypto_testdriver-crypto_openssl.Po \
+ ./$(DEPDIR)/crypto_testdriver-mock_msg.Po \
+ ./$(DEPDIR)/crypto_testdriver-otime.Po \
+ ./$(DEPDIR)/crypto_testdriver-packet_id.Po \
+ ./$(DEPDIR)/crypto_testdriver-platform.Po \
+ ./$(DEPDIR)/crypto_testdriver-test_crypto.Po \
+ ./$(DEPDIR)/ncp_testdriver-buffer.Po \
+ ./$(DEPDIR)/ncp_testdriver-crypto.Po \
+ ./$(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po \
+ ./$(DEPDIR)/ncp_testdriver-crypto_openssl.Po \
+ ./$(DEPDIR)/ncp_testdriver-mock_msg.Po \
+ ./$(DEPDIR)/ncp_testdriver-otime.Po \
+ ./$(DEPDIR)/ncp_testdriver-packet_id.Po \
+ ./$(DEPDIR)/ncp_testdriver-platform.Po \
+ ./$(DEPDIR)/ncp_testdriver-test_ncp.Po \
+ ./$(DEPDIR)/networking_testdriver-buffer.Po \
+ ./$(DEPDIR)/networking_testdriver-crypto.Po \
+ ./$(DEPDIR)/networking_testdriver-crypto_mbedtls.Po \
+ ./$(DEPDIR)/networking_testdriver-crypto_openssl.Po \
+ ./$(DEPDIR)/networking_testdriver-mock_msg.Po \
+ ./$(DEPDIR)/networking_testdriver-networking_sitnl.Po \
+ ./$(DEPDIR)/networking_testdriver-otime.Po \
+ ./$(DEPDIR)/networking_testdriver-packet_id.Po \
+ ./$(DEPDIR)/networking_testdriver-platform.Po \
+ ./$(DEPDIR)/networking_testdriver-test_networking.Po \
./$(DEPDIR)/packet_id_testdriver-buffer.Po \
+ ./$(DEPDIR)/packet_id_testdriver-mock_get_random.Po \
./$(DEPDIR)/packet_id_testdriver-mock_msg.Po \
./$(DEPDIR)/packet_id_testdriver-otime.Po \
./$(DEPDIR)/packet_id_testdriver-packet_id.Po \
./$(DEPDIR)/packet_id_testdriver-platform.Po \
./$(DEPDIR)/packet_id_testdriver-test_packet_id.Po \
+ ./$(DEPDIR)/tls_crypt_testdriver-argv.Po \
+ ./$(DEPDIR)/tls_crypt_testdriver-base64.Po \
./$(DEPDIR)/tls_crypt_testdriver-buffer.Po \
./$(DEPDIR)/tls_crypt_testdriver-crypto.Po \
./$(DEPDIR)/tls_crypt_testdriver-crypto_mbedtls.Po \
./$(DEPDIR)/tls_crypt_testdriver-crypto_openssl.Po \
+ ./$(DEPDIR)/tls_crypt_testdriver-env_set.Po \
./$(DEPDIR)/tls_crypt_testdriver-mock_msg.Po \
./$(DEPDIR)/tls_crypt_testdriver-otime.Po \
./$(DEPDIR)/tls_crypt_testdriver-packet_id.Po \
./$(DEPDIR)/tls_crypt_testdriver-platform.Po \
+ ./$(DEPDIR)/tls_crypt_testdriver-run_command.Po \
./$(DEPDIR)/tls_crypt_testdriver-test_tls_crypt.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
@@ -224,10 +350,15 @@ 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 = $(argv_testdriver_SOURCES) $(buffer_testdriver_SOURCES) \
+SOURCES = $(argv_testdriver_SOURCES) $(auth_token_testdriver_SOURCES) \
+ $(buffer_testdriver_SOURCES) $(crypto_testdriver_SOURCES) \
+ $(ncp_testdriver_SOURCES) $(networking_testdriver_SOURCES) \
$(packet_id_testdriver_SOURCES) \
$(tls_crypt_testdriver_SOURCES)
-DIST_SOURCES = $(argv_testdriver_SOURCES) $(buffer_testdriver_SOURCES) \
+DIST_SOURCES = $(argv_testdriver_SOURCES) \
+ $(auth_token_testdriver_SOURCES) $(buffer_testdriver_SOURCES) \
+ $(crypto_testdriver_SOURCES) $(ncp_testdriver_SOURCES) \
+ $(am__networking_testdriver_SOURCES_DIST) \
$(packet_id_testdriver_SOURCES) \
$(tls_crypt_testdriver_SOURCES)
am__can_run_installinfo = \
@@ -290,7 +421,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -304,6 +436,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -331,7 +464,6 @@ 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@
@@ -382,6 +514,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -445,6 +579,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -457,17 +592,15 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = foreign
-TESTS = $(check_PROGRAMS)
+test_binaries = $(am__append_1) crypto_testdriver packet_id_testdriver \
+ auth_token_testdriver ncp_testdriver $(am__append_2)
openvpn_includedir = $(top_srcdir)/include
openvpn_srcdir = $(top_srcdir)/src/openvpn
compat_srcdir = $(top_srcdir)/src/compat
-argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) \
- $(OPTIONAL_CRYPTO_CFLAGS)
-
-argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line \
- $(OPTIONAL_CRYPTO_LIBS)
-
+argv_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
+argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line
argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
$(openvpn_srcdir)/platform.c \
$(openvpn_srcdir)/buffer.c \
$(openvpn_srcdir)/argv.c
@@ -475,30 +608,97 @@ argv_testdriver_SOURCES = test_argv.c mock_msg.c mock_msg.h \
buffer_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir)
buffer_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line
buffer_testdriver_SOURCES = test_buffer.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
+ $(openvpn_srcdir)/platform.c
+
+crypto_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+
+crypto_testdriver_LDFLAGS = @TEST_LDFLAGS@
+crypto_testdriver_SOURCES = test_crypto.c mock_msg.c mock_msg.h \
$(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
$(openvpn_srcdir)/platform.c
packet_id_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+
+packet_id_testdriver_LDFLAGS = @TEST_LDFLAGS@
+packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \
+ mock_get_random.c \
+ $(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
+ $(openvpn_srcdir)/platform.c
+
+tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+
+tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+ -Wl,--wrap=buffer_read_from_file \
+ -Wl,--wrap=buffer_write_file \
+ -Wl,--wrap=parse_line \
+ -Wl,--wrap=rand_bytes
+
+tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \
+ $(openvpn_srcdir)/argv.c \
+ $(openvpn_srcdir)/base64.c \
+ $(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
+ $(openvpn_srcdir)/env_set.c \
+ $(openvpn_srcdir)/otime.c \
+ $(openvpn_srcdir)/packet_id.c \
+ $(openvpn_srcdir)/platform.c \
+ $(openvpn_srcdir)/run_command.c
+
+@HAVE_SITNL_TRUE@networking_testdriver_CFLAGS = @TEST_CFLAGS@ \
+@HAVE_SITNL_TRUE@ -I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
+@HAVE_SITNL_TRUE@ $(OPTIONAL_CRYPTO_CFLAGS)
+
+@HAVE_SITNL_TRUE@networking_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) \
+@HAVE_SITNL_TRUE@ $(OPTIONAL_CRYPTO_LIBS)
+
+@HAVE_SITNL_TRUE@networking_testdriver_SOURCES = test_networking.c mock_msg.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/networking_sitnl.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/buffer.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/crypto.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/crypto_mbedtls.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/crypto_openssl.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/otime.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/packet_id.c \
+@HAVE_SITNL_TRUE@ $(openvpn_srcdir)/platform.c
+
+auth_token_testdriver_CFLAGS = @TEST_CFLAGS@ \
-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
$(OPTIONAL_CRYPTO_CFLAGS)
-packet_id_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+auth_token_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
$(OPTIONAL_CRYPTO_LIBS)
-packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \
+auth_token_testdriver_SOURCES = test_auth_token.c mock_msg.c \
$(openvpn_srcdir)/buffer.c \
+ $(openvpn_srcdir)/crypto.c \
+ $(openvpn_srcdir)/crypto_mbedtls.c \
+ $(openvpn_srcdir)/crypto_openssl.c \
$(openvpn_srcdir)/otime.c \
$(openvpn_srcdir)/packet_id.c \
- $(openvpn_srcdir)/platform.c
+ $(openvpn_srcdir)/platform.c \
+ $(openvpn_srcdir)/base64.c
-tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \
+ncp_testdriver_CFLAGS = @TEST_CFLAGS@ \
-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
$(OPTIONAL_CRYPTO_CFLAGS)
-tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
+ncp_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
$(OPTIONAL_CRYPTO_LIBS)
-tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c mock_msg.h \
+ncp_testdriver_SOURCES = test_ncp.c mock_msg.c \
$(openvpn_srcdir)/buffer.c \
$(openvpn_srcdir)/crypto.c \
$(openvpn_srcdir)/crypto_mbedtls.c \
@@ -554,10 +754,26 @@ argv_testdriver$(EXEEXT): $(argv_testdriver_OBJECTS) $(argv_testdriver_DEPENDENC
@rm -f argv_testdriver$(EXEEXT)
$(AM_V_CCLD)$(argv_testdriver_LINK) $(argv_testdriver_OBJECTS) $(argv_testdriver_LDADD) $(LIBS)
+auth_token_testdriver$(EXEEXT): $(auth_token_testdriver_OBJECTS) $(auth_token_testdriver_DEPENDENCIES) $(EXTRA_auth_token_testdriver_DEPENDENCIES)
+ @rm -f auth_token_testdriver$(EXEEXT)
+ $(AM_V_CCLD)$(auth_token_testdriver_LINK) $(auth_token_testdriver_OBJECTS) $(auth_token_testdriver_LDADD) $(LIBS)
+
buffer_testdriver$(EXEEXT): $(buffer_testdriver_OBJECTS) $(buffer_testdriver_DEPENDENCIES) $(EXTRA_buffer_testdriver_DEPENDENCIES)
@rm -f buffer_testdriver$(EXEEXT)
$(AM_V_CCLD)$(buffer_testdriver_LINK) $(buffer_testdriver_OBJECTS) $(buffer_testdriver_LDADD) $(LIBS)
+crypto_testdriver$(EXEEXT): $(crypto_testdriver_OBJECTS) $(crypto_testdriver_DEPENDENCIES) $(EXTRA_crypto_testdriver_DEPENDENCIES)
+ @rm -f crypto_testdriver$(EXEEXT)
+ $(AM_V_CCLD)$(crypto_testdriver_LINK) $(crypto_testdriver_OBJECTS) $(crypto_testdriver_LDADD) $(LIBS)
+
+ncp_testdriver$(EXEEXT): $(ncp_testdriver_OBJECTS) $(ncp_testdriver_DEPENDENCIES) $(EXTRA_ncp_testdriver_DEPENDENCIES)
+ @rm -f ncp_testdriver$(EXEEXT)
+ $(AM_V_CCLD)$(ncp_testdriver_LINK) $(ncp_testdriver_OBJECTS) $(ncp_testdriver_LDADD) $(LIBS)
+
+networking_testdriver$(EXEEXT): $(networking_testdriver_OBJECTS) $(networking_testdriver_DEPENDENCIES) $(EXTRA_networking_testdriver_DEPENDENCIES)
+ @rm -f networking_testdriver$(EXEEXT)
+ $(AM_V_CCLD)$(networking_testdriver_LINK) $(networking_testdriver_OBJECTS) $(networking_testdriver_LDADD) $(LIBS)
+
packet_id_testdriver$(EXEEXT): $(packet_id_testdriver_OBJECTS) $(packet_id_testdriver_DEPENDENCIES) $(EXTRA_packet_id_testdriver_DEPENDENCIES)
@rm -f packet_id_testdriver$(EXEEXT)
$(AM_V_CCLD)$(packet_id_testdriver_LINK) $(packet_id_testdriver_OBJECTS) $(packet_id_testdriver_LDADD) $(LIBS)
@@ -574,27 +790,71 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-argv.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-mock_get_random.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-mock_msg.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-platform.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argv_testdriver-test_argv.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-base64.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-crypto_openssl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-mock_msg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-otime.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-packet_id.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-platform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_token_testdriver-test_auth_token.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer_testdriver-mock_get_random.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer_testdriver-mock_msg.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer_testdriver-platform.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer_testdriver-test_buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-crypto_openssl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-mock_msg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-otime.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-packet_id.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-platform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_testdriver-test_crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-crypto_openssl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-mock_msg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-otime.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-packet_id.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-platform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ncp_testdriver-test_ncp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-crypto_mbedtls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-crypto_openssl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-mock_msg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-networking_sitnl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-otime.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-packet_id.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-platform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networking_testdriver-test_networking.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-buffer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-mock_get_random.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-mock_msg.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-otime.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-packet_id.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-platform.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id_testdriver-test_packet_id.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-argv.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-base64.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-buffer.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-crypto.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-crypto_mbedtls.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-crypto_openssl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-env_set.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-mock_msg.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-otime.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-packet_id.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-platform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-run_command.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypt_testdriver-test_tls_crypt.Po@am__quote@ # am--include-marker
$(am__depfiles_remade):
@@ -652,6 +912,20 @@ argv_testdriver-mock_msg.obj: mock_msg.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -c -o argv_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+argv_testdriver-mock_get_random.o: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -MT argv_testdriver-mock_get_random.o -MD -MP -MF $(DEPDIR)/argv_testdriver-mock_get_random.Tpo -c -o argv_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/argv_testdriver-mock_get_random.Tpo $(DEPDIR)/argv_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='argv_testdriver-mock_get_random.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -c -o argv_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
+
+argv_testdriver-mock_get_random.obj: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -MT argv_testdriver-mock_get_random.obj -MD -MP -MF $(DEPDIR)/argv_testdriver-mock_get_random.Tpo -c -o argv_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/argv_testdriver-mock_get_random.Tpo $(DEPDIR)/argv_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='argv_testdriver-mock_get_random.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -c -o argv_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
+
argv_testdriver-platform.o: $(openvpn_srcdir)/platform.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -MT argv_testdriver-platform.o -MD -MP -MF $(DEPDIR)/argv_testdriver-platform.Tpo -c -o argv_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/argv_testdriver-platform.Tpo $(DEPDIR)/argv_testdriver-platform.Po
@@ -694,6 +968,146 @@ argv_testdriver-argv.obj: $(openvpn_srcdir)/argv.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(argv_testdriver_CFLAGS) $(CFLAGS) -c -o argv_testdriver-argv.obj `if test -f '$(openvpn_srcdir)/argv.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/argv.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/argv.c'; fi`
+auth_token_testdriver-test_auth_token.o: test_auth_token.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-test_auth_token.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-test_auth_token.Tpo -c -o auth_token_testdriver-test_auth_token.o `test -f 'test_auth_token.c' || echo '$(srcdir)/'`test_auth_token.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-test_auth_token.Tpo $(DEPDIR)/auth_token_testdriver-test_auth_token.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_auth_token.c' object='auth_token_testdriver-test_auth_token.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-test_auth_token.o `test -f 'test_auth_token.c' || echo '$(srcdir)/'`test_auth_token.c
+
+auth_token_testdriver-test_auth_token.obj: test_auth_token.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-test_auth_token.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-test_auth_token.Tpo -c -o auth_token_testdriver-test_auth_token.obj `if test -f 'test_auth_token.c'; then $(CYGPATH_W) 'test_auth_token.c'; else $(CYGPATH_W) '$(srcdir)/test_auth_token.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-test_auth_token.Tpo $(DEPDIR)/auth_token_testdriver-test_auth_token.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_auth_token.c' object='auth_token_testdriver-test_auth_token.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-test_auth_token.obj `if test -f 'test_auth_token.c'; then $(CYGPATH_W) 'test_auth_token.c'; else $(CYGPATH_W) '$(srcdir)/test_auth_token.c'; fi`
+
+auth_token_testdriver-mock_msg.o: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-mock_msg.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-mock_msg.Tpo -c -o auth_token_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-mock_msg.Tpo $(DEPDIR)/auth_token_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='auth_token_testdriver-mock_msg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+
+auth_token_testdriver-mock_msg.obj: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-mock_msg.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-mock_msg.Tpo -c -o auth_token_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-mock_msg.Tpo $(DEPDIR)/auth_token_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='auth_token_testdriver-mock_msg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+
+auth_token_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-buffer.Tpo -c -o auth_token_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-buffer.Tpo $(DEPDIR)/auth_token_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='auth_token_testdriver-buffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+
+auth_token_testdriver-buffer.obj: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-buffer.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-buffer.Tpo -c -o auth_token_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-buffer.Tpo $(DEPDIR)/auth_token_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='auth_token_testdriver-buffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+
+auth_token_testdriver-crypto.o: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto.Tpo -c -o auth_token_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto.Tpo $(DEPDIR)/auth_token_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='auth_token_testdriver-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+
+auth_token_testdriver-crypto.obj: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto.Tpo -c -o auth_token_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto.Tpo $(DEPDIR)/auth_token_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='auth_token_testdriver-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+
+auth_token_testdriver-crypto_mbedtls.o: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto_mbedtls.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Tpo -c -o auth_token_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='auth_token_testdriver-crypto_mbedtls.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+
+auth_token_testdriver-crypto_mbedtls.obj: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto_mbedtls.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Tpo -c -o auth_token_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='auth_token_testdriver-crypto_mbedtls.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+
+auth_token_testdriver-crypto_openssl.o: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto_openssl.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto_openssl.Tpo -c -o auth_token_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto_openssl.Tpo $(DEPDIR)/auth_token_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='auth_token_testdriver-crypto_openssl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+
+auth_token_testdriver-crypto_openssl.obj: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-crypto_openssl.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-crypto_openssl.Tpo -c -o auth_token_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-crypto_openssl.Tpo $(DEPDIR)/auth_token_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='auth_token_testdriver-crypto_openssl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+
+auth_token_testdriver-otime.o: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-otime.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-otime.Tpo -c -o auth_token_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-otime.Tpo $(DEPDIR)/auth_token_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='auth_token_testdriver-otime.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+
+auth_token_testdriver-otime.obj: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-otime.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-otime.Tpo -c -o auth_token_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-otime.Tpo $(DEPDIR)/auth_token_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='auth_token_testdriver-otime.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+
+auth_token_testdriver-packet_id.o: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-packet_id.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-packet_id.Tpo -c -o auth_token_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-packet_id.Tpo $(DEPDIR)/auth_token_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='auth_token_testdriver-packet_id.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+
+auth_token_testdriver-packet_id.obj: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-packet_id.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-packet_id.Tpo -c -o auth_token_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-packet_id.Tpo $(DEPDIR)/auth_token_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='auth_token_testdriver-packet_id.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+
+auth_token_testdriver-platform.o: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-platform.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-platform.Tpo -c -o auth_token_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-platform.Tpo $(DEPDIR)/auth_token_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='auth_token_testdriver-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+
+auth_token_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-platform.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-platform.Tpo -c -o auth_token_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-platform.Tpo $(DEPDIR)/auth_token_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='auth_token_testdriver-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+
+auth_token_testdriver-base64.o: $(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-base64.o -MD -MP -MF $(DEPDIR)/auth_token_testdriver-base64.Tpo -c -o auth_token_testdriver-base64.o `test -f '$(openvpn_srcdir)/base64.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-base64.Tpo $(DEPDIR)/auth_token_testdriver-base64.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/base64.c' object='auth_token_testdriver-base64.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-base64.o `test -f '$(openvpn_srcdir)/base64.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/base64.c
+
+auth_token_testdriver-base64.obj: $(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -MT auth_token_testdriver-base64.obj -MD -MP -MF $(DEPDIR)/auth_token_testdriver-base64.Tpo -c -o auth_token_testdriver-base64.obj `if test -f '$(openvpn_srcdir)/base64.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/base64.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/base64.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/auth_token_testdriver-base64.Tpo $(DEPDIR)/auth_token_testdriver-base64.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/base64.c' object='auth_token_testdriver-base64.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(auth_token_testdriver_CFLAGS) $(CFLAGS) -c -o auth_token_testdriver-base64.obj `if test -f '$(openvpn_srcdir)/base64.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/base64.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/base64.c'; fi`
+
buffer_testdriver-test_buffer.o: test_buffer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-test_buffer.o -MD -MP -MF $(DEPDIR)/buffer_testdriver-test_buffer.Tpo -c -o buffer_testdriver-test_buffer.o `test -f 'test_buffer.c' || echo '$(srcdir)/'`test_buffer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buffer_testdriver-test_buffer.Tpo $(DEPDIR)/buffer_testdriver-test_buffer.Po
@@ -722,19 +1136,19 @@ buffer_testdriver-mock_msg.obj: mock_msg.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
-buffer_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/buffer_testdriver-buffer.Tpo -c -o buffer_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buffer_testdriver-buffer.Tpo $(DEPDIR)/buffer_testdriver-buffer.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='buffer_testdriver-buffer.o' libtool=no @AMDEPBACKSLASH@
+buffer_testdriver-mock_get_random.o: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-mock_get_random.o -MD -MP -MF $(DEPDIR)/buffer_testdriver-mock_get_random.Tpo -c -o buffer_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buffer_testdriver-mock_get_random.Tpo $(DEPDIR)/buffer_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='buffer_testdriver-mock_get_random.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
-buffer_testdriver-buffer.obj: $(openvpn_srcdir)/buffer.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-buffer.obj -MD -MP -MF $(DEPDIR)/buffer_testdriver-buffer.Tpo -c -o buffer_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buffer_testdriver-buffer.Tpo $(DEPDIR)/buffer_testdriver-buffer.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='buffer_testdriver-buffer.obj' libtool=no @AMDEPBACKSLASH@
+buffer_testdriver-mock_get_random.obj: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-mock_get_random.obj -MD -MP -MF $(DEPDIR)/buffer_testdriver-mock_get_random.Tpo -c -o buffer_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buffer_testdriver-mock_get_random.Tpo $(DEPDIR)/buffer_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='buffer_testdriver-mock_get_random.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
buffer_testdriver-platform.o: $(openvpn_srcdir)/platform.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -MT buffer_testdriver-platform.o -MD -MP -MF $(DEPDIR)/buffer_testdriver-platform.Tpo -c -o buffer_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
@@ -750,6 +1164,398 @@ buffer_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buffer_testdriver_CFLAGS) $(CFLAGS) -c -o buffer_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+crypto_testdriver-test_crypto.o: test_crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-test_crypto.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-test_crypto.Tpo -c -o crypto_testdriver-test_crypto.o `test -f 'test_crypto.c' || echo '$(srcdir)/'`test_crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-test_crypto.Tpo $(DEPDIR)/crypto_testdriver-test_crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_crypto.c' object='crypto_testdriver-test_crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-test_crypto.o `test -f 'test_crypto.c' || echo '$(srcdir)/'`test_crypto.c
+
+crypto_testdriver-test_crypto.obj: test_crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-test_crypto.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-test_crypto.Tpo -c -o crypto_testdriver-test_crypto.obj `if test -f 'test_crypto.c'; then $(CYGPATH_W) 'test_crypto.c'; else $(CYGPATH_W) '$(srcdir)/test_crypto.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-test_crypto.Tpo $(DEPDIR)/crypto_testdriver-test_crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_crypto.c' object='crypto_testdriver-test_crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-test_crypto.obj `if test -f 'test_crypto.c'; then $(CYGPATH_W) 'test_crypto.c'; else $(CYGPATH_W) '$(srcdir)/test_crypto.c'; fi`
+
+crypto_testdriver-mock_msg.o: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-mock_msg.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-mock_msg.Tpo -c -o crypto_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-mock_msg.Tpo $(DEPDIR)/crypto_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='crypto_testdriver-mock_msg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+
+crypto_testdriver-mock_msg.obj: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-mock_msg.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-mock_msg.Tpo -c -o crypto_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-mock_msg.Tpo $(DEPDIR)/crypto_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='crypto_testdriver-mock_msg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+
+crypto_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-buffer.Tpo -c -o crypto_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-buffer.Tpo $(DEPDIR)/crypto_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='crypto_testdriver-buffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+
+crypto_testdriver-buffer.obj: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-buffer.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-buffer.Tpo -c -o crypto_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-buffer.Tpo $(DEPDIR)/crypto_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='crypto_testdriver-buffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+
+crypto_testdriver-crypto.o: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto.Tpo -c -o crypto_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto.Tpo $(DEPDIR)/crypto_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='crypto_testdriver-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+
+crypto_testdriver-crypto.obj: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto.Tpo -c -o crypto_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto.Tpo $(DEPDIR)/crypto_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='crypto_testdriver-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+
+crypto_testdriver-crypto_mbedtls.o: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto_mbedtls.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Tpo -c -o crypto_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='crypto_testdriver-crypto_mbedtls.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+
+crypto_testdriver-crypto_mbedtls.obj: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto_mbedtls.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Tpo -c -o crypto_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='crypto_testdriver-crypto_mbedtls.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+
+crypto_testdriver-crypto_openssl.o: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto_openssl.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto_openssl.Tpo -c -o crypto_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto_openssl.Tpo $(DEPDIR)/crypto_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='crypto_testdriver-crypto_openssl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+
+crypto_testdriver-crypto_openssl.obj: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-crypto_openssl.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-crypto_openssl.Tpo -c -o crypto_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-crypto_openssl.Tpo $(DEPDIR)/crypto_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='crypto_testdriver-crypto_openssl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+
+crypto_testdriver-otime.o: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-otime.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-otime.Tpo -c -o crypto_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-otime.Tpo $(DEPDIR)/crypto_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='crypto_testdriver-otime.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+
+crypto_testdriver-otime.obj: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-otime.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-otime.Tpo -c -o crypto_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-otime.Tpo $(DEPDIR)/crypto_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='crypto_testdriver-otime.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+
+crypto_testdriver-packet_id.o: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-packet_id.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-packet_id.Tpo -c -o crypto_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-packet_id.Tpo $(DEPDIR)/crypto_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='crypto_testdriver-packet_id.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+
+crypto_testdriver-packet_id.obj: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-packet_id.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-packet_id.Tpo -c -o crypto_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-packet_id.Tpo $(DEPDIR)/crypto_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='crypto_testdriver-packet_id.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+
+crypto_testdriver-platform.o: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-platform.o -MD -MP -MF $(DEPDIR)/crypto_testdriver-platform.Tpo -c -o crypto_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-platform.Tpo $(DEPDIR)/crypto_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='crypto_testdriver-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+
+crypto_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -MT crypto_testdriver-platform.obj -MD -MP -MF $(DEPDIR)/crypto_testdriver-platform.Tpo -c -o crypto_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/crypto_testdriver-platform.Tpo $(DEPDIR)/crypto_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='crypto_testdriver-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(crypto_testdriver_CFLAGS) $(CFLAGS) -c -o crypto_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+
+ncp_testdriver-test_ncp.o: test_ncp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-test_ncp.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-test_ncp.Tpo -c -o ncp_testdriver-test_ncp.o `test -f 'test_ncp.c' || echo '$(srcdir)/'`test_ncp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-test_ncp.Tpo $(DEPDIR)/ncp_testdriver-test_ncp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_ncp.c' object='ncp_testdriver-test_ncp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-test_ncp.o `test -f 'test_ncp.c' || echo '$(srcdir)/'`test_ncp.c
+
+ncp_testdriver-test_ncp.obj: test_ncp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-test_ncp.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-test_ncp.Tpo -c -o ncp_testdriver-test_ncp.obj `if test -f 'test_ncp.c'; then $(CYGPATH_W) 'test_ncp.c'; else $(CYGPATH_W) '$(srcdir)/test_ncp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-test_ncp.Tpo $(DEPDIR)/ncp_testdriver-test_ncp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_ncp.c' object='ncp_testdriver-test_ncp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-test_ncp.obj `if test -f 'test_ncp.c'; then $(CYGPATH_W) 'test_ncp.c'; else $(CYGPATH_W) '$(srcdir)/test_ncp.c'; fi`
+
+ncp_testdriver-mock_msg.o: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-mock_msg.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-mock_msg.Tpo -c -o ncp_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-mock_msg.Tpo $(DEPDIR)/ncp_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='ncp_testdriver-mock_msg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+
+ncp_testdriver-mock_msg.obj: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-mock_msg.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-mock_msg.Tpo -c -o ncp_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-mock_msg.Tpo $(DEPDIR)/ncp_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='ncp_testdriver-mock_msg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+
+ncp_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-buffer.Tpo -c -o ncp_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-buffer.Tpo $(DEPDIR)/ncp_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='ncp_testdriver-buffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+
+ncp_testdriver-buffer.obj: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-buffer.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-buffer.Tpo -c -o ncp_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-buffer.Tpo $(DEPDIR)/ncp_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='ncp_testdriver-buffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+
+ncp_testdriver-crypto.o: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto.Tpo -c -o ncp_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto.Tpo $(DEPDIR)/ncp_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='ncp_testdriver-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+
+ncp_testdriver-crypto.obj: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto.Tpo -c -o ncp_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto.Tpo $(DEPDIR)/ncp_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='ncp_testdriver-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+
+ncp_testdriver-crypto_mbedtls.o: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto_mbedtls.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Tpo -c -o ncp_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='ncp_testdriver-crypto_mbedtls.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+
+ncp_testdriver-crypto_mbedtls.obj: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto_mbedtls.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Tpo -c -o ncp_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='ncp_testdriver-crypto_mbedtls.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+
+ncp_testdriver-crypto_openssl.o: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto_openssl.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto_openssl.Tpo -c -o ncp_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto_openssl.Tpo $(DEPDIR)/ncp_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='ncp_testdriver-crypto_openssl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+
+ncp_testdriver-crypto_openssl.obj: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-crypto_openssl.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-crypto_openssl.Tpo -c -o ncp_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-crypto_openssl.Tpo $(DEPDIR)/ncp_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='ncp_testdriver-crypto_openssl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+
+ncp_testdriver-otime.o: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-otime.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-otime.Tpo -c -o ncp_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-otime.Tpo $(DEPDIR)/ncp_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='ncp_testdriver-otime.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+
+ncp_testdriver-otime.obj: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-otime.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-otime.Tpo -c -o ncp_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-otime.Tpo $(DEPDIR)/ncp_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='ncp_testdriver-otime.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+
+ncp_testdriver-packet_id.o: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-packet_id.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-packet_id.Tpo -c -o ncp_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-packet_id.Tpo $(DEPDIR)/ncp_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='ncp_testdriver-packet_id.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+
+ncp_testdriver-packet_id.obj: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-packet_id.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-packet_id.Tpo -c -o ncp_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-packet_id.Tpo $(DEPDIR)/ncp_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='ncp_testdriver-packet_id.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+
+ncp_testdriver-platform.o: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-platform.o -MD -MP -MF $(DEPDIR)/ncp_testdriver-platform.Tpo -c -o ncp_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-platform.Tpo $(DEPDIR)/ncp_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='ncp_testdriver-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+
+ncp_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -MT ncp_testdriver-platform.obj -MD -MP -MF $(DEPDIR)/ncp_testdriver-platform.Tpo -c -o ncp_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ncp_testdriver-platform.Tpo $(DEPDIR)/ncp_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='ncp_testdriver-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ncp_testdriver_CFLAGS) $(CFLAGS) -c -o ncp_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+
+networking_testdriver-test_networking.o: test_networking.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-test_networking.o -MD -MP -MF $(DEPDIR)/networking_testdriver-test_networking.Tpo -c -o networking_testdriver-test_networking.o `test -f 'test_networking.c' || echo '$(srcdir)/'`test_networking.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-test_networking.Tpo $(DEPDIR)/networking_testdriver-test_networking.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_networking.c' object='networking_testdriver-test_networking.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-test_networking.o `test -f 'test_networking.c' || echo '$(srcdir)/'`test_networking.c
+
+networking_testdriver-test_networking.obj: test_networking.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-test_networking.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-test_networking.Tpo -c -o networking_testdriver-test_networking.obj `if test -f 'test_networking.c'; then $(CYGPATH_W) 'test_networking.c'; else $(CYGPATH_W) '$(srcdir)/test_networking.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-test_networking.Tpo $(DEPDIR)/networking_testdriver-test_networking.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_networking.c' object='networking_testdriver-test_networking.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-test_networking.obj `if test -f 'test_networking.c'; then $(CYGPATH_W) 'test_networking.c'; else $(CYGPATH_W) '$(srcdir)/test_networking.c'; fi`
+
+networking_testdriver-mock_msg.o: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-mock_msg.o -MD -MP -MF $(DEPDIR)/networking_testdriver-mock_msg.Tpo -c -o networking_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-mock_msg.Tpo $(DEPDIR)/networking_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='networking_testdriver-mock_msg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-mock_msg.o `test -f 'mock_msg.c' || echo '$(srcdir)/'`mock_msg.c
+
+networking_testdriver-mock_msg.obj: mock_msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-mock_msg.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-mock_msg.Tpo -c -o networking_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-mock_msg.Tpo $(DEPDIR)/networking_testdriver-mock_msg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_msg.c' object='networking_testdriver-mock_msg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+
+networking_testdriver-networking_sitnl.o: $(openvpn_srcdir)/networking_sitnl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-networking_sitnl.o -MD -MP -MF $(DEPDIR)/networking_testdriver-networking_sitnl.Tpo -c -o networking_testdriver-networking_sitnl.o `test -f '$(openvpn_srcdir)/networking_sitnl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/networking_sitnl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-networking_sitnl.Tpo $(DEPDIR)/networking_testdriver-networking_sitnl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/networking_sitnl.c' object='networking_testdriver-networking_sitnl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-networking_sitnl.o `test -f '$(openvpn_srcdir)/networking_sitnl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/networking_sitnl.c
+
+networking_testdriver-networking_sitnl.obj: $(openvpn_srcdir)/networking_sitnl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-networking_sitnl.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-networking_sitnl.Tpo -c -o networking_testdriver-networking_sitnl.obj `if test -f '$(openvpn_srcdir)/networking_sitnl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/networking_sitnl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/networking_sitnl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-networking_sitnl.Tpo $(DEPDIR)/networking_testdriver-networking_sitnl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/networking_sitnl.c' object='networking_testdriver-networking_sitnl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-networking_sitnl.obj `if test -f '$(openvpn_srcdir)/networking_sitnl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/networking_sitnl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/networking_sitnl.c'; fi`
+
+networking_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/networking_testdriver-buffer.Tpo -c -o networking_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-buffer.Tpo $(DEPDIR)/networking_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='networking_testdriver-buffer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
+
+networking_testdriver-buffer.obj: $(openvpn_srcdir)/buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-buffer.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-buffer.Tpo -c -o networking_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-buffer.Tpo $(DEPDIR)/networking_testdriver-buffer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/buffer.c' object='networking_testdriver-buffer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-buffer.obj `if test -f '$(openvpn_srcdir)/buffer.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/buffer.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/buffer.c'; fi`
+
+networking_testdriver-crypto.o: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto.o -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto.Tpo -c -o networking_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto.Tpo $(DEPDIR)/networking_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='networking_testdriver-crypto.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto.o `test -f '$(openvpn_srcdir)/crypto.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto.c
+
+networking_testdriver-crypto.obj: $(openvpn_srcdir)/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto.Tpo -c -o networking_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto.Tpo $(DEPDIR)/networking_testdriver-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto.c' object='networking_testdriver-crypto.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto.obj `if test -f '$(openvpn_srcdir)/crypto.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto.c'; fi`
+
+networking_testdriver-crypto_mbedtls.o: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto_mbedtls.o -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto_mbedtls.Tpo -c -o networking_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/networking_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='networking_testdriver-crypto_mbedtls.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto_mbedtls.o `test -f '$(openvpn_srcdir)/crypto_mbedtls.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_mbedtls.c
+
+networking_testdriver-crypto_mbedtls.obj: $(openvpn_srcdir)/crypto_mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto_mbedtls.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto_mbedtls.Tpo -c -o networking_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto_mbedtls.Tpo $(DEPDIR)/networking_testdriver-crypto_mbedtls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_mbedtls.c' object='networking_testdriver-crypto_mbedtls.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto_mbedtls.obj `if test -f '$(openvpn_srcdir)/crypto_mbedtls.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_mbedtls.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_mbedtls.c'; fi`
+
+networking_testdriver-crypto_openssl.o: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto_openssl.o -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto_openssl.Tpo -c -o networking_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto_openssl.Tpo $(DEPDIR)/networking_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='networking_testdriver-crypto_openssl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto_openssl.o `test -f '$(openvpn_srcdir)/crypto_openssl.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/crypto_openssl.c
+
+networking_testdriver-crypto_openssl.obj: $(openvpn_srcdir)/crypto_openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-crypto_openssl.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-crypto_openssl.Tpo -c -o networking_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-crypto_openssl.Tpo $(DEPDIR)/networking_testdriver-crypto_openssl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/crypto_openssl.c' object='networking_testdriver-crypto_openssl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+
+networking_testdriver-otime.o: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-otime.o -MD -MP -MF $(DEPDIR)/networking_testdriver-otime.Tpo -c -o networking_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-otime.Tpo $(DEPDIR)/networking_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='networking_testdriver-otime.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
+
+networking_testdriver-otime.obj: $(openvpn_srcdir)/otime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-otime.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-otime.Tpo -c -o networking_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-otime.Tpo $(DEPDIR)/networking_testdriver-otime.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/otime.c' object='networking_testdriver-otime.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-otime.obj `if test -f '$(openvpn_srcdir)/otime.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/otime.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/otime.c'; fi`
+
+networking_testdriver-packet_id.o: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-packet_id.o -MD -MP -MF $(DEPDIR)/networking_testdriver-packet_id.Tpo -c -o networking_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-packet_id.Tpo $(DEPDIR)/networking_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='networking_testdriver-packet_id.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-packet_id.o `test -f '$(openvpn_srcdir)/packet_id.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/packet_id.c
+
+networking_testdriver-packet_id.obj: $(openvpn_srcdir)/packet_id.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-packet_id.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-packet_id.Tpo -c -o networking_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-packet_id.Tpo $(DEPDIR)/networking_testdriver-packet_id.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/packet_id.c' object='networking_testdriver-packet_id.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-packet_id.obj `if test -f '$(openvpn_srcdir)/packet_id.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/packet_id.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/packet_id.c'; fi`
+
+networking_testdriver-platform.o: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-platform.o -MD -MP -MF $(DEPDIR)/networking_testdriver-platform.Tpo -c -o networking_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-platform.Tpo $(DEPDIR)/networking_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='networking_testdriver-platform.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-platform.o `test -f '$(openvpn_srcdir)/platform.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/platform.c
+
+networking_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -MT networking_testdriver-platform.obj -MD -MP -MF $(DEPDIR)/networking_testdriver-platform.Tpo -c -o networking_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/networking_testdriver-platform.Tpo $(DEPDIR)/networking_testdriver-platform.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/platform.c' object='networking_testdriver-platform.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(networking_testdriver_CFLAGS) $(CFLAGS) -c -o networking_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+
packet_id_testdriver-test_packet_id.o: test_packet_id.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -MT packet_id_testdriver-test_packet_id.o -MD -MP -MF $(DEPDIR)/packet_id_testdriver-test_packet_id.Tpo -c -o packet_id_testdriver-test_packet_id.o `test -f 'test_packet_id.c' || echo '$(srcdir)/'`test_packet_id.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/packet_id_testdriver-test_packet_id.Tpo $(DEPDIR)/packet_id_testdriver-test_packet_id.Po
@@ -778,6 +1584,20 @@ packet_id_testdriver-mock_msg.obj: mock_msg.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -c -o packet_id_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+packet_id_testdriver-mock_get_random.o: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -MT packet_id_testdriver-mock_get_random.o -MD -MP -MF $(DEPDIR)/packet_id_testdriver-mock_get_random.Tpo -c -o packet_id_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/packet_id_testdriver-mock_get_random.Tpo $(DEPDIR)/packet_id_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='packet_id_testdriver-mock_get_random.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -c -o packet_id_testdriver-mock_get_random.o `test -f 'mock_get_random.c' || echo '$(srcdir)/'`mock_get_random.c
+
+packet_id_testdriver-mock_get_random.obj: mock_get_random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -MT packet_id_testdriver-mock_get_random.obj -MD -MP -MF $(DEPDIR)/packet_id_testdriver-mock_get_random.Tpo -c -o packet_id_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/packet_id_testdriver-mock_get_random.Tpo $(DEPDIR)/packet_id_testdriver-mock_get_random.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mock_get_random.c' object='packet_id_testdriver-mock_get_random.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -c -o packet_id_testdriver-mock_get_random.obj `if test -f 'mock_get_random.c'; then $(CYGPATH_W) 'mock_get_random.c'; else $(CYGPATH_W) '$(srcdir)/mock_get_random.c'; fi`
+
packet_id_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(packet_id_testdriver_CFLAGS) $(CFLAGS) -MT packet_id_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/packet_id_testdriver-buffer.Tpo -c -o packet_id_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/packet_id_testdriver-buffer.Tpo $(DEPDIR)/packet_id_testdriver-buffer.Po
@@ -862,6 +1682,34 @@ tls_crypt_testdriver-mock_msg.obj: mock_msg.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-mock_msg.obj `if test -f 'mock_msg.c'; then $(CYGPATH_W) 'mock_msg.c'; else $(CYGPATH_W) '$(srcdir)/mock_msg.c'; fi`
+tls_crypt_testdriver-argv.o: $(openvpn_srcdir)/argv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-argv.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-argv.Tpo -c -o tls_crypt_testdriver-argv.o `test -f '$(openvpn_srcdir)/argv.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/argv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-argv.Tpo $(DEPDIR)/tls_crypt_testdriver-argv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/argv.c' object='tls_crypt_testdriver-argv.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-argv.o `test -f '$(openvpn_srcdir)/argv.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/argv.c
+
+tls_crypt_testdriver-argv.obj: $(openvpn_srcdir)/argv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-argv.obj -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-argv.Tpo -c -o tls_crypt_testdriver-argv.obj `if test -f '$(openvpn_srcdir)/argv.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/argv.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/argv.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-argv.Tpo $(DEPDIR)/tls_crypt_testdriver-argv.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/argv.c' object='tls_crypt_testdriver-argv.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-argv.obj `if test -f '$(openvpn_srcdir)/argv.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/argv.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/argv.c'; fi`
+
+tls_crypt_testdriver-base64.o: $(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-base64.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-base64.Tpo -c -o tls_crypt_testdriver-base64.o `test -f '$(openvpn_srcdir)/base64.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-base64.Tpo $(DEPDIR)/tls_crypt_testdriver-base64.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/base64.c' object='tls_crypt_testdriver-base64.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-base64.o `test -f '$(openvpn_srcdir)/base64.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/base64.c
+
+tls_crypt_testdriver-base64.obj: $(openvpn_srcdir)/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-base64.obj -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-base64.Tpo -c -o tls_crypt_testdriver-base64.obj `if test -f '$(openvpn_srcdir)/base64.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/base64.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/base64.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-base64.Tpo $(DEPDIR)/tls_crypt_testdriver-base64.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/base64.c' object='tls_crypt_testdriver-base64.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-base64.obj `if test -f '$(openvpn_srcdir)/base64.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/base64.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/base64.c'; fi`
+
tls_crypt_testdriver-buffer.o: $(openvpn_srcdir)/buffer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-buffer.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-buffer.Tpo -c -o tls_crypt_testdriver-buffer.o `test -f '$(openvpn_srcdir)/buffer.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/buffer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-buffer.Tpo $(DEPDIR)/tls_crypt_testdriver-buffer.Po
@@ -918,6 +1766,20 @@ tls_crypt_testdriver-crypto_openssl.obj: $(openvpn_srcdir)/crypto_openssl.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-crypto_openssl.obj `if test -f '$(openvpn_srcdir)/crypto_openssl.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/crypto_openssl.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/crypto_openssl.c'; fi`
+tls_crypt_testdriver-env_set.o: $(openvpn_srcdir)/env_set.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-env_set.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-env_set.Tpo -c -o tls_crypt_testdriver-env_set.o `test -f '$(openvpn_srcdir)/env_set.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/env_set.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-env_set.Tpo $(DEPDIR)/tls_crypt_testdriver-env_set.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/env_set.c' object='tls_crypt_testdriver-env_set.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-env_set.o `test -f '$(openvpn_srcdir)/env_set.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/env_set.c
+
+tls_crypt_testdriver-env_set.obj: $(openvpn_srcdir)/env_set.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-env_set.obj -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-env_set.Tpo -c -o tls_crypt_testdriver-env_set.obj `if test -f '$(openvpn_srcdir)/env_set.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/env_set.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/env_set.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-env_set.Tpo $(DEPDIR)/tls_crypt_testdriver-env_set.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/env_set.c' object='tls_crypt_testdriver-env_set.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-env_set.obj `if test -f '$(openvpn_srcdir)/env_set.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/env_set.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/env_set.c'; fi`
+
tls_crypt_testdriver-otime.o: $(openvpn_srcdir)/otime.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-otime.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-otime.Tpo -c -o tls_crypt_testdriver-otime.o `test -f '$(openvpn_srcdir)/otime.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/otime.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-otime.Tpo $(DEPDIR)/tls_crypt_testdriver-otime.Po
@@ -960,6 +1822,20 @@ tls_crypt_testdriver-platform.obj: $(openvpn_srcdir)/platform.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-platform.obj `if test -f '$(openvpn_srcdir)/platform.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/platform.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/platform.c'; fi`
+tls_crypt_testdriver-run_command.o: $(openvpn_srcdir)/run_command.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-run_command.o -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-run_command.Tpo -c -o tls_crypt_testdriver-run_command.o `test -f '$(openvpn_srcdir)/run_command.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/run_command.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-run_command.Tpo $(DEPDIR)/tls_crypt_testdriver-run_command.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/run_command.c' object='tls_crypt_testdriver-run_command.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-run_command.o `test -f '$(openvpn_srcdir)/run_command.c' || echo '$(srcdir)/'`$(openvpn_srcdir)/run_command.c
+
+tls_crypt_testdriver-run_command.obj: $(openvpn_srcdir)/run_command.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -MT tls_crypt_testdriver-run_command.obj -MD -MP -MF $(DEPDIR)/tls_crypt_testdriver-run_command.Tpo -c -o tls_crypt_testdriver-run_command.obj `if test -f '$(openvpn_srcdir)/run_command.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/run_command.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/run_command.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tls_crypt_testdriver-run_command.Tpo $(DEPDIR)/tls_crypt_testdriver-run_command.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(openvpn_srcdir)/run_command.c' object='tls_crypt_testdriver-run_command.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tls_crypt_testdriver_CFLAGS) $(CFLAGS) -c -o tls_crypt_testdriver-run_command.obj `if test -f '$(openvpn_srcdir)/run_command.c'; then $(CYGPATH_W) '$(openvpn_srcdir)/run_command.c'; else $(CYGPATH_W) '$(srcdir)/$(openvpn_srcdir)/run_command.c'; fi`
+
mostlyclean-libtool:
-rm -f *.lo
@@ -1188,27 +2064,71 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
distclean: distclean-am
-rm -f ./$(DEPDIR)/argv_testdriver-argv.Po
-rm -f ./$(DEPDIR)/argv_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/argv_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/argv_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/argv_testdriver-platform.Po
-rm -f ./$(DEPDIR)/argv_testdriver-test_argv.Po
- -rm -f ./$(DEPDIR)/buffer_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-base64.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-test_auth_token.Po
+ -rm -f ./$(DEPDIR)/buffer_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-platform.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-test_buffer.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-test_crypto.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-test_ncp.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-networking_sitnl.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-test_networking.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/packet_id_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-otime.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-packet_id.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-platform.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-test_packet_id.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-argv.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-base64.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-buffer.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto_mbedtls.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-env_set.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-otime.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-packet_id.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-run_command.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-test_tls_crypt.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
@@ -1257,27 +2177,71 @@ installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/argv_testdriver-argv.Po
-rm -f ./$(DEPDIR)/argv_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/argv_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/argv_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/argv_testdriver-platform.Po
-rm -f ./$(DEPDIR)/argv_testdriver-test_argv.Po
- -rm -f ./$(DEPDIR)/buffer_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-base64.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/auth_token_testdriver-test_auth_token.Po
+ -rm -f ./$(DEPDIR)/buffer_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-platform.Po
-rm -f ./$(DEPDIR)/buffer_testdriver-test_buffer.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/crypto_testdriver-test_crypto.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/ncp_testdriver-test_ncp.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto_mbedtls.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-mock_msg.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-networking_sitnl.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-otime.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-packet_id.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/networking_testdriver-test_networking.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-buffer.Po
+ -rm -f ./$(DEPDIR)/packet_id_testdriver-mock_get_random.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-otime.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-packet_id.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-platform.Po
-rm -f ./$(DEPDIR)/packet_id_testdriver-test_packet_id.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-argv.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-base64.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-buffer.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto_mbedtls.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-crypto_openssl.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-env_set.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-mock_msg.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-otime.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-packet_id.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-platform.Po
+ -rm -f ./$(DEPDIR)/tls_crypt_testdriver-run_command.Po
-rm -f ./$(DEPDIR)/tls_crypt_testdriver-test_tls_crypt.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/tests/unit_tests/openvpn/mock_get_random.c b/tests/unit_tests/openvpn/mock_get_random.c
new file mode 100644
index 0000000..d0d2574
--- /dev/null
+++ b/tests/unit_tests/openvpn/mock_get_random.c
@@ -0,0 +1,36 @@
+/*
+ * 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) 2017-2021 Fox Crypto B.V. <openvpn@foxcrypto.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.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+unsigned long
+get_random(void)
+{
+ /* rand() is not very random, but it's C99 and this is just for testing */
+ return rand();
+}
diff --git a/tests/unit_tests/openvpn/mock_msg.c b/tests/unit_tests/openvpn/mock_msg.c
index 140e637..3ede98c 100644
--- a/tests/unit_tests/openvpn/mock_msg.c
+++ b/tests/unit_tests/openvpn/mock_msg.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/tests/unit_tests/openvpn/mock_msg.h b/tests/unit_tests/openvpn/mock_msg.h
index 53cae26..be5f2e5 100644
--- a/tests/unit_tests/openvpn/mock_msg.h
+++ b/tests/unit_tests/openvpn/mock_msg.h
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
diff --git a/tests/unit_tests/openvpn/test_argv.c b/tests/unit_tests/openvpn/test_argv.c
index 0fdd3f0..3dc470a 100644
--- a/tests/unit_tests/openvpn/test_argv.c
+++ b/tests/unit_tests/openvpn/test_argv.c
@@ -9,6 +9,7 @@
#include <setjmp.h>
#include <cmocka.h>
#include <assert.h>
+#include <stdbool.h>
#include "argv.h"
#include "buffer.h"
@@ -38,7 +39,7 @@ argv_printf__multiple_spaces_in_format__parsed_as_one(void **state)
argv_printf(&a, " %s %s %d ", PATH1, PATH2, 42);
assert_int_equal(a.argc, 3);
- argv_reset(&a);
+ argv_free(&a);
}
static void
@@ -50,7 +51,30 @@ argv_printf_cat__multiple_spaces_in_format__parsed_as_one(void **state)
argv_printf_cat(&a, " %s %s", PATH2, PARAM1);
assert_int_equal(a.argc, 3);
- argv_reset(&a);
+ argv_free(&a);
+}
+
+static void
+argv_printf__embedded_format_directive__replaced_in_output(void **state)
+{
+ struct argv a = argv_new();
+
+ argv_printf(&a, "<p1:%s>", PATH1);
+ assert_int_equal(a.argc, 1);
+ assert_string_equal(a.argv[0], "<p1:" PATH1 ">");
+
+ argv_free(&a);
+}
+
+static void
+argv_printf__group_sep_in_arg__fail_no_ouput(void **state)
+{
+ struct argv a = argv_new();
+
+ assert_false(argv_printf(&a, "tool --do %s", "this\035--harmful"));
+ assert_int_equal(a.argc, 0);
+
+ argv_free(&a);
}
static void
@@ -58,16 +82,61 @@ argv_printf__combined_path_with_spaces__argc_correct(void **state)
{
struct argv a = argv_new();
- argv_printf(&a, "%s%sc", PATH1, PATH2);
+ argv_printf(&a, "%s%s", PATH1, PATH2);
assert_int_equal(a.argc, 1);
- argv_printf(&a, "%s%sc %d", PATH1, PATH2, 42);
+ argv_printf(&a, "%s%s %d", PATH1, PATH2, 42);
assert_int_equal(a.argc, 2);
- argv_printf(&a, "foo %s%sc %s x y", PATH2, PATH1, "foo");
+ argv_printf(&a, "foo %s%s %s x y", PATH2, PATH1, "foo");
assert_int_equal(a.argc, 5);
- argv_reset(&a);
+ argv_free(&a);
+}
+
+static void
+argv_printf__empty_parameter__argc_correct(void **state)
+{
+ struct argv a = argv_new();
+
+ argv_printf(&a, "%s", "");
+ assert_int_equal(a.argc, 1);
+
+ argv_printf(&a, "%s %s", PATH1, "");
+ assert_int_equal(a.argc, 2);
+
+ argv_printf(&a, "%s %s %s", PATH1, "", PARAM1);
+ assert_int_equal(a.argc, 3);
+
+ argv_printf(&a, "%s %s %s %s", PATH1, "", "", PARAM1);
+ assert_int_equal(a.argc, 4);
+
+ argv_printf(&a, "%s %s", "", PARAM1);
+ assert_int_equal(a.argc, 2);
+
+ argv_free(&a);
+}
+
+static void
+argv_printf__long_args__data_correct(void **state)
+{
+ int i;
+ struct argv a = argv_new();
+ const char *args[] = {
+ "good_tools_have_good_names_even_though_it_might_impair_typing",
+ "--long-opt=looooooooooooooooooooooooooooooooooooooooooooooooong",
+ "--long-cat=loooooooooooooooooooooooooooooooooooooooooooooooooooonger",
+ "file_with_very_descriptive_filename_that_leaves_no_questions_open.jpg.exe"
+ };
+
+ argv_printf(&a, "%s %s %s %s", args[0], args[1], args[2], args[3]);
+ assert_int_equal(a.argc, 4);
+ for (i = 0; i < a.argc; i++)
+ {
+ assert_string_equal(a.argv[i], args[i]);
+ }
+
+ argv_free(&a);
}
static void
@@ -78,7 +147,7 @@ argv_parse_cmd__command_string__argc_correct(void **state)
argv_parse_cmd(&a, SCRIPT_CMD);
assert_int_equal(a.argc, 3);
- argv_reset(&a);
+ argv_free(&a);
}
static void
@@ -90,7 +159,7 @@ argv_parse_cmd__command_and_extra_options__argc_correct(void **state)
argv_printf_cat(&a, "bar baz %d %s", 42, PATH1);
assert_int_equal(a.argc, 7);
- argv_reset(&a);
+ argv_free(&a);
}
static void
@@ -103,7 +172,21 @@ argv_printf_cat__used_twice__argc_correct(void **state)
argv_printf_cat(&a, "foo");
assert_int_equal(a.argc, 5);
- argv_reset(&a);
+ argv_free(&a);
+}
+
+static void
+argv_str__empty_argv__empty_output(void **state)
+{
+ struct argv a = argv_new();
+ struct gc_arena gc = gc_new();
+ const char *output;
+
+ output = argv_str(&a, &gc, PA_BRACKET);
+ assert_string_equal(output, "");
+
+ argv_free(&a);
+ gc_free(&gc);
}
static void
@@ -113,7 +196,7 @@ argv_str__multiple_argv__correct_output(void **state)
struct gc_arena gc = gc_new();
const char *output;
- argv_printf(&a, "%s%sc", PATH1, PATH2);
+ argv_printf(&a, "%s%s", PATH1, PATH2);
argv_printf_cat(&a, "%s", PARAM1);
argv_printf_cat(&a, "%s", PARAM2);
argv_printf_cat(&a, "%d", -1);
@@ -121,9 +204,9 @@ argv_str__multiple_argv__correct_output(void **state)
argv_printf_cat(&a, "%lu", 1L );
output = argv_str(&a, &gc, PA_BRACKET);
assert_string_equal(output, "[" PATH1 PATH2 "] [" PARAM1 "] [" PARAM2 "]"
- " [-1] [4294967295] [1]");
+ " [-1] [4294967295] [1]");
- argv_reset(&a);
+ argv_free(&a);
gc_free(&gc);
}
@@ -136,9 +219,9 @@ argv_insert_head__empty_argv__head_only(void **state)
b = argv_insert_head(&a, PATH1);
assert_int_equal(b.argc, 1);
assert_string_equal(b.argv[0], PATH1);
- argv_reset(&b);
+ argv_free(&b);
- argv_reset(&a);
+ argv_free(&a);
}
static void
@@ -151,7 +234,8 @@ argv_insert_head__non_empty_argv__head_added(void **state)
argv_printf(&a, "%s", PATH2);
b = argv_insert_head(&a, PATH1);
assert_int_equal(b.argc, a.argc + 1);
- for (i = 0; i < b.argc; i++) {
+ for (i = 0; i < b.argc; i++)
+ {
if (i == 0)
{
assert_string_equal(b.argv[i], PATH1);
@@ -161,9 +245,9 @@ argv_insert_head__non_empty_argv__head_added(void **state)
assert_string_equal(b.argv[i], a.argv[i - 1]);
}
}
- argv_reset(&b);
+ argv_free(&b);
- argv_reset(&a);
+ argv_free(&a);
}
int
@@ -172,10 +256,15 @@ main(void)
const struct CMUnitTest tests[] = {
cmocka_unit_test(argv_printf__multiple_spaces_in_format__parsed_as_one),
cmocka_unit_test(argv_printf_cat__multiple_spaces_in_format__parsed_as_one),
+ cmocka_unit_test(argv_printf__embedded_format_directive__replaced_in_output),
+ cmocka_unit_test(argv_printf__group_sep_in_arg__fail_no_ouput),
cmocka_unit_test(argv_printf__combined_path_with_spaces__argc_correct),
+ cmocka_unit_test(argv_printf__empty_parameter__argc_correct),
+ cmocka_unit_test(argv_printf__long_args__data_correct),
cmocka_unit_test(argv_parse_cmd__command_string__argc_correct),
cmocka_unit_test(argv_parse_cmd__command_and_extra_options__argc_correct),
cmocka_unit_test(argv_printf_cat__used_twice__argc_correct),
+ cmocka_unit_test(argv_str__empty_argv__empty_output),
cmocka_unit_test(argv_str__multiple_argv__correct_output),
cmocka_unit_test(argv_insert_head__non_empty_argv__head_added),
};
diff --git a/tests/unit_tests/openvpn/test_auth_token.c b/tests/unit_tests/openvpn/test_auth_token.c
new file mode 100644
index 0000000..6bfddf0
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_auth_token.c
@@ -0,0 +1,397 @@
+/*
+ * 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) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "auth_token.c"
+
+#include "mock_msg.h"
+
+struct test_context {
+ struct tls_multi multi;
+ struct key_type kt;
+ struct user_pass up;
+ struct tls_session *session;
+};
+
+/* Dummy functions that do nothing to mock the functionality */
+void
+send_push_reply_auth_token(struct tls_multi *multi)
+{
+}
+
+void
+auth_set_client_reason(struct tls_multi *multi, const char *reason)
+{
+
+}
+
+static const char *now0key0 = "SESS_ID_AT_0123456789abcdefAAAAAAAAAAAAAAAAAAAAAE5JsQJOVfo8jnI3RL3tBaR5NkE4yPfcylFUHmHSc5Bu";
+
+static const char *zeroinline = "-----BEGIN OpenVPN auth-token server key-----\n"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"
+ "-----END OpenVPN auth-token server key-----";
+
+static const char *allx01inline = "-----BEGIN OpenVPN auth-token server key-----\n"
+ "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n"
+ "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\n"
+ "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=\n"
+ "-----END OpenVPN auth-token server key-----";
+
+static const char *random_key = "-----BEGIN OpenVPN auth-token server key-----\n"
+ "+mmmf7IQ5cymtMVjKYTWk8IOcYanRlpQmV9Tb3EjkHYxueBVDg3yqRgzeBlVGzNLD//rAPiOVhau\n"
+ "3NDBjNOQB8951bfs7Cc2mYfay92Bh2gRJ5XEM/DMfzCWN+7uU6NWoTTHr4FuojnIQtjtqVAj/JS9\n"
+ "w+dTSp/vYHl+c7uHd19uVRu/qLqV85+rm4tUGIjO7FfYuwyPqwmhuIsi3hs9QkSimh888FmBpoKY\n"
+ "/tbKVTJZmSERKti9KEwtV2eVAR0znN5KW7lCB3mHVAhN7bUpcoDjfCzYIFARxwswTFu9gFkwqUMY\n"
+ "I1KUOgIsVNs4llACioeXplYekWETR+YkJwDc/A==\n"
+ "-----END OpenVPN auth-token server key-----";
+
+static const char *random_token = "SESS_ID_AT_ThhRItzOKNKrh3dfAAAAAFwzHpwAAAAAXDMenDdrq0RoH3dkA1f7O3wO+7kZcx2DusVZrRmFlWQM9HOb";
+
+
+static int
+setup(void **state)
+{
+ struct test_context *ctx = calloc(1, sizeof(*ctx));
+ *state = ctx;
+
+ struct key key = { 0 };
+
+ ctx->kt = auth_token_kt();
+ if (!ctx->kt.digest)
+ {
+ return 0;
+ }
+ ctx->multi.opt.auth_token_generate = true;
+ ctx->multi.opt.auth_token_lifetime = 3000;
+ ctx->session = &ctx->multi.session[TM_ACTIVE];
+
+ ctx->session->opt = calloc(1, sizeof(struct tls_options));
+ ctx->session->opt->renegotiate_seconds = 120;
+ ctx->session->opt->auth_token_lifetime = 3000;
+
+ strcpy(ctx->up.username, "test user name");
+ strcpy(ctx->up.password, "ignored");
+
+ init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
+
+ now = 0;
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ wipe_auth_token(&ctx->multi);
+
+ free(ctx->session->opt);
+ free(ctx);
+
+ return 0;
+}
+
+static void
+auth_token_basic_test(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ generate_auth_token(&ctx->up, &ctx->multi);
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+}
+
+static void
+auth_token_fail_invalid_key(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ generate_auth_token(&ctx->up, &ctx->multi);
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+ /* Change auth-token key */
+ struct key key;
+ memset(&key, '1', sizeof(key));
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
+
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session), 0);
+
+ /* Load original test key again */
+ memset(&key, 0, sizeof(key));
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ init_key_ctx(&ctx->multi.opt.auth_token_key, &key, &ctx->kt, false, "TEST");
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+}
+
+static void
+auth_token_test_timeout(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ now = 100000;
+ generate_auth_token(&ctx->up, &ctx->multi);
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+
+ /* No time has passed */
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+ /* Token before validity, should be rejected */
+ now = 100000 - 100;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED);
+
+ /* Token still in validity, should be accepted */
+ now = 100000 + 2*ctx->session->opt->renegotiate_seconds - 20;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+ /* Token past validity, should be rejected */
+ now = 100000 + 2*ctx->session->opt->renegotiate_seconds + 20;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED);
+
+ /* Check if the mode for a client that never updates its token works */
+ ctx->multi.auth_token_initial = strdup(ctx->up.password);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+ /* But not when we reached our timeout */
+ now = 100000 + ctx->session->opt->auth_token_lifetime + 1;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED);
+
+ free(ctx->multi.auth_token_initial);
+ ctx->multi.auth_token_initial = NULL;
+
+ /* regenerate the token util it hits the expiry */
+ now = 100000;
+ while (now < 100000 + ctx->session->opt->auth_token_lifetime + 1)
+ {
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+ generate_auth_token(&ctx->up, &ctx->multi);
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ now += ctx->session->opt->renegotiate_seconds;
+ }
+
+
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED);
+ ctx->multi.opt.auth_token_lifetime = 0;
+
+ /* Non expiring token should be fine */
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+}
+
+static void
+zerohmac(char *token)
+{
+ char *hmacstart = token + AUTH_TOKEN_SESSION_ID_LEN
+ + strlen(SESSION_ID_PREFIX) + 2*sizeof(uint64_t);
+ memset(hmacstart, 0x8d, strlen(hmacstart));
+}
+
+static void
+auth_token_test_known_keys(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ now = 0;
+ /* Preload the session id so the same session id is used here */
+ ctx->multi.auth_token = strdup(now0key0);
+
+ /* Zero the hmac part to ensure we have a newly generated token */
+ zerohmac(ctx->multi.auth_token);
+
+ generate_auth_token(&ctx->up, &ctx->multi);
+
+ assert_string_equal(now0key0, ctx->multi.auth_token);
+
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+}
+
+static const char *lastsesion_statevalue;
+void
+setenv_str(struct env_set *es, const char *name, const char *value)
+{
+ if (streq(name, "session_state"))
+ {
+ lastsesion_statevalue = value;
+ }
+}
+
+static void
+auth_token_test_empty_user(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ CLEAR(ctx->up.username);
+ now = 0;
+
+ generate_auth_token(&ctx->up, &ctx->multi);
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK);
+
+ now = 100000;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED);
+ strcpy(ctx->up.username, "test user name");
+
+ now = 0;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_VALID_EMPTYUSER);
+
+ strcpy(ctx->up.username, "test user name");
+ now = 100000;
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED|AUTH_TOKEN_VALID_EMPTYUSER);
+
+ zerohmac(ctx->up.password);
+ assert_int_equal(verify_auth_token(&ctx->up, &ctx->multi, ctx->session),
+ 0);
+}
+
+static void
+auth_token_test_env(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ struct key_state *ks = &ctx->multi.session[TM_ACTIVE].key[KS_PRIMARY];
+
+ ks->auth_token_state_flags = 0;
+ ctx->multi.auth_token = NULL;
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "Initial");
+
+ ks->auth_token_state_flags = 0;
+ strcpy(ctx->up.password, now0key0);
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "Invalid");
+
+ ks->auth_token_state_flags = AUTH_TOKEN_HMAC_OK;
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "Authenticated");
+
+ ks->auth_token_state_flags = AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED;
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "Expired");
+
+ ks->auth_token_state_flags = AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_VALID_EMPTYUSER;
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "AuthenticatedEmptyUser");
+
+ ks->auth_token_state_flags = AUTH_TOKEN_HMAC_OK|AUTH_TOKEN_EXPIRED|AUTH_TOKEN_VALID_EMPTYUSER;
+ add_session_token_env(ctx->session, &ctx->multi, &ctx->up);
+ assert_string_equal(lastsesion_statevalue, "ExpiredEmptyUser");
+}
+
+static void
+auth_token_test_random_keys(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ now = 0x5c331e9c;
+ /* Preload the session id so the same session id is used here */
+ ctx->multi.auth_token = strdup(random_token);
+
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ auth_token_init_secret(&ctx->multi.opt.auth_token_key, random_key, true);
+
+ /* Zero the hmac part to ensure we have a newly generated token */
+ zerohmac(ctx->multi.auth_token);
+
+ generate_auth_token(&ctx->up, &ctx->multi);
+
+ assert_string_equal(random_token, ctx->multi.auth_token);
+
+ strcpy(ctx->up.password, ctx->multi.auth_token);
+ assert_true(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
+}
+
+
+static void
+auth_token_test_key_load(void **state)
+{
+ struct test_context *ctx = (struct test_context *) *state;
+
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ auth_token_init_secret(&ctx->multi.opt.auth_token_key, zeroinline, true);
+ strcpy(ctx->up.password, now0key0);
+ assert_true(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
+
+ free_key_ctx(&ctx->multi.opt.auth_token_key);
+ auth_token_init_secret(&ctx->multi.opt.auth_token_key, allx01inline, true);
+ assert_false(verify_auth_token(&ctx->up, &ctx->multi, ctx->session));
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(auth_token_basic_test, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_fail_invalid_key, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_known_keys, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_empty_user, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_env, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_random_keys, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_key_load, setup, teardown),
+ cmocka_unit_test_setup_teardown(auth_token_test_timeout, setup, teardown),
+ };
+
+#if defined(ENABLE_CRYPTO_OPENSSL)
+ OpenSSL_add_all_algorithms();
+#endif
+
+ int ret = cmocka_run_group_tests_name("auth-token tests", tests, NULL, NULL);
+
+ return ret;
+}
diff --git a/tests/unit_tests/openvpn/test_buffer.c b/tests/unit_tests/openvpn/test_buffer.c
index d083b78..5e854c2 100644
--- a/tests/unit_tests/openvpn/test_buffer.c
+++ b/tests/unit_tests/openvpn/test_buffer.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -33,6 +33,7 @@
#include <cmocka.h>
#include "buffer.h"
+#include "buffer.c"
static void
test_buffer_strprefix(void **state)
@@ -62,7 +63,8 @@ struct test_buffer_list_aggregate_ctx {
struct buffer_list *empty_buffers;
};
-static int test_buffer_list_setup(void **state)
+static int
+test_buffer_list_setup(void **state)
{
struct test_buffer_list_aggregate_ctx *ctx = calloc(1, sizeof(*ctx));
ctx->empty = buffer_list_new(0);
@@ -85,7 +87,8 @@ static int test_buffer_list_setup(void **state)
return 0;
}
-static int test_buffer_list_teardown(void **state)
+static int
+test_buffer_list_teardown(void **state)
{
struct test_buffer_list_aggregate_ctx *ctx = *state;
@@ -197,6 +200,48 @@ test_buffer_list_aggregate_separator_emptybuffers(void **state)
assert_int_equal(BLEN(buf), 0);
}
+static void
+test_buffer_free_gc_one(void **state)
+{
+ struct gc_arena gc = gc_new();
+ struct buffer buf = alloc_buf_gc(1024, &gc);
+
+ assert_ptr_equal(gc.list + 1, buf.data);
+ free_buf_gc(&buf, &gc);
+ assert_null(gc.list);
+
+ gc_free(&gc);
+}
+
+static void
+test_buffer_free_gc_two(void **state)
+{
+ struct gc_arena gc = gc_new();
+ struct buffer buf1 = alloc_buf_gc(1024, &gc);
+ struct buffer buf2 = alloc_buf_gc(1024, &gc);
+ struct buffer buf3 = alloc_buf_gc(1024, &gc);
+
+ struct gc_entry *e;
+
+ e = gc.list;
+
+ assert_ptr_equal(e + 1, buf3.data);
+ assert_ptr_equal(e->next + 1, buf2.data);
+ assert_ptr_equal(e->next->next + 1, buf1.data);
+
+ free_buf_gc(&buf2, &gc);
+
+ assert_non_null(gc.list);
+
+ while (e)
+ {
+ assert_ptr_not_equal(e + 1, buf2.data);
+ e = e->next;
+ }
+
+ gc_free(&gc);
+}
+
int
main(void)
{
@@ -226,6 +271,8 @@ main(void)
cmocka_unit_test_setup_teardown(test_buffer_list_aggregate_separator_emptybuffers,
test_buffer_list_setup,
test_buffer_list_teardown),
+ cmocka_unit_test(test_buffer_free_gc_one),
+ cmocka_unit_test(test_buffer_free_gc_two),
};
return cmocka_run_group_tests_name("buffer", tests, NULL, NULL);
diff --git a/tests/unit_tests/openvpn/test_crypto.c b/tests/unit_tests/openvpn/test_crypto.c
new file mode 100644
index 0000000..baaaa92
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_crypto.c
@@ -0,0 +1,158 @@
+/*
+ * 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) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "crypto.h"
+
+#include "mock_msg.h"
+
+static const char testtext[] = "Dummy text to test PEM encoding";
+
+static void
+crypto_pem_encode_decode_loopback(void **state)
+{
+ struct gc_arena gc = gc_new();
+ struct buffer src_buf;
+ buf_set_read(&src_buf, (void *)testtext, sizeof(testtext));
+
+ uint8_t dec[sizeof(testtext)];
+ struct buffer dec_buf;
+ buf_set_write(&dec_buf, dec, sizeof(dec));
+
+ struct buffer pem_buf;
+
+ assert_true(crypto_pem_encode("TESTKEYNAME", &pem_buf, &src_buf, &gc));
+ assert_true(BLEN(&src_buf) < BLEN(&pem_buf));
+
+ /* Wrong key name */
+ assert_false(crypto_pem_decode("WRONGNAME", &dec_buf, &pem_buf));
+
+ assert_true(crypto_pem_decode("TESTKEYNAME", &dec_buf, &pem_buf));
+ assert_int_equal(BLEN(&src_buf), BLEN(&dec_buf));
+ assert_memory_equal(BPTR(&src_buf), BPTR(&dec_buf), BLEN(&src_buf));
+
+ gc_free(&gc);
+}
+
+static void
+test_translate_cipher(const char *ciphername, const char *openvpn_name)
+{
+ const cipher_kt_t *cipher = cipher_kt_get(ciphername);
+
+ /* Empty cipher is fine */
+ if (!cipher)
+ {
+ return;
+ }
+
+ const char *kt_name = cipher_kt_name(cipher);
+
+ assert_string_equal(kt_name, openvpn_name);
+}
+
+static void
+test_cipher_names(const char *ciphername, const char *openvpn_name)
+{
+ struct gc_arena gc = gc_new();
+ /* Go through some variants, if the cipher library accepts these, they
+ * should be normalised to the openvpn name */
+ char *upper = string_alloc(ciphername, &gc);
+ char *lower = string_alloc(ciphername, &gc);
+ char *random_case = string_alloc(ciphername, &gc);
+
+ for (int i = 0; i < strlen(ciphername); i++)
+ {
+ upper[i] = toupper(ciphername[i]);
+ lower[i] = tolower(ciphername[i]);
+ if (rand() & 0x1)
+ {
+ random_case[i] = upper[i];
+ }
+ else
+ {
+ random_case[i] = lower[i];
+ }
+ }
+
+ if (!openvpn_name)
+ {
+ openvpn_name = upper;
+ }
+
+ test_translate_cipher(upper, openvpn_name);
+ test_translate_cipher(lower, openvpn_name);
+ test_translate_cipher(random_case, openvpn_name);
+ test_translate_cipher(ciphername, openvpn_name);
+
+
+ gc_free(&gc);
+}
+
+static void
+crypto_translate_cipher_names(void **state)
+{
+ /* Test that a number of ciphers to see that they turn out correctly */
+ test_cipher_names("BF-CBC", NULL);
+ test_cipher_names("BLOWFISH-CBC", "BF-CBC");
+ test_cipher_names("Chacha20-Poly1305", NULL);
+ test_cipher_names("AES-128-GCM", NULL);
+ test_cipher_names("AES-128-CBC", NULL);
+ test_cipher_names("CAMELLIA-128-CFB128", "CAMELLIA-128-CFB");
+ test_cipher_names("id-aes256-GCM", "AES-256-GCM");
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(crypto_pem_encode_decode_loopback),
+ cmocka_unit_test(crypto_translate_cipher_names),
+ };
+
+#if defined(ENABLE_CRYPTO_OPENSSL)
+ OpenSSL_add_all_algorithms();
+#endif
+
+ int ret = cmocka_run_group_tests_name("crypto tests", tests, NULL, NULL);
+
+#if defined(ENABLE_CRYPTO_OPENSSL)
+ EVP_cleanup();
+#endif
+
+ return ret;
+}
diff --git a/tests/unit_tests/openvpn/test_ncp.c b/tests/unit_tests/openvpn/test_ncp.c
new file mode 100644
index 0000000..494a028
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_ncp.c
@@ -0,0 +1,241 @@
+/*
+ * 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) 2019-2021 Arne Schwabe <arne@rfc2549.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "ssl_ncp.c"
+
+/* Defines for use in the tests and the mock parse_line() */
+
+const char *bf_chacha = "BF-CBC:CHACHA20-POLY1305";
+const char *aes_ciphers = "AES-256-GCM:AES-128-GCM";
+
+static void
+test_check_ncp_ciphers_list(void **state)
+{
+ struct gc_arena gc = gc_new();
+ bool have_chacha = cipher_kt_get("CHACHA20-POLY1305");
+
+ assert_string_equal(mutate_ncp_cipher_list("none", &gc), "none");
+ assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:none", &gc),
+ "AES-256-GCM:none");
+
+ assert_string_equal(mutate_ncp_cipher_list(aes_ciphers, &gc), aes_ciphers);
+
+ if (have_chacha)
+ {
+ assert_string_equal(mutate_ncp_cipher_list(bf_chacha, &gc), bf_chacha);
+ assert_string_equal(mutate_ncp_cipher_list("BF-CBC:CHACHA20-POLY1305", &gc),
+ bf_chacha);
+ }
+ else
+ {
+ assert_ptr_equal(mutate_ncp_cipher_list(bf_chacha, &gc), NULL);
+ }
+
+ /* For testing that with OpenSSL 1.1.0+ that also accepts ciphers in
+ * a different spelling the normalised cipher output is the same */
+ bool have_chacha_mixed_case = cipher_kt_get("ChaCha20-Poly1305");
+ if (have_chacha_mixed_case)
+ {
+ assert_string_equal(mutate_ncp_cipher_list("BF-CBC:ChaCha20-Poly1305", &gc),
+ bf_chacha);
+ }
+
+ assert_ptr_equal(mutate_ncp_cipher_list("vollbit", &gc), NULL);
+ assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:vollbit", &gc), NULL);
+ assert_ptr_equal(mutate_ncp_cipher_list("", &gc), NULL);
+
+ assert_ptr_equal(mutate_ncp_cipher_list(
+ "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
+ "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
+ "ChaCha20-Poly1305", &gc), NULL);
+
+#ifdef ENABLE_CRYPTO_OPENSSL
+ assert_string_equal(mutate_ncp_cipher_list("id-aes128-GCM:id-aes256-GCM",
+ &gc), "AES-128-GCM:AES-256-GCM");
+#else
+ assert_string_equal(mutate_ncp_cipher_list("BLOWFISH-CBC",
+ &gc), "BF-CBC");
+#endif
+ gc_free(&gc);
+}
+
+static void
+test_extract_client_ciphers(void **state)
+{
+ struct gc_arena gc = gc_new();
+ const char *client_peer_info;
+ const char *peer_list;
+
+ client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2";
+ peer_list = tls_peer_ncp_list(client_peer_info, &gc);
+ assert_string_equal(aes_ciphers,peer_list);
+ assert_true(tls_peer_supports_ncp(client_peer_info));
+
+ client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2\nIV_CIPHERS=BF-CBC";
+ peer_list = tls_peer_ncp_list(client_peer_info, &gc);
+ assert_string_equal("BF-CBC", peer_list);
+ assert_true(tls_peer_supports_ncp(client_peer_info));
+
+ client_peer_info = "IV_NCP=2\nIV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
+ peer_list = tls_peer_ncp_list(client_peer_info, &gc);
+ assert_string_equal("BF-CBC:FOO-BAR", peer_list);
+ assert_true(tls_peer_supports_ncp(client_peer_info));
+
+ client_peer_info = "IV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
+ peer_list = tls_peer_ncp_list(client_peer_info, &gc);
+ assert_string_equal("BF-CBC:FOO-BAR", peer_list);
+ assert_true(tls_peer_supports_ncp(client_peer_info));
+
+ client_peer_info = "IV_YOLO=NO\nIV_BAR=7";
+ peer_list = tls_peer_ncp_list(client_peer_info, &gc);
+ assert_string_equal("", peer_list);
+ assert_false(tls_peer_supports_ncp(client_peer_info));
+
+ peer_list = tls_peer_ncp_list(NULL, &gc);
+ assert_string_equal("", peer_list);
+ assert_false(tls_peer_supports_ncp(client_peer_info));
+
+ gc_free(&gc);
+}
+
+static void
+test_poor_man(void **state)
+{
+ struct gc_arena gc = gc_new();
+ char *best_cipher;
+
+ const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM";
+ const char *serverlistbfcbc = "CHACHA20_POLY1305:AES-128-GCM:BF-CBC:none";
+
+ best_cipher = ncp_get_best_cipher(serverlist,
+ "IV_YOLO=NO\nIV_BAR=7",
+ "BF-CBC", &gc);
+
+ assert_ptr_equal(best_cipher, NULL);
+
+
+ best_cipher = ncp_get_best_cipher(serverlistbfcbc,
+ "IV_YOLO=NO\nIV_BAR=7",
+ "BF-CBC", &gc);
+
+ assert_string_equal(best_cipher, "BF-CBC");
+
+
+ best_cipher = ncp_get_best_cipher(serverlist,
+ "IV_NCP=1\nIV_BAR=7",
+ "AES-128-GCM", &gc);
+
+ assert_string_equal(best_cipher, "AES-128-GCM");
+
+ best_cipher = ncp_get_best_cipher(serverlist, NULL,
+ "AES-128-GCM", &gc);
+
+ assert_string_equal(best_cipher, "AES-128-GCM");
+
+ best_cipher = ncp_get_best_cipher(serverlist, NULL,
+ "none", &gc);
+ assert_ptr_equal(best_cipher, NULL);
+
+ best_cipher = ncp_get_best_cipher(serverlistbfcbc, NULL,
+ "none", &gc);
+ assert_string_equal(best_cipher, "none");
+
+ best_cipher = ncp_get_best_cipher(serverlist, NULL,NULL, &gc);
+ assert_ptr_equal(best_cipher, NULL);
+
+ gc_free(&gc);
+}
+
+
+static void
+test_ncp_best(void **state)
+{
+ struct gc_arena gc = gc_new();
+ char *best_cipher;
+
+ const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM:AES-256-GCM";
+
+ best_cipher = ncp_get_best_cipher(serverlist,
+ "IV_YOLO=NO\nIV_NCP=2\nIV_BAR=7",
+ "BF-CBC", &gc);
+
+ assert_string_equal(best_cipher, "AES-128-GCM");
+
+ /* Best cipher is in --cipher of client */
+ best_cipher = ncp_get_best_cipher(serverlist, "IV_NCP=2\nIV_BAR=7",
+ "CHACHA20_POLY1305", &gc);
+
+ assert_string_equal(best_cipher, "CHACHA20_POLY1305");
+
+ /* Best cipher is in --cipher of client */
+ best_cipher = ncp_get_best_cipher(serverlist, "IV_CIPHERS=AES-128-GCM",
+ "AES-256-CBC", &gc);
+
+
+ assert_string_equal(best_cipher, "AES-128-GCM");
+
+ /* IV_NCP=2 should be ignored if IV_CIPHERS is sent */
+ best_cipher = ncp_get_best_cipher(serverlist,
+ "IV_FOO=7\nIV_CIPHERS=AES-256-GCM\nIV_NCP=2",
+ "AES-256-CBC", &gc);
+
+ assert_string_equal(best_cipher, "AES-256-GCM");
+
+
+ gc_free(&gc);
+}
+
+
+
+const struct CMUnitTest ncp_tests[] = {
+ cmocka_unit_test(test_check_ncp_ciphers_list),
+ cmocka_unit_test(test_extract_client_ciphers),
+ cmocka_unit_test(test_poor_man),
+ cmocka_unit_test(test_ncp_best)
+};
+
+
+int
+main(void)
+{
+#if defined(ENABLE_CRYPTO_OPENSSL)
+ OpenSSL_add_all_algorithms();
+#endif
+ return cmocka_run_group_tests(ncp_tests, NULL, NULL);
+}
diff --git a/tests/unit_tests/openvpn/test_networking.c b/tests/unit_tests/openvpn/test_networking.c
new file mode 100644
index 0000000..9e9744f
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_networking.c
@@ -0,0 +1,253 @@
+#include "config.h"
+#include "syshead.h"
+#include "networking.h"
+
+
+static char *iface = "ovpn-dummy0";
+
+static int
+net__iface_up(bool up)
+{
+ printf("CMD: ip link set %s %s\n", iface, up ? "up" : "down");
+
+ return net_iface_up(NULL, iface, up);
+}
+
+static int
+net__iface_mtu_set(int mtu)
+{
+ printf("CMD: ip link set %s mtu %d\n", iface, mtu);
+
+ return net_iface_mtu_set(NULL, iface, mtu);
+}
+
+static int
+net__addr_v4_add(const char *addr_str, int prefixlen)
+{
+ in_addr_t addr;
+ int ret;
+
+ ret = inet_pton(AF_INET, addr_str, &addr);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ addr = ntohl(addr);
+
+ printf("CMD: ip addr add %s/%d dev %s\n", addr_str, prefixlen, iface);
+
+ return net_addr_v4_add(NULL, iface, &addr, prefixlen);
+}
+
+static int
+net__addr_v6_add(const char *addr_str, int prefixlen)
+{
+ struct in6_addr addr;
+ int ret;
+
+ ret = inet_pton(AF_INET6, addr_str, &addr);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ printf("CMD: ip -6 addr add %s/%d dev %s\n", addr_str, prefixlen, iface);
+
+ return net_addr_v6_add(NULL, iface, &addr, prefixlen);
+}
+
+static int
+net__route_v4_add(const char *dst_str, int prefixlen, int metric)
+{
+ in_addr_t dst;
+ int ret;
+
+ if (!dst_str)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET, dst_str, &dst);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ dst = ntohl(dst);
+
+ printf("CMD: ip route add %s/%d dev %s", dst_str, prefixlen, iface);
+ if (metric > 0)
+ {
+ printf(" metric %d", metric);
+ }
+ printf("\n");
+
+ return net_route_v4_add(NULL, &dst, prefixlen, NULL, iface, 0, metric);
+
+}
+
+static int
+net__route_v4_add_gw(const char *dst_str, int prefixlen, const char *gw_str,
+ int metric)
+{
+ in_addr_t dst, gw;
+ int ret;
+
+ if (!dst_str || !gw_str)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET, dst_str, &dst);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET, gw_str, &gw);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ dst = ntohl(dst);
+ gw = ntohl(gw);
+
+ printf("CMD: ip route add %s/%d dev %s via %s", dst_str, prefixlen, iface,
+ gw_str);
+ if (metric > 0)
+ {
+ printf(" metric %d", metric);
+ }
+ printf("\n");
+
+ return net_route_v4_add(NULL, &dst, prefixlen, &gw, iface, 0, metric);
+}
+
+static int
+net__route_v6_add(const char *dst_str, int prefixlen, int metric)
+{
+ struct in6_addr dst;
+ int ret;
+
+ if (!dst_str)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET6, dst_str, &dst);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ printf("CMD: ip -6 route add %s/%d dev %s", dst_str, prefixlen, iface);
+ if (metric > 0)
+ {
+ printf(" metric %d", metric);
+ }
+ printf("\n");
+
+ return net_route_v6_add(NULL, &dst, prefixlen, NULL, iface, 0, metric);
+
+}
+
+static int
+net__route_v6_add_gw(const char *dst_str, int prefixlen, const char *gw_str,
+ int metric)
+{
+ struct in6_addr dst, gw;
+ int ret;
+
+ if (!dst_str || !gw_str)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET6, dst_str, &dst);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ ret = inet_pton(AF_INET6, gw_str, &gw);
+ if (ret != 1)
+ {
+ return -1;
+ }
+
+ printf("CMD: ip -6 route add %s/%d dev %s via %s", dst_str, prefixlen,
+ iface, gw_str);
+ if (metric > 0)
+ {
+ printf(" metric %d", metric);
+ }
+ printf("\n");
+
+ return net_route_v6_add(NULL, &dst, prefixlen, &gw, iface, 0, metric);
+}
+
+static void
+usage(char *name)
+{
+ printf("Usage: %s <0-7>\n", name);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int test;
+
+ if (argc < 2)
+ {
+ usage(argv[0]);
+ return -1;
+ }
+
+ /* the t_net script can use this command to perform a dry-run test */
+ if (strcmp(argv[1], "test") == 0)
+ {
+ return 0;
+ }
+
+ if (argc > 3)
+ {
+ iface = argv[2];
+ }
+
+ test = atoi(argv[1]);
+ switch (test)
+ {
+ case 0:
+ return net__iface_up(true);
+
+ case 1:
+ return net__iface_mtu_set(1281);
+
+ case 2:
+ return net__addr_v4_add("10.255.255.1", 24);
+
+ case 3:
+ return net__addr_v6_add("2001::1", 64);
+
+ case 4:
+ return net__route_v4_add("11.11.11.0", 24, 0);
+
+ case 5:
+ return net__route_v4_add_gw("11.11.12.0", 24, "10.255.255.2", 0);
+
+ case 6:
+ return net__route_v6_add("2001:babe:cafe:babe::", 64, 600);
+
+ case 7:
+ return net__route_v6_add_gw("2001:cafe:babe::", 48, "2001::2", 600);
+
+ default:
+ printf("invalid test: %d\n", test);
+ break;
+ }
+
+ usage(argv[0]);
+ return -1;
+}
diff --git a/tests/unit_tests/openvpn/test_packet_id.c b/tests/unit_tests/openvpn/test_packet_id.c
index ba420c4..a3d4db2 100644
--- a/tests/unit_tests/openvpn/test_packet_id.c
+++ b/tests/unit_tests/openvpn/test_packet_id.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -49,9 +49,10 @@ struct test_packet_id_write_data {
};
static int
-test_packet_id_write_setup(void **state) {
+test_packet_id_write_setup(void **state)
+{
struct test_packet_id_write_data *data =
- calloc(1, sizeof(struct test_packet_id_write_data));
+ calloc(1, sizeof(struct test_packet_id_write_data));
if (!data)
{
@@ -66,7 +67,8 @@ test_packet_id_write_setup(void **state) {
}
static int
-test_packet_id_write_teardown(void **state) {
+test_packet_id_write_teardown(void **state)
+{
free(*state);
return 0;
}
@@ -155,20 +157,27 @@ test_packet_id_write_long_wrap(void **state)
}
int
-main(void) {
+main(void)
+{
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup_teardown(test_packet_id_write_short,
- test_packet_id_write_setup, test_packet_id_write_teardown),
- cmocka_unit_test_setup_teardown(test_packet_id_write_long,
- test_packet_id_write_setup, test_packet_id_write_teardown),
- cmocka_unit_test_setup_teardown(test_packet_id_write_short_prepend,
- test_packet_id_write_setup, test_packet_id_write_teardown),
- cmocka_unit_test_setup_teardown(test_packet_id_write_long_prepend,
- test_packet_id_write_setup, test_packet_id_write_teardown),
- cmocka_unit_test_setup_teardown(test_packet_id_write_short_wrap,
- test_packet_id_write_setup, test_packet_id_write_teardown),
- cmocka_unit_test_setup_teardown(test_packet_id_write_long_wrap,
- test_packet_id_write_setup, test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_short,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_long,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_short_prepend,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_long_prepend,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_short_wrap,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
+ cmocka_unit_test_setup_teardown(test_packet_id_write_long_wrap,
+ test_packet_id_write_setup,
+ test_packet_id_write_teardown),
};
return cmocka_run_group_tests_name("packet_id tests", tests, NULL, NULL);
diff --git a/tests/unit_tests/openvpn/test_tls_crypt.c b/tests/unit_tests/openvpn/test_tls_crypt.c
index f5618f8..3e604d6 100644
--- a/tests/unit_tests/openvpn/test_tls_crypt.c
+++ b/tests/unit_tests/openvpn/test_tls_crypt.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2016-2018 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.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
@@ -27,8 +27,6 @@
#include "config-msvc.h"
#endif
-#ifdef ENABLE_CRYPTO
-
#include "syshead.h"
#include <stdio.h>
@@ -45,9 +43,98 @@
#define TESTBUF_SIZE 128
-const char plaintext_short[1];
+/* Defines for use in the tests and the mock parse_line() */
+#define PATH1 "/s p a c e"
+#define PATH2 "/foo bar/baz"
+#define PARAM1 "param1"
+#define PARAM2 "param two"
+
+static const char *test_server_key = \
+ "-----BEGIN OpenVPN tls-crypt-v2 server key-----\n"
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=\n"
+ "-----END OpenVPN tls-crypt-v2 server key-----\n";
+
+static const char *test_client_key = \
+ "-----BEGIN OpenVPN tls-crypt-v2 client key-----\n"
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
+ "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\n"
+ "wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\n"
+ "8PHy8/T19vf4+fr7/P3+/xd9pcB0qUYZsWvkrLcfGmzPJPM8a7r0mEWdXwbDadSV\n"
+ "LHg5bv2TwlmPR3HgaMr8o9LTh9hxUTkrH3S0PfKRNwcso86ua/dBFTyXsM9tg4aw\n"
+ "3dS6ogH9AkaT+kRRDgNcKWkQCbwmJK2JlfkXHBwbAtmn78AkNuho6QCFqCdqGab3\n"
+ "zh2vheFqGMPdGpukbFrT3rcO3VLxUeG+RdzXiMTCpJSovFBP1lDkYwYJPnz6daEh\n"
+ "j0TzJ3BVru9W3CpotdNt7u09knxAfpCxjtrP3semsDew/gTBtcfQ/OoTFyFHnN5k\n"
+ "RZ+q17SC4nba3Pp8/Fs0+hSbv2tJozoD8SElFq7SIWJsciTYh8q8f5yQxjdt4Wxu\n"
+ "/Z5wtPCAZ0tOzj4ItTI77fBOYRTfEayzHgEr\n"
+ "-----END OpenVPN tls-crypt-v2 client key-----\n";
+
+
+/* Has custom metadata of AABBCCDD (base64) */
+static const char *test_client_key_metadata = \
+ "-----BEGIN OpenVPN tls-crypt-v2 client key-----\n"
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
+ "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\n"
+ "wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\n"
+ "8PHy8/T19vf4+fr7/P3+/2ntp1WCqhcLjJQY/igkjNt3Yb6i0neqFkfrOp2UCDcz\n"
+ "6RSJtPLZbvOOKUHk2qwxPYUsFCnz/IWV6/ZiLRrabzUpS8oSN1HS6P7qqAdrHKgf\n"
+ "hVTHasdSf2UdMTPC7HBgnP9Ll0FhKN0h7vSzbbt7QM7wH9mr1ecc/Mt0SYW2lpwA\n"
+ "aJObYGTyk6hTgWm0g/MLrworLrezTqUHBZzVsu+LDyqLWK1lzJNd66MuNOsGA4YF\n"
+ "fbCsDh8n3H+Cw1k5YNBZDYYJOtVUgBWXheO6vgoOmqDdI0dAQ3hVo9DE+SkCFjgf\n"
+ "l4FY2yLEh9ZVZZrl1eD1Owh/X178CkHrBJYl9LNQSyQEKlDGWwBLQ/pY3qtjctr3\n"
+ "pV62MPQdBo+1lcsjDCJVQA6XUyltas4BKQ==\n"
+ "-----END OpenVPN tls-crypt-v2 client key-----\n";
+
+int
+__wrap_parse_line(const char *line, char **p, const int n, const char *file,
+ const int line_num, int msglevel, struct gc_arena *gc)
+{
+ p[0] = PATH1 PATH2;
+ p[1] = PARAM1;
+ p[2] = PARAM2;
+ return 3;
+}
+
+bool
+__wrap_buffer_write_file(const char *filename, const struct buffer *buf)
+{
+ const char *pem = BSTR(buf);
+ check_expected(filename);
+ check_expected(pem);
+
+ return mock();
+}
+
+struct buffer
+__wrap_buffer_read_from_file(const char *filename, struct gc_arena *gc)
+{
+ check_expected(filename);
-struct test_context {
+ const char *pem_str = (const char *) mock();
+ struct buffer ret = alloc_buf_gc(strlen(pem_str) + 1, gc);
+ buf_write(&ret, pem_str, strlen(pem_str) + 1);
+
+ return ret;
+}
+
+
+/** Predictable random for tests */
+int
+__wrap_rand_bytes(uint8_t *output, int len)
+{
+ for (int i = 0; i < len; i++)
+ {
+ output[i] = i;
+ }
+ return true;
+}
+
+struct test_tls_crypt_context {
struct crypto_options co;
struct key_type kt;
struct buffer source;
@@ -56,8 +143,9 @@ struct test_context {
};
static int
-setup(void **state) {
- struct test_context *ctx = calloc(1, sizeof(*ctx));
+test_tls_crypt_setup(void **state)
+{
+ struct test_tls_crypt_context *ctx = calloc(1, sizeof(*ctx));
*state = ctx;
struct key key = { 0 };
@@ -77,17 +165,21 @@ setup(void **state) {
ctx->unwrapped = alloc_buf(TESTBUF_SIZE);
/* Write test plaintext */
- buf_write(&ctx->source, plaintext_short, sizeof(plaintext_short));
+ const char *plaintext = "1234567890";
+ buf_write(&ctx->source, plaintext, strlen(plaintext));
- /* Write dummy opcode and session id */
- buf_write(&ctx->ciphertext, "012345678", 1 + 8);
+ /* Write test ciphertext */
+ const char *ciphertext = "012345678";
+ buf_write(&ctx->ciphertext, ciphertext, strlen(ciphertext));
return 0;
}
static int
-teardown(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+test_tls_crypt_teardown(void **state)
+{
+ struct test_tls_crypt_context *ctx =
+ (struct test_tls_crypt_context *)*state;
free_buf(&ctx->source);
free_buf(&ctx->ciphertext);
@@ -100,7 +192,8 @@ teardown(void **state) {
return 0;
}
-static void skip_if_tls_crypt_not_supported(struct test_context *ctx)
+static void
+skip_if_tls_crypt_not_supported(struct test_tls_crypt_context *ctx)
{
if (!ctx->kt.cipher || !ctx->kt.digest)
{
@@ -112,8 +205,9 @@ static void skip_if_tls_crypt_not_supported(struct test_context *ctx)
* Check that short messages are successfully wrapped-and-unwrapped.
*/
static void
-tls_crypt_loopback(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_loopback(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -129,8 +223,9 @@ tls_crypt_loopback(void **state) {
* Check that zero-byte messages are successfully wrapped-and-unwrapped.
*/
static void
-tls_crypt_loopback_zero_len(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_loopback_zero_len(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -148,8 +243,9 @@ tls_crypt_loopback_zero_len(void **state) {
* Check that max-length messages are successfully wrapped-and-unwrapped.
*/
static void
-tls_crypt_loopback_max_len(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_loopback_max_len(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -169,8 +265,9 @@ tls_crypt_loopback_max_len(void **state) {
* Check that too-long messages are gracefully rejected.
*/
static void
-tls_crypt_fail_msg_too_long(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_fail_msg_too_long(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -185,8 +282,9 @@ tls_crypt_fail_msg_too_long(void **state) {
* are not accepted.
*/
static void
-tls_crypt_fail_invalid_key(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_fail_invalid_key(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -204,8 +302,9 @@ tls_crypt_fail_invalid_key(void **state) {
* Check that replayed packets are not accepted.
*/
static void
-tls_crypt_fail_replay(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_fail_replay(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -223,8 +322,9 @@ tls_crypt_fail_replay(void **state) {
* know the packet ID yet.
*/
static void
-tls_crypt_ignore_replay(void **state) {
- struct test_context *ctx = (struct test_context *) *state;
+tls_crypt_ignore_replay(void **state)
+{
+ struct test_tls_crypt_context *ctx = (struct test_tls_crypt_context *) *state;
skip_if_tls_crypt_not_supported(ctx);
@@ -238,22 +338,304 @@ tls_crypt_ignore_replay(void **state) {
assert_true(tls_crypt_unwrap(&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
}
+struct test_tls_crypt_v2_context {
+ struct gc_arena gc;
+ struct key2 server_key2;
+ struct key_ctx_bi server_keys;
+ struct key2 client_key2;
+ struct key_ctx_bi client_key;
+ struct buffer metadata;
+ struct buffer unwrapped_metadata;
+ struct buffer wkc;
+};
+
+static int
+test_tls_crypt_v2_setup(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx = calloc(1, sizeof(*ctx));
+ *state = ctx;
+
+ ctx->gc = gc_new();
+
+ /* Slightly longer buffers to be able to test too-long data */
+ ctx->metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN+16, &ctx->gc);
+ ctx->unwrapped_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN+16,
+ &ctx->gc);
+ ctx->wkc = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN+16, &ctx->gc);
+
+ /* Generate server key */
+ rand_bytes((void *)ctx->server_key2.keys, sizeof(ctx->server_key2.keys));
+ ctx->server_key2.n = 2;
+ struct key_type kt = tls_crypt_kt();
+ init_key_ctx_bi(&ctx->server_keys, &ctx->server_key2,
+ KEY_DIRECTION_BIDIRECTIONAL, &kt,
+ "tls-crypt-v2 server key");
+
+ /* Generate client key */
+ rand_bytes((void *)ctx->client_key2.keys, sizeof(ctx->client_key2.keys));
+ ctx->client_key2.n = 2;
+
+ return 0;
+}
+
+static int
+test_tls_crypt_v2_teardown(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ free_key_ctx_bi(&ctx->server_keys);
+ free_key_ctx_bi(&ctx->client_key);
+
+ gc_free(&ctx->gc);
+
+ free(ctx);
+
+ return 0;
+}
+
+/**
+ * Check wrapping and unwrapping a tls-crypt-v2 client key without metadata.
+ */
+static void
+tls_crypt_v2_wrap_unwrap_no_metadata(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ struct buffer wrapped_client_key = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN,
+ &ctx->gc);
+ assert_true(tls_crypt_v2_wrap_client_key(&wrapped_client_key,
+ &ctx->client_key2,
+ &ctx->metadata,
+ &ctx->server_keys.encrypt,
+ &ctx->gc));
+
+ struct buffer unwrap_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN,
+ &ctx->gc);
+ struct key2 unwrapped_client_key2 = { 0 };
+ assert_true(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2,
+ &unwrap_metadata,
+ wrapped_client_key,
+ &ctx->server_keys.decrypt));
+
+ assert_true(0 == memcmp(ctx->client_key2.keys, unwrapped_client_key2.keys,
+ sizeof(ctx->client_key2.keys)));
+}
+
+/**
+ * Check wrapping and unwrapping a tls-crypt-v2 client key with maximum length
+ * metadata.
+ */
+static void
+tls_crypt_v2_wrap_unwrap_max_metadata(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ uint8_t *metadata =
+ buf_write_alloc(&ctx->metadata, TLS_CRYPT_V2_MAX_METADATA_LEN);
+ assert_true(rand_bytes(metadata, TLS_CRYPT_V2_MAX_METADATA_LEN));
+ assert_true(tls_crypt_v2_wrap_client_key(&ctx->wkc, &ctx->client_key2,
+ &ctx->metadata,
+ &ctx->server_keys.encrypt,
+ &ctx->gc));
+
+ struct buffer unwrap_metadata = alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN,
+ &ctx->gc);
+ struct key2 unwrapped_client_key2 = { 0 };
+ assert_true(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2,
+ &unwrap_metadata, ctx->wkc,
+ &ctx->server_keys.decrypt));
+
+ assert_true(0 == memcmp(ctx->client_key2.keys, unwrapped_client_key2.keys,
+ sizeof(ctx->client_key2.keys)));
+ assert_true(buf_equal(&ctx->metadata, &unwrap_metadata));
+
+ struct tls_wrap_ctx wrap_ctx = {
+ .mode = TLS_WRAP_CRYPT,
+ .tls_crypt_v2_server_key = ctx->server_keys.encrypt,
+ };
+ assert_true(tls_crypt_v2_extract_client_key(&ctx->wkc, &wrap_ctx, NULL));
+ tls_wrap_free(&wrap_ctx);
+}
+
+/**
+ * Check that wrapping a tls-crypt-v2 client key with too long metadata fails
+ * as expected.
+ */
+static void
+tls_crypt_v2_wrap_too_long_metadata(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ assert_true(buf_inc_len(&ctx->metadata, TLS_CRYPT_V2_MAX_METADATA_LEN+1));
+ assert_false(tls_crypt_v2_wrap_client_key(&ctx->wkc, &ctx->client_key2,
+ &ctx->metadata,
+ &ctx->server_keys.encrypt,
+ &ctx->gc));
+}
+
+/**
+ * Check that unwrapping a tls-crypt-v2 client key with the wrong server key
+ * fails as expected.
+ */
+static void
+tls_crypt_v2_wrap_unwrap_wrong_key(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ assert_true(tls_crypt_v2_wrap_client_key(&ctx->wkc, &ctx->client_key2,
+ &ctx->metadata,
+ &ctx->server_keys.encrypt,
+ &ctx->gc));
+
+ /* Change server key */
+ struct key_type kt = tls_crypt_kt();
+ free_key_ctx_bi(&ctx->server_keys);
+ memset(&ctx->server_key2.keys, 0, sizeof(ctx->server_key2.keys));
+ init_key_ctx_bi(&ctx->server_keys, &ctx->server_key2,
+ KEY_DIRECTION_BIDIRECTIONAL, &kt,
+ "wrong tls-crypt-v2 server key");
+
+
+ struct key2 unwrapped_client_key2 = { 0 };
+ assert_false(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2,
+ &ctx->unwrapped_metadata,
+ ctx->wkc,
+ &ctx->server_keys.decrypt));
+
+ const struct key2 zero = { 0 };
+ assert_true(0 == memcmp(&unwrapped_client_key2, &zero, sizeof(zero)));
+ assert_true(0 == BLEN(&ctx->unwrapped_metadata));
+}
+
+/**
+ * Check that unwrapping a tls-crypt-v2 client key to a too small metadata
+ * buffer fails as expected.
+ */
+static void
+tls_crypt_v2_wrap_unwrap_dst_too_small(void **state)
+{
+ struct test_tls_crypt_v2_context *ctx =
+ (struct test_tls_crypt_v2_context *) *state;
+
+ uint8_t *metadata =
+ buf_write_alloc(&ctx->metadata, TLS_CRYPT_V2_MAX_METADATA_LEN);
+ assert_true(rand_bytes(metadata, TLS_CRYPT_V2_MAX_METADATA_LEN));
+ assert_true(tls_crypt_v2_wrap_client_key(&ctx->wkc, &ctx->client_key2,
+ &ctx->metadata,
+ &ctx->server_keys.encrypt,
+ &ctx->gc));
+
+ struct key2 unwrapped_client_key2 = { 0 };
+ struct buffer unwrapped_metadata =
+ alloc_buf_gc(TLS_CRYPT_V2_MAX_METADATA_LEN-1, &ctx->gc);
+ assert_false(tls_crypt_v2_unwrap_client_key(&unwrapped_client_key2,
+ &unwrapped_metadata, ctx->wkc,
+ &ctx->server_keys.decrypt));
+
+ const struct key2 zero = { 0 };
+ assert_true(0 == memcmp(&unwrapped_client_key2, &zero, sizeof(zero)));
+ assert_true(0 == BLEN(&ctx->unwrapped_metadata));
+}
+
+static void
+test_tls_crypt_v2_write_server_key_file(void **state)
+{
+ const char *filename = "testfilename.key";
+
+ expect_string(__wrap_buffer_write_file, filename, filename);
+ expect_memory(__wrap_buffer_write_file, pem, test_server_key,
+ strlen(test_server_key));
+ will_return(__wrap_buffer_write_file, true);
+
+ tls_crypt_v2_write_server_key_file(filename);
+}
+
+static void
+test_tls_crypt_v2_write_client_key_file(void **state)
+{
+ const char *filename = "testfilename.key";
+
+ /* Test writing the client key */
+ expect_string(__wrap_buffer_write_file, filename, filename);
+ expect_memory(__wrap_buffer_write_file, pem, test_client_key,
+ strlen(test_client_key));
+ will_return(__wrap_buffer_write_file, true);
+
+ /* Key generation re-reads the created file as a sanity check */
+ expect_string(__wrap_buffer_read_from_file, filename, filename);
+ will_return(__wrap_buffer_read_from_file, test_client_key);
+
+ tls_crypt_v2_write_client_key_file(filename, NULL, test_server_key, true);
+}
+
+static void
+test_tls_crypt_v2_write_client_key_file_metadata(void **state)
+{
+ const char *filename = "testfilename.key";
+ const char *b64metadata = "AABBCCDD";
+
+ /* Test writing the client key */
+ expect_string(__wrap_buffer_write_file, filename, filename);
+ expect_memory(__wrap_buffer_write_file, pem, test_client_key_metadata,
+ strlen(test_client_key_metadata));
+ will_return(__wrap_buffer_write_file, true);
+
+ /* Key generation re-reads the created file as a sanity check */
+ expect_string(__wrap_buffer_read_from_file, filename, filename);
+ will_return(__wrap_buffer_read_from_file, test_client_key_metadata);
+
+ tls_crypt_v2_write_client_key_file(filename, b64metadata, test_server_key,
+ true);
+}
+
int
-main(void) {
+main(void)
+{
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup_teardown(tls_crypt_loopback, setup, teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_loopback,
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_loopback_zero_len,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_loopback_max_len,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_fail_msg_too_long,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_fail_invalid_key,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_fail_replay,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
cmocka_unit_test_setup_teardown(tls_crypt_ignore_replay,
- setup, teardown),
+ test_tls_crypt_setup,
+ test_tls_crypt_teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_no_metadata,
+ test_tls_crypt_v2_setup,
+ test_tls_crypt_v2_teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_max_metadata,
+ test_tls_crypt_v2_setup,
+ test_tls_crypt_v2_teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_too_long_metadata,
+ test_tls_crypt_v2_setup,
+ test_tls_crypt_v2_teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_wrong_key,
+ test_tls_crypt_v2_setup,
+ test_tls_crypt_v2_teardown),
+ cmocka_unit_test_setup_teardown(tls_crypt_v2_wrap_unwrap_dst_too_small,
+ test_tls_crypt_v2_setup,
+ test_tls_crypt_v2_teardown),
+ cmocka_unit_test(test_tls_crypt_v2_write_server_key_file),
+ cmocka_unit_test(test_tls_crypt_v2_write_client_key_file),
+ cmocka_unit_test(test_tls_crypt_v2_write_client_key_file_metadata),
};
#if defined(ENABLE_CRYPTO_OPENSSL)
@@ -268,5 +650,3 @@ main(void) {
return ret;
}
-
-#endif /* ENABLE_CRYPTO */
diff --git a/tests/unit_tests/plugins/Makefile.in b/tests/unit_tests/plugins/Makefile.in
index 2a4b264..ede765f 100644
--- a/tests/unit_tests/plugins/Makefile.in
+++ b/tests/unit_tests/plugins/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -198,7 +198,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -212,6 +213,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -239,7 +241,6 @@ 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@
@@ -290,6 +291,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -353,6 +356,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/tests/unit_tests/plugins/auth-pam/Makefile.in b/tests/unit_tests/plugins/auth-pam/Makefile.in
index 2b7ca47..5743652 100644
--- a/tests/unit_tests/plugins/auth-pam/Makefile.in
+++ b/tests/unit_tests/plugins/auth-pam/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -218,7 +218,8 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
-CMAKE = @CMAKE@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
@@ -232,6 +233,7 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GIT = @GIT@
@@ -259,7 +261,6 @@ 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@
@@ -310,6 +311,8 @@ PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@
RANLIB = @RANLIB@
RC = @RC@
ROUTE = @ROUTE@
+RST2HTML = @RST2HTML@
+RST2MAN = @RST2MAN@
SED = @SED@
SELINUX_LIBS = @SELINUX_LIBS@
SET_MAKE = @SET_MAKE@
@@ -373,6 +376,7 @@ plugindir = @plugindir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sampledir = @sampledir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
diff --git a/vendor/Makefile.am b/vendor/Makefile.am
deleted file mode 100644
index 46072c3..0000000
--- a/vendor/Makefile.am
+++ /dev/null
@@ -1,22 +0,0 @@
-cmockasrc = $(srcdir)/cmocka
-# Not just '$(builddir)/cmocka', because cmocka requires an out-of-source build
-cmockabuild = $(builddir)/cmocka_build
-cmockadist = $(builddir)/dist
-
-MAINTAINERCLEANFILES = \
- $(srcdir)/Makefile.in \
- "$(cmockabuild)" \
- "$(cmockadist)"
-
-libcmocka:
-if CMOCKA_INITIALIZED
- mkdir -p $(cmockabuild) $(cmockadist)
- ## Compensate for the cd in the paths
- (cd $(cmockabuild) && cmake -DCMAKE_INSTALL_PREFIX=../$(cmockadist) ../$(cmockasrc) && make && make install)
-endif
-
-check: libcmocka
-
-clean:
- rm -rf $(cmockabuild)
- rm -rf $(cmockainstall)
diff --git a/version.m4 b/version.m4
index 2e23539..f47b4bf 100644
--- a/version.m4
+++ b/version.m4
@@ -2,13 +2,13 @@ dnl define the OpenVPN version
define([PRODUCT_NAME], [OpenVPN])
define([PRODUCT_TARNAME], [openvpn])
define([PRODUCT_VERSION_MAJOR], [2])
-define([PRODUCT_VERSION_MINOR], [4])
-define([PRODUCT_VERSION_PATCH], [.9])
+define([PRODUCT_VERSION_MINOR], [5])
+define([PRODUCT_VERSION_PATCH], [.4])
m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_MAJOR])
m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_MINOR], [[.]])
m4_append([PRODUCT_VERSION], [PRODUCT_VERSION_PATCH], [[]])
define([PRODUCT_BUGREPORT], [openvpn-users@lists.sourceforge.net])
-define([PRODUCT_VERSION_RESOURCE], [2,4,9,0])
+define([PRODUCT_VERSION_RESOURCE], [2,5,4,0])
dnl define the TAP version
define([PRODUCT_TAP_WIN_COMPONENT_ID], [tap0901])
define([PRODUCT_TAP_WIN_MIN_MAJOR], [9])