From 56284506fccdd38b3d45b1656809db8036d3ced0 Mon Sep 17 00:00:00 2001 From: Sergey Abramchuk Date: Thu, 3 May 2018 11:46:13 +0300 Subject: [PATCH] Squashed 'OpenVPN Adapter/Vendors/openvpn/' changes from e6d68831a..35bbca799 35bbca799 Merged in OVPN3-184-generate-warning (pull request #1) a73d2ce68 Merged in antonio/OVPN3-169-pure-ssl-transport (pull request #3) 8d7f5f3c1 Merged in feature/docker (pull request #2) d9b5055cd [OVPN3-169] cli.cpp: compile with -DOPENVPN_TLS_LINK when requested 2d99bbfea [OVPN3-169] cliopt.hpp: add support for TLS transport module 62c8461d2 [OVPN3-169] tcpcli.hpp: add runtime support for TLSLink e0e76bb28 [OVPN3-169] tcplink: introduce LinkBase abstract class a71014d40 [OVPN3-169] tcplink: create LinkCommon class and inherit from it cfd6df5bc build system: fix 'git apply' 3e49de7de [OVPN3-210] ovpncli: handle "allow-name-constraints" for OpenSSL 08d72bd76 [OVPN3-184] mbedtls: handle Name Constraints 40c70113d [OVPN3-184] Add mbedTLS patch ef8d11f34 [OVPN3-169] OpenSSL: implement write_ciphertext_unbuffered() function 37dc86378 [OVPN3-169] mbedTLS: implement write_ciphertext_unbuffered() function 5834ed401 [OVPN3-169] SSLAPI: add write_ciphertext_unbuffered() function 071050b5f vars-linux-dbg: update linux debug profile 5bbfe68c3 [OVPN3-169] Protocol: add support for TLS transport protocol type dc12d3189 [OVPN3-223] build: add docker images git-subtree-dir: OpenVPN Adapter/Vendors/openvpn git-subtree-split: 35bbca799dfa3fbe8e17f8d6e94c3946c397b593 --- client/ovpncli.cpp | 9 + deps/asio/build-asio | 6 +- deps/functions.sh | 17 + deps/mbedtls/build-mbedtls | 6 +- ...unsupported-critical-extensions-in-r.patch | 361 ++++++++++++++ docker-build.sh | 12 + dockerfiles/Dockerfile.centos | 19 + dockerfiles/Dockerfile.debian | 17 + dockerfiles/Dockerfile.ubu | 17 + openvpn/client/clievent.hpp | 24 + openvpn/client/cliopt.hpp | 14 +- openvpn/client/cliopthelper.hpp | 7 +- openvpn/client/cliproto.hpp | 14 +- openvpn/mbedtls/pki/x509cert.hpp | 9 +- openvpn/mbedtls/ssl/sslctx.hpp | 52 ++- openvpn/openssl/ssl/sslctx.hpp | 9 + openvpn/ssl/sslapi.hpp | 7 +- openvpn/ssl/sslchoose.hpp | 9 +- openvpn/transport/client/httpcli.hpp | 2 +- openvpn/transport/client/tcpcli.hpp | 59 ++- openvpn/transport/protocol.hpp | 33 +- openvpn/transport/tcplink.hpp | 380 +-------------- openvpn/transport/tcplinkbase.hpp | 39 ++ openvpn/transport/tcplinkcommon.hpp | 441 ++++++++++++++++++ test/ovpncli/go | 1 + vars/vars-linux-dbg | 10 +- 26 files changed, 1162 insertions(+), 412 deletions(-) create mode 100644 deps/mbedtls/patches/0008-Enable-allowing-unsupported-critical-extensions-in-r.patch create mode 100755 docker-build.sh create mode 100644 dockerfiles/Dockerfile.centos create mode 100644 dockerfiles/Dockerfile.debian create mode 100644 dockerfiles/Dockerfile.ubu create mode 100644 openvpn/transport/tcplinkbase.hpp create mode 100644 openvpn/transport/tcplinkcommon.hpp diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index e9cf6ba..65b937c 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -997,6 +997,15 @@ namespace openvpn { } #endif +#ifdef USE_OPENSSL + if (state->options.exists("allow-name-constraints")) + { + ClientEvent::Base::Ptr ev = new ClientEvent::UnsupportedFeature("allow-name-constraints", + "Always verified correctly with OpenSSL", false); + state->events->add_event(std::move(ev)); + } +#endif + // build client options object ClientOptions::Ptr client_options = new ClientOptions(state->options, cc); diff --git a/deps/asio/build-asio b/deps/asio/build-asio index e4fa9f8..91d96a8 100755 --- a/deps/asio/build-asio +++ b/deps/asio/build-asio @@ -36,11 +36,7 @@ else tar xfz $DL/$FNAME cd asio-$ASIO_VERSION - # apply pre-generated patches - for file in $O3/core/deps/asio/patches/*.patch; do - echo Applying patch: $file - git apply $file - done + apply_patches "asio" cd .. diff --git a/deps/functions.sh b/deps/functions.sh index 7890445..a9e932b 100644 --- a/deps/functions.sh +++ b/deps/functions.sh @@ -28,3 +28,20 @@ function download() check_download || return -1 } + +function apply_patches() +{ + DEP_NAME=$1 + + # change directory since git apply got confused when + # applying patches to files which are not found in index + DIR=$(pwd) + pushd ${DIR} + cd /tmp + # apply pre-generated patches + for file in $O3/core/deps/${DEP_NAME}/patches/*.patch; do + echo Applying patch: $file + git apply --directory ${DIR} --unsafe-path $file + done + popd +} diff --git a/deps/mbedtls/build-mbedtls b/deps/mbedtls/build-mbedtls index fcf9d26..a12e376 100755 --- a/deps/mbedtls/build-mbedtls +++ b/deps/mbedtls/build-mbedtls @@ -51,11 +51,7 @@ else # enable MD4 (needed for NTLM auth) perl -pi -e 's/^\/\/// if /#define MBEDTLS_MD4_C/' include/mbedtls/config.h - # apply pre-generated patches - for file in $O3/core/deps/mbedtls/patches/*.patch; do - echo Applying patch: $file - git apply $file - done + apply_patches "mbedtls" fi if [[ "x$TARGET" == xlinux* || "x$TARGET" == xosx* ]]; then diff --git a/deps/mbedtls/patches/0008-Enable-allowing-unsupported-critical-extensions-in-r.patch b/deps/mbedtls/patches/0008-Enable-allowing-unsupported-critical-extensions-in-r.patch new file mode 100644 index 0000000..da772f2 --- /dev/null +++ b/deps/mbedtls/patches/0008-Enable-allowing-unsupported-critical-extensions-in-r.patch @@ -0,0 +1,361 @@ +From 13dd5f71dfe345787c3c44ef177009530983bf20 Mon Sep 17 00:00:00 2001 +From: Lev Stipakov +Date: Fri, 23 Feb 2018 17:12:49 +0200 +Subject: [PATCH] Enable allowing unsupported critical extensions in runtime + +When compile time flag MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION +is not set, certificate parsing fails if certificate contains unsupported critical extension. + +This patch allows to modify this behavior in runtime. + +Signed-off-by: Lev Stipakov +--- + include/mbedtls/oid.h | 13 ++++- + include/mbedtls/ssl.h | 22 ++++++++ + include/mbedtls/x509_crt.h | 2 + + library/oid.c | 81 +++++++++++++++++++++++++----- + library/ssl_tls.c | 8 +++ + library/x509_crt.c | 10 +++- + tests/data_files/test-ca-nc.crt | 20 ++++++++ + tests/suites/test_suite_x509parse.data | 6 +++ + tests/suites/test_suite_x509parse.function | 15 ++++++ + 9 files changed, 162 insertions(+), 15 deletions(-) + create mode 100644 tests/data_files/test-ca-nc.crt + +diff --git a/include/mbedtls/oid.h b/include/mbedtls/oid.h +index fcecdafd..096b1b10 100644 +--- a/include/mbedtls/oid.h ++++ b/include/mbedtls/oid.h +@@ -401,7 +401,7 @@ int mbedtls_oid_get_numeric_string( char *buf, size_t size, const mbedtls_asn1_b + + #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) + /** +- * \brief Translate an X.509 extension OID into local values ++ * \brief Translate supported X.509 extension OID into local values + * + * \param oid OID to use + * \param ext_type place to store the extension type +@@ -409,6 +409,17 @@ int mbedtls_oid_get_numeric_string( char *buf, size_t size, const mbedtls_asn1_b + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ + int mbedtls_oid_get_x509_ext_type( const mbedtls_asn1_buf *oid, int *ext_type ); ++ ++/** ++ * \brief Translate supported and unsupported X.509 extension OID into local values ++ * ++ * \param oid OID to use ++ * \param ext_type place to store the extension type ++ * \param is_supported place to store flag if extension is supported (1 - supported, 0 otherwise) ++ * ++ * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND ++ */ ++int mbedtls_oid_get_x509_ext_type_supported( const mbedtls_asn1_buf *oid, int *ext_type, int *is_supported ); + #endif + + /** +diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h +index cc000700..cb779f86 100644 +--- a/include/mbedtls/ssl.h ++++ b/include/mbedtls/ssl.h +@@ -695,6 +695,10 @@ struct mbedtls_ssl_config + retransmission timeout (ms) */ + #endif + ++ uint32_t allowed_unsupported_critical_exts; /*!< Bit flags which represent runtime-enabled ++ unsupported critical extensions, e.g. ++ MBEDTLS_X509_EXT_NAME_CONSTRAINTS */ ++ + #if defined(MBEDTLS_SSL_RENEGOTIATION) + int renego_max_records; /*!< grace period for renegotiation */ + unsigned char renego_period[8]; /*!< value of the record counters +@@ -2234,6 +2238,24 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, + const unsigned char period[8] ); + #endif /* MBEDTLS_SSL_RENEGOTIATION */ + ++/** ++ * \brief Allows unsupported critical extensions ++ * ++ * Without compile-time flag MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION ++ * mbedTLS fails certificate verification if certificate contains ++ * unsupported critical extensions. ++ * ++ * This method allows to modify behavior in runtime by providing ++ * bit flags which represent unsupported extensions (for example MBEDTLS_X509_EXT_NAME_CONSTRAINTS) ++ * which should be allowed despite missing above mentioned compile-time flag. ++ * ++ * \param conf SSL configuration ++ * \param exts Bit flags which represent runtime-enabled unsupported critical extensions, ++ * e.g. MBEDTLS_X509_EXT_NAME_CONSTRAINTS ++ * ++ */ ++void mbedtls_ssl_conf_allow_unsupported_critical_exts( mbedtls_ssl_config *conf, uint32_t exts ); ++ + /** + * \brief Return the number of data bytes available to read + * +diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h +index 06166d8b..adc6474f 100644 +--- a/include/mbedtls/x509_crt.h ++++ b/include/mbedtls/x509_crt.h +@@ -89,6 +89,8 @@ typedef struct mbedtls_x509_crt + mbedtls_pk_type_t sig_pk; /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA */ + void *sig_opts; /**< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS */ + ++ uint32_t allowed_unsupported_critical_exts; /**< Optional Bit flags which represent runtime-enabled unsupported critical extensions, e.g. MBEDTLS_X509_EXT_NAME_CONSTRAINTS */ ++ + struct mbedtls_x509_crt *next; /**< Next certificate in the CA-chain. */ + } + mbedtls_x509_crt; +diff --git a/library/oid.c b/library/oid.c +index f13826ed..7c50f24f 100644 +--- a/library/oid.c ++++ b/library/oid.c +@@ -254,38 +254,95 @@ FN_OID_GET_ATTR1(mbedtls_oid_get_attr_short_name, oid_x520_attr_t, x520_attr, co + typedef struct { + mbedtls_oid_descriptor_t descriptor; + int ext_type; ++ int is_supported; + } oid_x509_ext_t; + + static const oid_x509_ext_t oid_x509_ext[] = + { + { +- { ADD_LEN( MBEDTLS_OID_BASIC_CONSTRAINTS ), "id-ce-basicConstraints", "Basic Constraints" }, +- MBEDTLS_X509_EXT_BASIC_CONSTRAINTS, ++ { ADD_LEN( MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER ), "id-ce-authorityKeyIdentifier", "Authority Key Identifier" }, ++ MBEDTLS_X509_EXT_AUTHORITY_KEY_IDENTIFIER, 0, + }, + { +- { ADD_LEN( MBEDTLS_OID_KEY_USAGE ), "id-ce-keyUsage", "Key Usage" }, +- MBEDTLS_X509_EXT_KEY_USAGE, ++ { ADD_LEN( MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER ), "id-ce-subjectKeyIdentifier", "Subject Key Identifier" }, ++ MBEDTLS_X509_EXT_SUBJECT_KEY_IDENTIFIER, 0, + }, + { +- { ADD_LEN( MBEDTLS_OID_EXTENDED_KEY_USAGE ), "id-ce-extKeyUsage", "Extended Key Usage" }, +- MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE, ++ { ADD_LEN( MBEDTLS_OID_KEY_USAGE ), "id-ce-keyUsage", "Key Usage" }, ++ MBEDTLS_X509_EXT_KEY_USAGE, 1, + }, + { +- { ADD_LEN( MBEDTLS_OID_SUBJECT_ALT_NAME ), "id-ce-subjectAltName", "Subject Alt Name" }, +- MBEDTLS_X509_EXT_SUBJECT_ALT_NAME, ++ { ADD_LEN( MBEDTLS_OID_CERTIFICATE_POLICIES ), "id-ce-certificatePolicies", "Certificate Policies" }, ++ MBEDTLS_X509_EXT_CERTIFICATE_POLICIES, 0, + }, + { +- { ADD_LEN( MBEDTLS_OID_NS_CERT_TYPE ), "id-netscape-certtype", "Netscape Certificate Type" }, +- MBEDTLS_X509_EXT_NS_CERT_TYPE, ++ { ADD_LEN( MBEDTLS_OID_POLICY_MAPPINGS ), "id-ce-policyMappings", "Policy Mapping" }, ++ MBEDTLS_X509_EXT_POLICY_MAPPINGS, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_ISSUER_ALT_NAME ), "id-ce-issuerAltName", "Issuer Alt Name" }, ++ MBEDTLS_X509_EXT_ISSUER_ALT_NAME, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_SUBJECT_DIRECTORY_ATTRS ), "id-ce-subjectDirectoryAttributes", "Subject Directory Attributes" }, ++ MBEDTLS_X509_EXT_SUBJECT_DIRECTORY_ATTRS, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_BASIC_CONSTRAINTS ), "id-ce-basicConstraints", "Basic Constraints" }, ++ MBEDTLS_X509_EXT_BASIC_CONSTRAINTS, 1, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_NAME_CONSTRAINTS ), "id-ce-nameConstraints", "Name Constraints" }, ++ MBEDTLS_X509_EXT_NAME_CONSTRAINTS, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_POLICY_CONSTRAINTS ), "id-ce-policyConstraints", "Policy Constraints" }, ++ MBEDTLS_X509_EXT_POLICY_CONSTRAINTS, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_EXTENDED_KEY_USAGE ), "id-ce-extKeyUsage", "Extended Key Usage" }, ++ MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE, 1 ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_CRL_DISTRIBUTION_POINTS ), "id-ce-cRLDistributionPoints", "CRL Distribution Points" }, ++ MBEDTLS_X509_EXT_CRL_DISTRIBUTION_POINTS, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_INIHIBIT_ANYPOLICY ), "id-ce-inhibitAnyPolicy", "Inhibit Any Policy" }, ++ MBEDTLS_X509_EXT_INIHIBIT_ANYPOLICY, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_FRESHEST_CRL ), "id-ce-freshestCRL", "Freshest CRL" }, ++ MBEDTLS_X509_EXT_FRESHEST_CRL, 0, ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_SUBJECT_ALT_NAME ), "id-ce-subjectAltName", "Subject Alt Name" }, ++ MBEDTLS_X509_EXT_SUBJECT_ALT_NAME, 1 ++ }, ++ { ++ { ADD_LEN( MBEDTLS_OID_NS_CERT_TYPE ), "id-netscape-certtype", "Netscape Certificate Type" }, ++ MBEDTLS_X509_EXT_NS_CERT_TYPE, 1 + }, + { + { NULL, 0, NULL, NULL }, +- 0, ++ 0, 0 + }, + }; + + FN_OID_TYPED_FROM_ASN1(oid_x509_ext_t, x509_ext, oid_x509_ext) +-FN_OID_GET_ATTR1(mbedtls_oid_get_x509_ext_type, oid_x509_ext_t, x509_ext, int, ext_type) ++FN_OID_GET_ATTR2(mbedtls_oid_get_x509_ext_type_supported, oid_x509_ext_t, x509_ext, int, ext_type, int, is_supported) ++ ++int mbedtls_oid_get_x509_ext_type( const mbedtls_asn1_buf *oid, int *ext_type ) ++{ ++ int ret = 0; ++ int is_supported = 0; ++ ++ ret = mbedtls_oid_get_x509_ext_type_supported(oid, ext_type, &is_supported); ++ if( is_supported == 0 ) ++ ret = MBEDTLS_ERR_OID_NOT_FOUND; ++ ++ return( ret ); ++} + + static const mbedtls_oid_descriptor_t oid_ext_key_usage[] = + { +diff --git a/library/ssl_tls.c b/library/ssl_tls.c +index 661ae706..ed1f7b67 100644 +--- a/library/ssl_tls.c ++++ b/library/ssl_tls.c +@@ -4468,6 +4468,9 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) + + mbedtls_x509_crt_init( ssl->session_negotiate->peer_cert ); + ++ ssl->session_negotiate->peer_cert->allowed_unsupported_critical_exts = ++ ssl->conf->allowed_unsupported_critical_exts; ++ + i += 3; + + while( i < ssl->in_hslen ) +@@ -6344,6 +6347,11 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, + } + #endif /* MBEDTLS_SSL_RENEGOTIATION */ + ++void mbedtls_ssl_conf_allow_unsupported_critical_exts( mbedtls_ssl_config *conf, uint32_t exts ) ++{ ++ conf->allowed_unsupported_critical_exts = exts; ++} ++ + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + #if defined(MBEDTLS_SSL_CLI_C) + void mbedtls_ssl_conf_session_tickets( mbedtls_ssl_config *conf, int use_tickets ) +diff --git a/library/x509_crt.c b/library/x509_crt.c +index c6209fb4..1a61e5e9 100644 +--- a/library/x509_crt.c ++++ b/library/x509_crt.c +@@ -526,6 +526,7 @@ static int x509_get_crt_ext( unsigned char **p, + int ret; + size_t len; + unsigned char *end_ext_data, *end_ext_octet; ++ int is_supported; + + if( ( ret = mbedtls_x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) + { +@@ -585,9 +586,9 @@ static int x509_get_crt_ext( unsigned char **p, + /* + * Detect supported extensions + */ +- ret = mbedtls_oid_get_x509_ext_type( &extn_oid, &ext_type ); ++ ret = mbedtls_oid_get_x509_ext_type_supported( &extn_oid, &ext_type, &is_supported ); + +- if( ret != 0 ) ++ if( ( ret != 0 ) || ( is_supported == 0 ) ) + { + /* No parser found, skip extension */ + *p = end_ext_octet; +@@ -595,6 +596,10 @@ static int x509_get_crt_ext( unsigned char **p, + #if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) + if( is_critical ) + { ++ /* Do not fail if extension is found, but unsupported and allowed in runtime */ ++ if( ( ret == 0 ) && ( ext_type & crt->allowed_unsupported_critical_exts ) ) ++ continue; ++ + /* Data is marked as critical: fail */ + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); +@@ -948,6 +953,7 @@ int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *bu + + prev = crt; + mbedtls_x509_crt_init( crt->next ); ++ crt->next->allowed_unsupported_critical_exts = crt->allowed_unsupported_critical_exts; + crt = crt->next; + } + +diff --git a/tests/data_files/test-ca-nc.crt b/tests/data_files/test-ca-nc.crt +new file mode 100644 +index 00000000..7e0c5613 +--- /dev/null ++++ b/tests/data_files/test-ca-nc.crt +@@ -0,0 +1,20 @@ ++-----BEGIN CERTIFICATE----- ++MIIDSzCCAjOgAwIBAgIJAJx/NjT4C4viMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV ++BAMMCExlZXZpQ0E0MB4XDTE4MDEyNzE1MDczMloXDTI4MDEyNTE1MDczMlowEzER ++MA8GA1UEAwwITGVldmlDQTQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB ++AQDWN79RTlyFm5o0LVMSVjc68W0+gtl95xpaaD7IS6gDYjcbGnCwSefiq7y9rtck ++OM1A5Bzhj5+iWbmZStUmeJUhSGgxP/FxuUaAV0fsBGJ5jDrzmbhzDkHsNxDMB2ks ++XFyy4LfODcBs9TXxY43KUKuq/0meiT3WAaZWHMYle9vkQJM2l0RyH4IXHCHiIRwd ++2wntin6T9QOFJOc2ietNb7KsXVne81wb7h9BVMsjCIAsbPpHa+PZQs1xFuxmRxCs ++kpavnMy+SqevHhvqtvbHppcXYtZspTnkVoXWUdx3HHXgZMQKlAWlwyx57xpZBU2g ++qksO+KCLVYOQMN9usmuMOpHHAgMBAAGjgaEwgZ4wHQYDVR0eAQH/BBMwEaAPMA2C ++C2V4YW1wbGUuY29tMB0GA1UdDgQWBBR3T9IilPeRAFfLO8ocg216OBo+6DBDBgNV ++HSMEPDA6gBR3T9IilPeRAFfLO8ocg216OBo+6KEXpBUwEzERMA8GA1UEAwwITGVl ++dmlDQTSCCQCcfzY0+AuL4jAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq ++hkiG9w0BAQsFAAOCAQEAR086ciNM3ujSQNhhguqFHYGfDRRuAgOk4l7GXIfFa9te ++B2KMLSwP367QaMwFxRrOoDvixIjzbpiiKB3cv+IXqGyfsRJw47XLwGK4FtSsXjst ++m2M8W5iXBQ94XoLj9OKb4ZJWKI930S/PF7uuxICtWttYSoylfyMkiR45+1SLj2eF ++X4EnXK3Q0H42v8LCDFqj9iNQ2WMLwA7kFPB+oOZxkFi2G0F3VuW+JZeBPQCpYdRO ++0kQQ/gIZE6KEdscKHi9y6OfGSeRlDBMADky9NiZy7I3AcspLcmMQh/191/DnooNe ++OwQ6w1HweApjB46bGyILpGUi9MZhvCnoLWg+cN3/wQ== ++-----END CERTIFICATE----- +diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data +index b8c902e2..e7dcb61f 100644 +--- a/tests/suites/test_suite_x509parse.data ++++ b/tests/suites/test_suite_x509parse.data +@@ -1574,6 +1574,12 @@ X509 File parse (trailing spaces, OK) + depends_on:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C + x509parse_crt_file:"data_files/server7_trailing_space.crt":0 + ++X509 File parse (unsupported critical ext Name Constraints, fail) ++x509parse_crt_file:"data_files/test-ca-nc.crt":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ++ ++X509 File parse (allowed unsupported critical ext Name Constraints, ok) ++x509parse_crt_file_allow_exts:"data_files/test-ca-nc.crt":MBEDTLS_X509_EXT_NAME_CONSTRAINTS:0 ++ + X509 Get time (UTC no issues) + depends_on:MBEDTLS_X509_USE_C + x509_get_time:MBEDTLS_ASN1_UTC_TIME:"500101000000Z":0:1950:1:1:0:0:0 +diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function +index 0dfdd61c..2be1defd 100644 +--- a/tests/suites/test_suite_x509parse.function ++++ b/tests/suites/test_suite_x509parse.function +@@ -395,6 +395,21 @@ exit: + } + /* END_CASE */ + ++/* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C:MBEDTLS_FS_IO */ ++void x509parse_crt_file_allow_exts( char *crt_file, int exts, int result ) ++{ ++ mbedtls_x509_crt crt; ++ ++ mbedtls_x509_crt_init( &crt ); ++ crt.allowed_unsupported_critical_exts = exts; ++ ++ TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == result ); ++ ++exit: ++ mbedtls_x509_crt_free( &crt ); ++} ++/* END_CASE */ ++ + /* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C */ + void x509parse_crt( char *crt_data, char *result_str, int result ) + { +-- +2.14.3 (Apple Git-98) + diff --git a/docker-build.sh b/docker-build.sh new file mode 100755 index 0000000..97bbe21 --- /dev/null +++ b/docker-build.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# build ovpn3-core with system-provided mbedtls and lz4 on various linux distros + +docker build -f dockerfiles/Dockerfile.debian -t deb . +docker run -it deb + +docker build -f dockerfiles/Dockerfile.ubu -t ubu . +docker run -it ubu + +docker build -f dockerfiles/Dockerfile.centos -t cnt . +docker run -it cnt diff --git a/dockerfiles/Dockerfile.centos b/dockerfiles/Dockerfile.centos new file mode 100644 index 0000000..32d1088 --- /dev/null +++ b/dockerfiles/Dockerfile.centos @@ -0,0 +1,19 @@ +FROM centos/devtoolset-7-toolchain-centos7 + +USER 0 +RUN yum -y update && yum -y install epel-release && \ + yum -y install -y mbedtls-devel lz4-devel git wget perl-Digest-SHA make + +ADD . /ovpn3/core + +ENV O3 /ovpn3/ +ENV DEP_DIR /ovpn3/deps +ENV DL /ovpn3/dl + +CMD mkdir $DEP_DIR && mkdir $DL && \ + /ovpn3/core/scripts/linux/build-all && \ + cd $O3/core/test/ovpncli && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build cli && \ + cd $O3/core/test/ssl && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build proto && \ + ./proto diff --git a/dockerfiles/Dockerfile.debian b/dockerfiles/Dockerfile.debian new file mode 100644 index 0000000..3aaceaa --- /dev/null +++ b/dockerfiles/Dockerfile.debian @@ -0,0 +1,17 @@ +FROM debian:9 + +RUN apt-get update && apt-get install -y autoconf build-essential wget git liblz4-dev libmbedtls-dev + +ADD . /ovpn3/core + +ENV O3 /ovpn3/ +ENV DEP_DIR /ovpn3/deps +ENV DL /ovpn3/dl + +CMD mkdir $DEP_DIR && mkdir $DL && \ + /ovpn3/core/scripts/linux/build-all && \ + cd $O3/core/test/ovpncli && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build cli && \ + cd $O3/core/test/ssl && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build proto && \ + ./proto diff --git a/dockerfiles/Dockerfile.ubu b/dockerfiles/Dockerfile.ubu new file mode 100644 index 0000000..1502a28 --- /dev/null +++ b/dockerfiles/Dockerfile.ubu @@ -0,0 +1,17 @@ +FROM ubuntu:16.04 + +RUN apt-get update && apt-get install -y autoconf build-essential wget git liblz4-dev libmbedtls-dev + +ADD . /ovpn3/core + +ENV O3 /ovpn3/ +ENV DEP_DIR /ovpn3/deps +ENV DL /ovpn3/dl + +CMD mkdir $DEP_DIR && mkdir $DL && \ + /ovpn3/core/scripts/linux/build-all && \ + cd $O3/core/test/ovpncli && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build cli && \ + cd $O3/core/test/ssl && \ + ECHO=1 PROF=linux ASIO=1 MTLS_SYS=1 LZ4_SYS=1 NOSSL=1 $O3/core/scripts/build proto && \ + ./proto diff --git a/openvpn/client/clievent.hpp b/openvpn/client/clievent.hpp index 7a4cf6b..d865646 100644 --- a/openvpn/client/clievent.hpp +++ b/openvpn/client/clievent.hpp @@ -54,6 +54,7 @@ namespace openvpn { PAUSE, RESUME, RELAY, + UNSUPPORTED_FEATURE, // start of nonfatal errors, must be marked by NONFATAL_ERROR_START below TRANSPORT_ERROR, @@ -105,6 +106,7 @@ namespace openvpn { "PAUSE", "RESUME", "RELAY", + "UNSUPPORTED_FEATURE", // nonfatal errors "TRANSPORT_ERROR", @@ -253,6 +255,28 @@ namespace openvpn { TLSVersionMinFail() : Base(TLS_VERSION_MIN) {} }; + struct UnsupportedFeature : public Base + { + typedef RCPtr Ptr; + + UnsupportedFeature(const std::string& name_arg, const std::string& reason_arg, bool critical_arg) + : Base(UNSUPPORTED_FEATURE), + name(name_arg), + reason(reason_arg), + critical(critical_arg) {} + + std::string name; + std::string reason; + bool critical; + + virtual std::string render() const + { + std::ostringstream out; + out << "name: " << name << ", reason: " << reason << ", critical: " << critical; + return out.str(); + } + }; + struct Connected : public Base { typedef RCPtr Ptr; diff --git a/openvpn/client/cliopt.hpp b/openvpn/client/cliopt.hpp index 29a9116..419fa6b 100644 --- a/openvpn/client/cliopt.hpp +++ b/openvpn/client/cliopt.hpp @@ -667,6 +667,9 @@ namespace openvpn { if (relay_mode) lflags |= SSLConfigAPI::LF_RELAY_MODE; + if (opt.exists("allow-name-constraints")) + lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS; + // client SSL config SSLLib::SSLAPI::Config::Ptr cc(new SSLLib::SSLAPI::Config()); cc->set_external_pki_callback(config.external_pki); @@ -730,7 +733,6 @@ namespace openvpn { #endif #else - if (dco) { DCO::TransportConfig transconf; @@ -789,7 +791,11 @@ namespace openvpn { #endif transport_factory = udpconf; } - else if (transport_protocol.is_tcp()) + else if (transport_protocol.is_tcp() +#ifdef OPENVPN_TLS_LINK + || transport_protocol.is_tls() +#endif + ) { // TCP transport TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj(); @@ -797,6 +803,10 @@ namespace openvpn { tcpconf->frame = frame; tcpconf->stats = cli_stats; tcpconf->socket_protect = socket_protect; +#ifdef OPENVPN_TLS_LINK + if (transport_protocol.is_tls()) + tcpconf->use_tls = true; +#endif #ifdef OPENVPN_GREMLIN tcpconf->gremlin_config = gremlin_config; #endif diff --git a/openvpn/client/cliopthelper.hpp b/openvpn/client/cliopthelper.hpp index 4fa11f0..7d27fce 100644 --- a/openvpn/client/cliopthelper.hpp +++ b/openvpn/client/cliopthelper.hpp @@ -291,10 +291,15 @@ namespace openvpn { protoConfig->load(options, ProtoContextOptions(), -1, false); } + unsigned int lflags = SSLConfigAPI::LF_PARSE_MODE; + + if (options.exists("allow-name-constraints")) + lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS; + // ssl lib configuration try { sslConfig.reset(new SSLLib::SSLAPI::Config()); - sslConfig->load(options, SSLConfigAPI::LF_PARSE_MODE); + sslConfig->load(options, lflags); } catch (...) { sslConfig.reset(); } diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index 74f8e1f..0ebb576 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -791,10 +791,16 @@ namespace openvpn { uint32_t tls_warnings = get_tls_warnings(); if (tls_warnings & SSLAPI::TLS_WARN_SIG_MD5) - { - ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: received certificate signed with MD5. Please inform your admin to upgrade to a stronger algorithm. Support for MD5 will be dropped at end of Apr 2018"); - cli_events->add_event(std::move(ev)); - } + { + ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: received certificate signed with MD5. Please inform your admin to upgrade to a stronger algorithm. Support for MD5 will be dropped at end of Apr 2018"); + cli_events->add_event(std::move(ev)); + } + + if (tls_warnings & SSLAPI::TLS_WARN_NAME_CONSTRAINTS) + { + ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: Your CA contains a 'x509v3 Name Constraints' extension, but its validation is not supported. This might be a security breach, please contact your administrator."); + cli_events->add_event(std::move(ev)); + } } // base class calls here when primary session transitions to ACTIVE state diff --git a/openvpn/mbedtls/pki/x509cert.hpp b/openvpn/mbedtls/pki/x509cert.hpp index b4ba7e2..11b8434 100644 --- a/openvpn/mbedtls/pki/x509cert.hpp +++ b/openvpn/mbedtls/pki/x509cert.hpp @@ -130,12 +130,12 @@ namespace openvpn { return chain; } - ~X509Cert() + virtual ~X509Cert() { dealloc(); } - private: + protected: void alloc() { if (!chain) @@ -145,6 +145,9 @@ namespace openvpn { } } + mbedtls_x509_crt *chain; + + private: void dealloc() { if (chain) @@ -155,8 +158,6 @@ namespace openvpn { } } - mbedtls_x509_crt *chain; - static const std::string begin_cert; static const std::string end_cert; }; diff --git a/openvpn/mbedtls/ssl/sslctx.hpp b/openvpn/mbedtls/ssl/sslctx.hpp index c784986..d043770 100644 --- a/openvpn/mbedtls/ssl/sslctx.hpp +++ b/openvpn/mbedtls/ssl/sslctx.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -211,7 +212,8 @@ namespace openvpn { tls_cert_profile(TLSCertProfile::UNDEF), local_cert_enabled(true), enable_renegotiation(false), - force_aes_cbc_ciphersuites(false) {} + force_aes_cbc_ciphersuites(false), + allow_name_constraints(false) {} virtual SSLFactoryAPI::Ptr new_factory() { @@ -456,6 +458,8 @@ namespace openvpn { && opt.exists("client-cert-not-required")) flags |= SSLConst::NO_VERIFY_PEER; + allow_name_constraints = lflags & LF_ALLOW_NAME_CONSTRAINTS; + // ca { std::string ca_txt = opt.cat("ca"); @@ -532,6 +536,16 @@ namespace openvpn { } } + bool name_constraints_allowed() const + { + return allow_name_constraints; + } + + bool is_server() const + { + return mode.is_server(); + } + private: const mbedtls_x509_crt_profile *select_crt_profile() const { @@ -553,8 +567,12 @@ namespace openvpn { } Mode mode; + + protected: MbedTLSPKI::X509Cert::Ptr crt_chain; // local cert chain (including client cert + extra certs) MbedTLSPKI::X509Cert::Ptr ca_chain; // CA chain for remote verification + + private: MbedTLSPKI::X509CRL::Ptr crl_chain; // CRL chain for remote verification MbedTLSPKI::PKContext::Ptr priv_key; // private key std::string priv_key_pwd; // private key password @@ -573,6 +591,7 @@ namespace openvpn { bool local_cert_enabled; bool enable_renegotiation; bool force_aes_cbc_ciphersuites; + bool allow_name_constraints; RandomAPI::Ptr rng; // random data source }; @@ -649,6 +668,14 @@ namespace openvpn { overflow = true; } + virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) + { + if (ct_in.size() < MAX_CIPHERTEXT_IN) + ct_in.write(data, size); + else + overflow = true; + } + virtual bool read_ciphertext_ready() const { return !ct_out.empty(); @@ -676,12 +703,12 @@ namespace openvpn { return authcert; } - ~SSL() + virtual ~SSL() { erase(); } - private: + protected: SSL(MbedTLSContext* ctx, const char *hostname) { clear(); @@ -863,6 +890,10 @@ namespace openvpn { } } + mbedtls_ssl_config *sslconf; // SSL configuration parameters for SSL connection object + MbedTLSContext *parent; + + private: // cleartext read callback static int ct_read_func(void *arg, unsigned char *data, size_t length) { @@ -925,9 +956,7 @@ namespace openvpn { clear(); } - MbedTLSContext *parent; mbedtls_ssl_context *ssl; // underlying SSL connection object - mbedtls_ssl_config *sslconf; // SSL configuration parameters for SSL connection object MbedTLSPKI::PKContext epki_ctx; // external PKI context RandomAPI::Ptr rng; // random data source MemQStream ct_in; // write ciphertext to here @@ -954,13 +983,13 @@ namespace openvpn { { return config->mode; } - - ~MbedTLSContext() + + virtual ~MbedTLSContext() { erase(); } - private: + protected: MbedTLSContext(Config* config_arg) : config(config_arg) { @@ -972,6 +1001,7 @@ namespace openvpn { } } + private: size_t key_len() const { return mbedtls_pk_get_bitlen(&config->crt_chain->get()->pk) / 8; @@ -1114,6 +1144,7 @@ namespace openvpn { return os.str(); } + protected: static int verify_callback_client(void *arg, mbedtls_x509_crt *cert, int depth, uint32_t *flags) { MbedTLSContext::SSL *ssl = (MbedTLSContext::SSL *)arg; @@ -1231,6 +1262,9 @@ namespace openvpn { return 0; } + Config::Ptr config; + + private: static std::string cert_info(const mbedtls_x509_crt *cert, const char *prefix = nullptr) { const size_t buf_size = 4096; @@ -1354,8 +1388,6 @@ namespace openvpn { MbedTLSContext *self = (MbedTLSContext *) arg; return self->key_len(); } - - Config::Ptr config; }; } // namespace openvpn diff --git a/openvpn/openssl/ssl/sslctx.hpp b/openvpn/openssl/ssl/sslctx.hpp index cfc8d15..ead66ed 100644 --- a/openvpn/openssl/ssl/sslctx.hpp +++ b/openvpn/openssl/ssl/sslctx.hpp @@ -469,6 +469,15 @@ namespace openvpn { overflow = true; } + virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) + { + bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in); + if (in->size() < MAX_CIPHERTEXT_IN) + in->write(data, size); + else + overflow = true; + } + virtual bool read_ciphertext_ready() const { return !bmq_stream::memq_from_bio(ct_out)->empty(); diff --git a/openvpn/ssl/sslapi.hpp b/openvpn/ssl/sslapi.hpp index 60b3412..378a6e1 100644 --- a/openvpn/ssl/sslapi.hpp +++ b/openvpn/ssl/sslapi.hpp @@ -51,6 +51,7 @@ namespace openvpn { enum TLSWarnings { TLS_WARN_SIG_MD5 = (1 << 0), + TLS_WARN_NAME_CONSTRAINTS = (1 << 1) }; typedef RCPtr Ptr; @@ -60,6 +61,7 @@ namespace openvpn { virtual ssize_t read_cleartext(void *data, const size_t capacity) = 0; virtual bool read_cleartext_ready() const = 0; virtual void write_ciphertext(const BufferPtr& buf) = 0; + virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) = 0; virtual bool read_ciphertext_ready() const = 0; virtual BufferPtr read_ciphertext() = 0; virtual std::string ssl_handshake_details() const = 0; @@ -109,9 +111,10 @@ namespace openvpn { }; enum LoadFlags { - LF_PARSE_MODE = (1<<0), + LF_PARSE_MODE = (1<<0), LF_ALLOW_CLIENT_CERT_NOT_REQUIRED = (1<<1), - LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive + LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive + LF_ALLOW_NAME_CONSTRAINTS = (1<<3) // do not fail on Name Constraints ext and drop a warning to UI }; std::string private_key_type_string() const diff --git a/openvpn/ssl/sslchoose.hpp b/openvpn/ssl/sslchoose.hpp index cc3a257..285260e 100644 --- a/openvpn/ssl/sslchoose.hpp +++ b/openvpn/ssl/sslchoose.hpp @@ -40,6 +40,9 @@ #include #include #include +#ifdef HAVE_OPENVPN_COMMON +#include +#endif #ifdef OPENVPN_PLATFORM_UWP #include #endif @@ -56,7 +59,11 @@ namespace openvpn { #if defined(USE_MBEDTLS) #define SSL_LIB_NAME "MbedTLS" typedef MbedTLSCryptoAPI CryptoAPI; - typedef MbedTLSContext SSLAPI; + #ifdef HAVE_OPENVPN_COMMON + typedef MbedTLSContextNameConstraints SSLAPI; + #else + typedef MbedTLSContext SSLAPI; + #endif #if defined OPENVPN_PLATFORM_UWP typedef MbedTLSRandomWithUWPEntropy RandomAPI; #else diff --git a/openvpn/transport/client/httpcli.hpp b/openvpn/transport/client/httpcli.hpp index cf355a9..83c865f 100644 --- a/openvpn/transport/client/httpcli.hpp +++ b/openvpn/transport/client/httpcli.hpp @@ -216,7 +216,7 @@ namespace openvpn { typedef TCPTransport::Link LinkImpl; friend class ClientConfig; // calls constructor - friend LinkImpl; // calls tcp_read_handler + friend LinkImpl::Base; // calls tcp_read_handler public: virtual void transport_start() diff --git a/openvpn/transport/client/tcpcli.hpp b/openvpn/transport/client/tcpcli.hpp index 9cd0106..ba3c33a 100644 --- a/openvpn/transport/client/tcpcli.hpp +++ b/openvpn/transport/client/tcpcli.hpp @@ -29,6 +29,9 @@ #include #include +#ifdef OPENVPN_TLS_LINK +#include +#endif #include #include #include @@ -48,6 +51,10 @@ namespace openvpn { SocketProtect* socket_protect; +#ifdef OPENVPN_TLS_LINK + bool use_tls = false; +#endif + #ifdef OPENVPN_GREMLIN Gremlin::Config::Ptr gremlin_config; #endif @@ -72,9 +79,12 @@ namespace openvpn { typedef RCPtr Ptr; typedef Link LinkImpl; +#ifdef OPENVPN_TLS_LINK + typedef TLSLink LinkImplTLS; +#endif friend class ClientConfig; // calls constructor - friend LinkImpl; // calls tcp_read_handler + friend LinkImpl::Base; // calls tcp_read_handler public: virtual void transport_start() @@ -207,24 +217,24 @@ namespace openvpn { return false; } - void tcp_eof_handler() // called by LinkImpl + void tcp_eof_handler() // called by LinkImpl::Base { config->stats->error(Error::NETWORK_EOF_ERROR); tcp_error_handler("NETWORK_EOF_ERROR"); } - bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl + bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl::Base { parent->transport_recv(buf); return !stop_requeueing; } - void tcp_write_queue_needs_send() // called by LinkImpl + void tcp_write_queue_needs_send() // called by LinkImpl::Base { parent->transport_needs_send(); } - void tcp_error_handler(const char *error) // called by LinkImpl + void tcp_error_handler(const char *error) // called by LinkImpl::Base { std::ostringstream os; os << "Transport error on '" << server_host << ": " << error; @@ -302,12 +312,35 @@ namespace openvpn { { if (!error) { - impl.reset(new LinkImpl(this, - socket, - 0, // // send_queue_max_size is unlimited because we regulate size in cliproto.hpp - config->free_list_max_size, - (*config->frame)[Frame::READ_LINK_TCP], - config->stats)); +#ifdef OPENVPN_TLS_LINK + if (config->use_tls) + { + SSLLib::SSLAPI::Config::Ptr ssl_conf; + ssl_conf.reset(new SSLLib::SSLAPI::Config()); + ssl_conf->set_mode(Mode(Mode::CLIENT)); + ssl_conf->set_flags(SSLConst::LOG_VERIFY_STATUS|SSLConst::NO_VERIFY_PEER); + ssl_conf->set_local_cert_enabled(false); + ssl_conf->set_frame(config->frame); + ssl_conf->set_rng(new SSLLib::RandomAPI(false)); + + impl.reset(new LinkImplTLS(this, + io_context, + socket, + 0, + config->free_list_max_size, + config->frame, + config->stats, + ssl_conf->new_factory())); + } + else +#endif + impl.reset(new LinkImpl(this, + socket, + 0, // send_queue_max_size is unlimited because we regulate size in cliproto.hpp + config->free_list_max_size, + (*config->frame)[Frame::READ_LINK_TCP], + config->stats)); + #ifdef OPENVPN_GREMLIN impl->gremlin_config(config->gremlin_config); #endif @@ -334,9 +367,9 @@ namespace openvpn { openvpn_io::ip::tcp::socket socket; ClientConfig::Ptr config; TransportClientParent* parent; - LinkImpl::Ptr impl; + LinkBase::Ptr impl; openvpn_io::ip::tcp::resolver resolver; - LinkImpl::protocol::endpoint server_endpoint; + LinkImpl::Base::protocol::endpoint server_endpoint; bool halt; bool stop_requeueing; }; diff --git a/openvpn/transport/protocol.hpp b/openvpn/transport/protocol.hpp index 7d6b32d..834c100 100644 --- a/openvpn/transport/protocol.hpp +++ b/openvpn/transport/protocol.hpp @@ -41,11 +41,14 @@ namespace openvpn { TCPv4, UDPv6, TCPv6, + TLSv4, // TLS over IPv4 + TLSv6, // TLS over IPv6 UnixStream, // unix domain socket (stream) UnixDGram, // unix domain socket (datagram) NamedPipe, // named pipe (Windows only) UDP=UDPv4, TCP=TCPv4, + TLS=TLSv4, }; enum AllowSuffix { @@ -64,8 +67,9 @@ namespace openvpn { bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; } bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; } - bool is_reliable() const { return is_tcp(); } - bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6; } + bool is_tls() const { return type_ == TLSv4 || type_ == TLSv6; } + bool is_reliable() const { return is_tcp() || is_tls(); } + bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6 || type_ == TLSv6; } bool is_unix() const { return type_ == UnixStream || type_ == UnixDGram; } bool is_named_pipe() const { return type_ == NamedPipe; } bool is_local() const { return is_unix() || is_named_pipe(); } @@ -87,7 +91,7 @@ namespace openvpn { unsigned int extra_transport_bytes() const { - return is_tcp() ? sizeof(std::uint16_t) : 0; + return (is_tcp() || is_tls()) ? sizeof(std::uint16_t) : 0; } void mod_addr_version(const IP::Addr& addr) @@ -101,12 +105,16 @@ namespace openvpn { type_ = UDPv4; else if (is_tcp()) type_ = TCPv4; + else if (is_tls()) + type_ = TLSv4; break; case IP::Addr::V6: if (is_udp()) type_ = UDPv6; else if (is_tcp()) type_ = TCPv6; + else if (is_tls()) + type_ = TLSv6; break; } } @@ -157,6 +165,9 @@ namespace openvpn { return 3; case NamedPipe: return 4; + case TLSv4: + case TLSv6: + return 5; default: return -1; } @@ -174,6 +185,10 @@ namespace openvpn { return "UDPv6"; case TCPv6: return "TCPv6"; + case TLSv4: + return "TLSv4"; + case TLSv6: + return "TLSv6"; case UnixStream: return "UnixStream"; case UnixDGram: @@ -199,6 +214,10 @@ namespace openvpn { return "udp6"; case TCPv6: return "tcp6"; + case TLSv4: + return "tls4"; + case TLSv6: + return "tls6"; case UnixStream: return "unix-stream"; case UnixDGram: @@ -224,6 +243,10 @@ namespace openvpn { return force_ipv4 ? "UDPv4" : "UDPv6"; case TCPv6: return force_ipv4 ? "TCPv4_CLIENT" : "TCPv6_CLIENT"; + case TLSv4: + return "TLSv4"; + case TLSv6: + return force_ipv4 ? "TLSv4" : "TLSv6"; default: return "UNDEF_PROTO"; } @@ -268,6 +291,8 @@ namespace openvpn { ret = UDPv4; else if (s1 == "tcp") ret = TCPv4; + else if (s1 == "tls") + ret = TLSv4; } else if (s2 == "6" || s2 == "v6") { @@ -275,6 +300,8 @@ namespace openvpn { ret = UDPv6; else if (s1 == "tcp") ret = TCPv6; + else if (s1 == "tls") + ret = TLSv6; } } return ret; diff --git a/openvpn/transport/tcplink.hpp b/openvpn/transport/tcplink.hpp index 5499b5f..e48cabc 100644 --- a/openvpn/transport/tcplink.hpp +++ b/openvpn/transport/tcplink.hpp @@ -37,401 +37,69 @@ #include #include #include +#include #ifdef OPENVPN_GREMLIN #include #endif -#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1 -#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x) -#else -#define OPENVPN_LOG_TCPLINK_ERROR(x) -#endif - -#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3 -#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x) -#else -#define OPENVPN_LOG_TCPLINK_VERBOSE(x) -#endif - namespace openvpn { namespace TCPTransport { - struct PacketFrom - { - typedef std::unique_ptr SPtr; - BufferAllocated buf; - }; - template - class Link : public RC + class Link : public LinkCommon { typedef std::deque Queue; public: + typedef LinkCommon Base; typedef RCPtr Ptr; typedef Protocol protocol; + friend Base; + Link(ReadHandler read_handler_arg, typename Protocol::socket& socket_arg, const size_t send_queue_max_size_arg, // 0 to disable const size_t free_list_max_size_arg, const Frame::Context& frame_context_arg, const SessionStats::Ptr& stats_arg) - : socket(socket_arg), - halt(false), - read_handler(read_handler_arg), - frame_context(frame_context_arg), - stats(stats_arg), - send_queue_max_size(send_queue_max_size_arg), - free_list_max_size(free_list_max_size_arg) - { - set_raw_mode(false); - } - -#ifdef OPENVPN_GREMLIN - void gremlin_config(const Gremlin::Config::Ptr& config) - { - if (config) - gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true)); - } -#endif - - // In raw mode, data is sent and received without any special encapsulation. - // In non-raw mode, data is packetized by prepending a 16-bit length word - // onto each packet. The OpenVPN protocol runs in non-raw mode, while other - // TCP protocols such as HTTP or HTTPS would run in raw mode. - // This method is a no-op if RAW_MODE_ONLY is true. - void set_raw_mode(const bool mode) - { - set_raw_mode_read(mode); - set_raw_mode_write(mode); - } - - void set_raw_mode_read(const bool mode) - { - if (RAW_MODE_ONLY) - raw_mode_read = true; - else - raw_mode_read = mode; - } - - void set_raw_mode_write(const bool mode) - { - if (RAW_MODE_ONLY) - raw_mode_write = true; - else - raw_mode_write = mode; - } - - bool is_raw_mode() const { - return is_raw_mode_read() && is_raw_mode_write(); - } - - bool is_raw_mode_read() const { - if (RAW_MODE_ONLY) - return true; - else - return raw_mode_read; - } - - bool is_raw_mode_write() const { - if (RAW_MODE_ONLY) - return true; - else - return raw_mode_write; - } - - void set_mutate(const TransportMutateStream::Ptr& mutate_arg) - { - mutate = mutate_arg; - } - - bool send_queue_empty() const - { - return send_queue_size() == 0; - } + : Base(read_handler_arg, socket_arg, send_queue_max_size_arg, + free_list_max_size_arg, frame_context_arg, stats_arg) + { } + // Called by LinkCommon and TCPTransport Client class unsigned int send_queue_size() const { - return queue.size() + return Base::queue.size() #ifdef OPENVPN_GREMLIN + (gremlin ? gremlin->send_size() : 0) #endif ; } - bool send(BufferAllocated& b) - { - if (halt) - return false; - - if (send_queue_max_size && queue.size() >= send_queue_max_size) - { - stats->error(Error::TCP_OVERFLOW); - read_handler->tcp_error_handler("TCP_OVERFLOW"); - stop(); - return false; - } - - BufferPtr buf; - if (!free_list.empty()) - { - buf = free_list.front(); - free_list.pop_front(); - } - else - buf.reset(new BufferAllocated()); - buf->swap(b); - if (!is_raw_mode_write()) - PacketStream::prepend_size(*buf); - if (mutate) - mutate->pre_send(*buf); -#ifdef OPENVPN_GREMLIN - if (gremlin) - gremlin_queue_send_buffer(buf); - else -#endif - queue_send_buffer(buf); - return true; - } - - void inject(const Buffer& src) - { - const size_t size = src.size(); - OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size); - if (size && !RAW_MODE_ONLY) - { - BufferAllocated buf; - frame_context.prepare(buf); - buf.write(src.c_data(), size); - BufferAllocated pkt; - put_pktstream(buf, pkt); - } - } - - void start() - { - if (!halt) - queue_recv(nullptr); - } - - void stop() - { - halt = true; -#ifdef OPENVPN_GREMLIN - if (gremlin) - gremlin->stop(); -#endif - } - - void reset_align_adjust(const size_t align_adjust) - { - frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2)); - } - - ~Link() { stop(); } - private: - void queue_send_buffer(BufferPtr& buf) + // Called by LinkCommon + virtual void from_app_send_buffer(BufferPtr& buf) override { - queue.push_back(std::move(buf)); - if (queue.size() == 1) // send operation not currently active? - queue_send(); + Base::queue_send_buffer(buf); } - void queue_send() - { - BufferAllocated& buf = *queue.front(); - socket.async_send(buf.const_buffer_clamp(), - [self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_sent) - { - self->handle_send(error, bytes_sent); - }); - } - - void handle_send(const openvpn_io::error_code& error, const size_t bytes_sent) - { - if (!halt) - { - if (!error) - { - OPENVPN_LOG_TCPLINK_VERBOSE("TCP send raw=" << raw_mode_write << " size=" << bytes_sent); - stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent); - stats->inc_stat(SessionStats::PACKETS_OUT, 1); - - BufferPtr buf = queue.front(); - if (bytes_sent == buf->size()) - { - queue.pop_front(); - if (free_list.size() < free_list_max_size) - { - buf->reset_content(); - free_list.push_back(std::move(buf)); // recycle the buffer for later use - } - } - else if (bytes_sent < buf->size()) - buf->advance(bytes_sent); - else - { - stats->error(Error::TCP_OVERFLOW); - read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for - stop(); - return; - } - } - else - { - OPENVPN_LOG_TCPLINK_ERROR("TCP send error: " << error.message()); - stats->error(Error::NETWORK_SEND_ERROR); - read_handler->tcp_error_handler("NETWORK_SEND_ERROR"); - stop(); - return; - } - if (!queue.empty()) - queue_send(); - else - read_handler->tcp_write_queue_needs_send(); - } - } - - void queue_recv(PacketFrom *tcpfrom) - { - OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::queue_recv"); - if (!tcpfrom) - tcpfrom = new PacketFrom(); - frame_context.prepare(tcpfrom->buf); - - socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf), - [self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable - { - self->handle_recv(std::move(tcpfrom), error, bytes_recvd); - }); - } - - void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd) - { - OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::handle_recv: " << error.message()); - if (!halt) - { - if (!error) - { - bool requeue = true; - OPENVPN_LOG_TCPLINK_VERBOSE("TCP recv raw=" << raw_mode_read << " size=" << bytes_recvd); - pfp->buf.set_size(bytes_recvd); - if (!is_raw_mode_read()) - { - try { - BufferAllocated pkt; - requeue = put_pktstream(pfp->buf, pkt); - if (!pfp->buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer - pfp->buf.move(pkt); - } - catch (const std::exception& e) - { - OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what()); - stats->error(Error::TCP_SIZE_ERROR); - read_handler->tcp_error_handler("TCP_SIZE_ERROR"); - stop(); - return; - } - } - else - { - if (mutate) - mutate->post_recv(pfp->buf); -#ifdef OPENVPN_GREMLIN - if (gremlin) - requeue = gremlin_recv(pfp->buf); - else -#endif - requeue = read_handler->tcp_read_handler(pfp->buf); - } - if (!halt && requeue) - queue_recv(pfp.release()); // reuse PacketFrom object - } - else if (error == openvpn_io::error::eof) - { - OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF"); - read_handler->tcp_eof_handler(); - } - else - { - OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message()); - stats->error(Error::NETWORK_RECV_ERROR); - read_handler->tcp_error_handler("NETWORK_RECV_ERROR"); - stop(); - } - } - } - - bool put_pktstream(BufferAllocated& buf, BufferAllocated& pkt) + virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) override { bool requeue = true; - stats->inc_stat(SessionStats::BYTES_IN, buf.size()); - stats->inc_stat(SessionStats::PACKETS_IN, 1); - if (mutate) - mutate->post_recv(buf); - while (buf.size()) - { - pktstream.put(buf, frame_context); - if (pktstream.ready()) - { - pktstream.get(pkt); -#ifdef OPENVPN_GREMLIN - if (gremlin) - requeue = gremlin_recv(pkt); - else -#endif - requeue = read_handler->tcp_read_handler(pkt); - } - } - return requeue; + OPENVPN_LOG_TCPLINK_VERBOSE("TCP recv raw=" << Base::raw_mode_read << " size=" << bytes_recvd); + + pfp->buf.set_size(bytes_recvd); + requeue = Base::process_recv_buffer(pfp->buf); + if (!Base::halt && requeue) + Base::queue_recv(pfp.release()); // reuse PacketFrom object } - -#ifdef OPENVPN_GREMLIN - void gremlin_queue_send_buffer(BufferPtr& buf) - { - gremlin->send_queue([self=Ptr(this), buf=std::move(buf)]() mutable { - if (!self->halt) - { - self->queue_send_buffer(buf); - } - }); - } - - bool gremlin_recv(BufferAllocated& buf) - { - gremlin->recv_queue([self=Ptr(this), buf=std::move(buf)]() mutable { - if (!self->halt) - { - const bool requeue = self->read_handler->tcp_read_handler(buf); - if (requeue) - self->queue_recv(nullptr); - } - }); - return false; - } -#endif - - typename Protocol::socket& socket; - bool halt; - bool raw_mode_read; - bool raw_mode_write; - ReadHandler read_handler; - Frame::Context frame_context; - SessionStats::Ptr stats; - const size_t send_queue_max_size; - const size_t free_list_max_size; - Queue queue; // send queue - Queue free_list; // recycled free buffers for send queue - PacketStream pktstream; - TransportMutateStream::Ptr mutate; - -#ifdef OPENVPN_GREMLIN - std::unique_ptr gremlin; -#endif }; } } // namespace openvpn diff --git a/openvpn/transport/tcplinkbase.hpp b/openvpn/transport/tcplinkbase.hpp new file mode 100644 index 0000000..a258f9d --- /dev/null +++ b/openvpn/transport/tcplinkbase.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2012-2018 OpenVPN Inc. + +// Base class for generic link objects. + +#include +#include + +#pragma once + +namespace openvpn +{ + namespace TCPTransport + { + struct PacketFrom + { + typedef std::unique_ptr SPtr; + BufferAllocated buf; + }; + + class LinkBase : public RC + { + protected: + virtual void recv_buffer(PacketFrom::SPtr& pfp, + const size_t bytes_recvd) = 0; + virtual void from_app_send_buffer(BufferPtr& buf) = 0; + + public: + typedef RCPtr Ptr; + + virtual bool send_queue_empty() const = 0; + virtual unsigned int send_queue_size() const = 0; + virtual void reset_align_adjust(const size_t align_adjust) = 0; + virtual bool send(BufferAllocated& b) = 0; + virtual void set_raw_mode(const bool mode) = 0; + virtual void start() = 0; + virtual void stop() = 0; + }; + } +} diff --git a/openvpn/transport/tcplinkcommon.hpp b/openvpn/transport/tcplinkcommon.hpp new file mode 100644 index 0000000..8ce808a --- /dev/null +++ b/openvpn/transport/tcplinkcommon.hpp @@ -0,0 +1,441 @@ +// Copyright (C) 2012-2018 OpenVPN Inc. + +// Base class for TCP link objects. + +#ifndef OPENVPN_TRANSPORT_COMMONLINK_H +#define OPENVPN_TRANSPORT_COMMONLINK_H + +#include +#include // for std::move +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENVPN_GREMLIN +#include +#endif + +#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1 +#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x) +#else +#define OPENVPN_LOG_TCPLINK_ERROR(x) +#endif + +#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3 +#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x) +#else +#define OPENVPN_LOG_TCPLINK_VERBOSE(x) +#endif + +namespace openvpn { + namespace TCPTransport { + + template + class LinkCommon : public LinkBase + { + typedef std::deque Queue; + + public: + typedef RCPtr> Ptr; + typedef Protocol protocol; + + // In raw mode, data is sent and received without any special encapsulation. + // In non-raw mode, data is packetized by prepending a 16-bit length word + // onto each packet. The OpenVPN protocol runs in non-raw mode, while other + // TCP protocols such as HTTP or HTTPS would run in raw mode. + // This method is a no-op if RAW_MODE_ONLY is true. + void set_raw_mode(const bool mode) + { + set_raw_mode_read(mode); + set_raw_mode_write(mode); + } + + void set_raw_mode_read(const bool mode) + { + if (RAW_MODE_ONLY) + raw_mode_read = true; + else + raw_mode_read = mode; + } + + void set_raw_mode_write(const bool mode) + { + if (RAW_MODE_ONLY) + raw_mode_write = true; + else + raw_mode_write = mode; + } + + void set_mutate(const TransportMutateStream::Ptr& mutate_arg) + { + mutate = mutate_arg; + } + + bool send_queue_empty() const + { + return send_queue_size() == 0; + } + + void inject(const Buffer& src) + { + const size_t size = src.size(); + OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size); + if (size && !RAW_MODE_ONLY) + { + BufferAllocated buf; + frame_context.prepare(buf); + buf.write(src.c_data(), size); + BufferAllocated pkt; + put_pktstream(buf, pkt); + } + } + + void start() + { + if (!halt) + queue_recv(nullptr); + } + + void stop() + { + halt = true; +#ifdef OPENVPN_GREMLIN + if (gremlin) + gremlin->stop(); +#endif + } + + void reset_align_adjust(const size_t align_adjust) + { + frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2)); + } + + unsigned int send_queue_size() const + { + return queue.size() +#ifdef OPENVPN_GREMLIN + + (gremlin ? gremlin->send_size() : 0) +#endif + ; + } + + bool send(BufferAllocated& b) + { + if (halt) + return false; + + if (send_queue_max_size && send_queue_size() >= send_queue_max_size) + { + stats->error(Error::TCP_OVERFLOW); + read_handler->tcp_error_handler("TCP_OVERFLOW"); + stop(); + return false; + } + + BufferPtr buf; + if (!free_list.empty()) + { + buf = free_list.front(); + free_list.pop_front(); + } + else + buf.reset(new BufferAllocated()); + buf->swap(b); + if (!is_raw_mode_write()) + PacketStream::prepend_size(*buf); + if (mutate) + mutate->pre_send(*buf); +#ifdef OPENVPN_GREMLIN + if (gremlin) + gremlin_queue_send_buffer(buf); + else +#endif + from_app_send_buffer(buf); + return true; + } + + void queue_recv(PacketFrom *tcpfrom) + { + OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::queue_recv"); + if (!tcpfrom) + tcpfrom = new PacketFrom(); + frame_context.prepare(tcpfrom->buf); + + socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf), + [self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable + { + try + { + self->handle_recv(std::move(tcpfrom), error, bytes_recvd); + } + catch (const std::exception& e) + { + OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what()); + self->stats->error(Error::TCP_SIZE_ERROR); + self->read_handler->tcp_error_handler("TCP_SIZE_ERROR"); + self->stop(); + } + }); + } + + protected: + LinkCommon(ReadHandler read_handler_arg, + typename Protocol::socket& socket_arg, + const size_t send_queue_max_size_arg, // 0 to disable + const size_t free_list_max_size_arg, + const Frame::Context& frame_context_arg, + const SessionStats::Ptr& stats_arg) + : socket(socket_arg), + halt(false), + read_handler(read_handler_arg), + frame_context(frame_context_arg), + stats(stats_arg), + send_queue_max_size(send_queue_max_size_arg), + free_list_max_size(free_list_max_size_arg) + { + set_raw_mode(false); + } + +#ifdef OPENVPN_GREMLIN + void gremlin_config(const Gremlin::Config::Ptr& config) + { + if (config) + gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true)); + } +#endif + + bool is_raw_mode() const { + return is_raw_mode_read() && is_raw_mode_write(); + } + + bool is_raw_mode_read() const { + if (RAW_MODE_ONLY) + return true; + else + return raw_mode_read; + } + + bool is_raw_mode_write() const { + if (RAW_MODE_ONLY) + return true; + else + return raw_mode_write; + } + + LinkCommon() { stop(); } + + void queue_send_buffer(BufferPtr& buf) + { + queue.push_back(std::move(buf)); + if (queue.size() == 1) // send operation not currently active? + queue_send(); + } + + void queue_send() + { + BufferAllocated& buf = *queue.front(); + socket.async_send(buf.const_buffer_clamp(), + [self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_sent) + { + self->handle_send(error, bytes_sent); + }); + } + + void handle_send(const openvpn_io::error_code& error, const size_t bytes_sent) + { + if (!halt) + { + if (!error) + { + OPENVPN_LOG_TCPLINK_VERBOSE("TLS-TCP send raw=" << raw_mode_write << " size=" << bytes_sent); + stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent); + stats->inc_stat(SessionStats::PACKETS_OUT, 1); + + BufferPtr buf = queue.front(); + if (bytes_sent == buf->size()) + { + queue.pop_front(); + if (free_list.size() < free_list_max_size) + { + buf->reset_content(); + free_list.push_back(std::move(buf)); // recycle the buffer for later use + } + } + else if (bytes_sent < buf->size()) + buf->advance(bytes_sent); + else + { + stats->error(Error::TCP_OVERFLOW); + read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for + stop(); + return; + } + } + else + { + OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP send error: " << error.message()); + stats->error(Error::NETWORK_SEND_ERROR); + read_handler->tcp_error_handler("NETWORK_SEND_ERROR"); + stop(); + return; + } + if (!queue.empty()) + queue_send(); + else + tcp_write_queue_needs_send(); + } + } + + bool process_recv_buffer(BufferAllocated& buf) + { + bool requeue = true; + + OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::process_recv_buffer: size=" << buf.size()); + + if (!is_raw_mode_read()) + { + try { + BufferAllocated pkt; + requeue = put_pktstream(buf, pkt); + if (!buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer + buf.move(pkt); + } + catch (const std::exception& e) + { + OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP packet extract error: " << e.what()); + stats->error(Error::TCP_SIZE_ERROR); + read_handler->tcp_error_handler("TCP_SIZE_ERROR"); + stop(); + return false; + } + } + else + { + if (mutate) + mutate->post_recv(buf); +#ifdef OPENVPN_GREMLIN + if (gremlin) + requeue = gremlin_recv(buf); + else +#endif + requeue = read_handler->tcp_read_handler(buf); + } + + return requeue; + } + + void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd) + { + OPENVPN_LOG_TCPLINK_VERBOSE("Link::handle_recv: " << error.message()); + if (!halt) + { + if (!error) + { + recv_buffer(pfp, bytes_recvd); + } + else if (error == openvpn_io::error::eof) + { + OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF"); + read_handler->tcp_eof_handler(); + } + else + { + OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message()); + stats->error(Error::NETWORK_RECV_ERROR); + read_handler->tcp_error_handler("NETWORK_RECV_ERROR"); + stop(); + } + } + } + + bool put_pktstream(BufferAllocated& buf, BufferAllocated& pkt) + { + bool requeue = true; + stats->inc_stat(SessionStats::BYTES_IN, buf.size()); + stats->inc_stat(SessionStats::PACKETS_IN, 1); + if (mutate) + mutate->post_recv(buf); + while (buf.size()) + { + pktstream.put(buf, frame_context); + if (pktstream.ready()) + { + pktstream.get(pkt); +#ifdef OPENVPN_GREMLIN + if (gremlin) + requeue = gremlin_recv(pkt); + else +#endif + requeue = read_handler->tcp_read_handler(pkt); + } + } + return requeue; + } + +#ifdef OPENVPN_GREMLIN + void gremlin_queue_send_buffer(BufferPtr& buf) + { + gremlin->send_queue([self=Ptr(this), buf=std::move(buf)]() mutable { + if (!self->halt) + { + self->queue_send_buffer(buf); + } + }); + } + + bool gremlin_recv(BufferAllocated& buf) + { + gremlin->recv_queue([self=Ptr(this), buf=std::move(buf)]() mutable { + if (!self->halt) + { + const bool requeue = self->read_handler->tcp_read_handler(buf); + if (requeue) + self->queue_recv(nullptr); + } + }); + return false; + } +#endif + + void tcp_write_queue_needs_send() + { + read_handler->tcp_write_queue_needs_send(); + } + + typename Protocol::socket& socket; + bool halt; + ReadHandler read_handler; + Frame::Context frame_context; + SessionStats::Ptr stats; + const size_t send_queue_max_size; + const size_t free_list_max_size; + Queue queue; // send queue + Queue free_list; // recycled free buffers for send queue + PacketStream pktstream; + TransportMutateStream::Ptr mutate; + bool raw_mode_read; + bool raw_mode_write; + +#ifdef OPENVPN_GREMLIN + std::unique_ptr gremlin; +#endif + + private: + virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) = 0; + virtual void from_app_send_buffer(BufferPtr& buf) = 0; + }; + } +} // namespace openvpn + +#endif diff --git a/test/ovpncli/go b/test/ovpncli/go index d059d22..dbad4ad 100755 --- a/test/ovpncli/go +++ b/test/ovpncli/go @@ -7,6 +7,7 @@ GCC_EXTRA="$GCC_EXTRA -DOPENVPN_SHOW_SESSION_TOKEN" [ "$DEX" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_DISABLE_EXPLICIT_EXIT" [ "$BS64" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_BS64_DATA_LIMIT=2500000" [ "$ROVER" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_REMOTE_OVERRIDE" +[ "$TLS" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_TLS_LINK" if [ "$AGENT" = "1" ]; then GCC_EXTRA="$GCC_EXTRA -DOPENVPN_COMMAND_AGENT" fi diff --git a/vars/vars-linux-dbg b/vars/vars-linux-dbg index 3e2d2cf..33006b2 100644 --- a/vars/vars-linux-dbg +++ b/vars/vars-linux-dbg @@ -2,11 +2,11 @@ export JAVA_DIR=/usr/lib/jvm/java-7-openjdk-amd64 [ -z "$DEP_DIR" ] && export DEP_DIR=$HOME/linux export PLATFORM=linux export DEBUG_BUILD=1 -export OTHER_COMPILER_FLAGS="-g -Wno-unused-local-typedefs -Wno-unused-variable -Wno-shift-count-overflow -pthread" +export OTHER_COMPILER_FLAGS="-ggdb -Wno-unused-local-typedefs -Wno-unused-variable -Wno-shift-count-overflow -pthread" export CXX_COMPILER_FLAGS="-std=c++14" export LIB_OPT_LEVEL="-O0" export LIB_FPIC="-fPIC" -export GPP_CMD=g++-5 -export GCC_CMD=gcc-5 -export AR_CMD=gcc-ar-5 -export RANLIB_CMD=gcc-ranlib-5 +export GPP_CMD=g++ +export GCC_CMD=gcc +export AR_CMD=gcc-ar +export RANLIB_CMD=gcc-ranlib