From fa6ece9683deec8d0ab74bc2b1a00bb78c6f3a7c Mon Sep 17 00:00:00 2001 From: Moorko Date: Mon, 2 Sep 2024 11:31:31 +0200 Subject: [PATCH 01/27] chore: add build directory to .gitignore Signed-off-by: Moorko --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 25fdc642..71513ae6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +build/ *~ .#* version.c -- GitLab From 320de1e26728f25704b959010f00a8a2d8ce384e Mon Sep 17 00:00:00 2001 From: Moorko Date: Mon, 2 Sep 2024 11:39:07 +0200 Subject: [PATCH 02/27] feat: implement tls fragmentation for openssl Signed-off-by: Moorko --- library.c | 2 + openconnect-internal.h | 3 ++ openssl.c | 100 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/library.c b/library.c index d5021594..16f1d311 100644 --- a/library.c +++ b/library.c @@ -101,6 +101,8 @@ struct openconnect_info *openconnect_vpninfo_new(const char *useragent, vpninfo->try_http_auth = 1; vpninfo->proxy_auth[AUTH_TYPE_BASIC].state = AUTH_DEFAULT_DISABLED; vpninfo->http_auth[AUTH_TYPE_BASIC].state = AUTH_DEFAULT_DISABLED; + vpninfo->tls_hs_record_frag_size = 0; + vpninfo->tls_hs_tcp_frag_size = 0; openconnect_set_reported_os(vpninfo, NULL); #ifdef HAVE_EPOLL vpninfo->epoll_fd = epoll_create1(EPOLL_CLOEXEC); diff --git a/openconnect-internal.h b/openconnect-internal.h index 7ec0f21c..15bfc665 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -749,6 +749,9 @@ struct openconnect_info { int ssl_fd; int dtls_fd; + int tls_hs_record_frag_size; /* Size of each TLS record size in TLS handshake. */ + int tls_hs_tcp_frag_size; /* Size of each TCP segment conveying TLS handshake records */ + int dtls_tos_current; int dtls_pass_tos; int dtls_tos_proto, dtls_tos_optname; diff --git a/openssl.c b/openssl.c index 4a366323..7f5c834b 100644 --- a/openssl.c +++ b/openssl.c @@ -59,6 +59,7 @@ typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer, #endif static char tls_library_version[32] = ""; +static size_t tls_hs_tcp_frag_size = 0; const char *openconnect_get_tls_library_version(void) { @@ -1864,10 +1865,75 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } +/* the name should be changed */ +static BIO_METHOD* getCustomBIOMethod () +{ + BIO_METHOD* bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "custom_socket"); + + const BIO_METHOD* default_method = BIO_s_socket(); + + BIO_meth_set_write_ex(bio_method, BIO_meth_get_write_ex(default_method)); + BIO_meth_set_write(bio_method, BIO_meth_get_write(default_method)); + + BIO_meth_set_read_ex(bio_method, BIO_meth_get_read_ex(default_method)); + BIO_meth_set_read(bio_method, BIO_meth_get_read(default_method)); + + BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); + BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); + + BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + + BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); + BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); + + BIO_meth_set_create(bio_method, BIO_meth_get_create(default_method)); + BIO_meth_set_destroy(bio_method, BIO_meth_get_destroy(default_method)); + + + return bio_method; +} + +/* Probably should be moved somewhere else */ +static int tls_tcp_frag_write_ex_func (BIO* bio, const char* data, const size_t size, size_t* written) +{ + int socket_fd = 0; + size_t current_written = 0; + BIO_get_fd(bio, &socket_fd); + + while (current_written < size) + { + const ssize_t ret = send(socket_fd, &data[current_written], MIN(tls_hs_tcp_frag_size, size - current_written), 0); + if (ret <= 0) + { + if (BIO_sock_should_retry(ret)) + { + BIO_set_retry_write(bio); + *written = current_written; + return 0; + } + } + current_written += ret; + } + + *written = current_written; + + + return 1; +} + +static int tls_tcp_frag_write_func (BIO* bio, const char* data, const int size) +{ + size_t written = 0; + tls_tcp_frag_write_ex_func(bio, data, size, &written); + return written; +} + int openconnect_open_https(struct openconnect_info *vpninfo) { SSL *https_ssl; BIO *https_bio; + BIO_METHOD* bio_socket_method; int ssl_sock; int err; @@ -1988,7 +2054,11 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); - https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE); + bio_socket_method = getCustomBIOMethod(); + https_bio = BIO_new(bio_socket_method); + BIO_set_fd(https_bio, ssl_sock, BIO_NOCLOSE); + + // https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE); BIO_set_nbio(https_bio, 1); SSL_set_bio(https_ssl, https_bio, https_bio); /* @@ -2017,6 +2087,19 @@ int openconnect_open_https(struct openconnect_info *vpninfo) SSL_set_tlsext_host_name(https_ssl, vpninfo->sni); else if (string_is_hostname(vpninfo->hostname)) SSL_set_tlsext_host_name(https_ssl, vpninfo->hostname); + + if (vpninfo->tls_hs_record_frag_size > 0) { + if (!SSL_set_split_send_fragment(https_ssl, vpninfo->tls_hs_record_frag_size)) { + vpn_progress(vpninfo, PRG_ERR, _("SSL ClientHello record fragmentation failed\n")); + } + } + + if (vpninfo->tls_hs_tcp_frag_size > 0) { + tls_hs_tcp_frag_size = vpninfo->tls_hs_tcp_frag_size; + BIO_meth_set_write(bio_socket_method, tls_tcp_frag_write_func); + BIO_meth_set_write_ex(bio_socket_method, tls_tcp_frag_write_ex_func); + } + #endif SSL_set_verify(https_ssl, SSL_VERIFY_PEER, NULL); @@ -2067,6 +2150,21 @@ int openconnect_open_https(struct openconnect_info *vpninfo) vpninfo->ssl_write = openconnect_openssl_write; vpninfo->ssl_gets = openconnect_openssl_gets; +#if OPENSSL_VERSION_NUMBER >= 0x10001070L + + /* Revert fragmentation settings as handshake is finished */ + + if (vpninfo->tls_hs_record_frag_size > 0) { + if (!SSL_set_split_send_fragment(https_ssl, SSL3_RT_MAX_PLAIN_LENGTH)) { + vpn_progress(vpninfo, PRG_ERR, _("SSL ClientHello record fragmentation failed\n")); + } + } + + BIO_meth_set_write_ex(bio_socket_method, BIO_meth_get_write_ex(BIO_s_socket())); + BIO_meth_set_write(bio_socket_method, BIO_meth_get_write(BIO_s_socket())); + +#endif + vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s with ciphersuite %s\n"), vpninfo->hostname, vpninfo->cstp_cipher); -- GitLab From 163102ac0a2fdc3a34da4fcba7cc3b9fd7b3b480 Mon Sep 17 00:00:00 2001 From: Moorko Date: Mon, 2 Sep 2024 11:40:28 +0200 Subject: [PATCH 03/27] feat: define tls fragmentation size setting functions and add them to jni Signed-off-by: Moorko --- .../infradead/libopenconnect/LibOpenConnect.java | 2 ++ jni.c | 16 ++++++++++++++++ libopenconnect.map.in | 3 +++ libopenconnect5.symbols | 2 ++ library.c | 14 ++++++++++++++ openconnect.h | 8 ++++++++ 6 files changed, 45 insertions(+) diff --git a/java/src/org/infradead/libopenconnect/LibOpenConnect.java b/java/src/org/infradead/libopenconnect/LibOpenConnect.java index d1393179..13ef87af 100644 --- a/java/src/org/infradead/libopenconnect/LibOpenConnect.java +++ b/java/src/org/infradead/libopenconnect/LibOpenConnect.java @@ -133,6 +133,8 @@ public abstract class LibOpenConnect { public synchronized native int setProxyAuth(String methods); public synchronized native int setHTTPProxy(String proxy); public synchronized native void setSNI(String sni); + public synchronized native void setTLSHSRecFragSize(int size); + public synchronized native void setTLSHSTCPFragSize(int size); public synchronized native void setXMLSHA1(String hash); public synchronized native void setHostname(String hostname); public synchronized native void setVersionString(String version); diff --git a/jni.c b/jni.c index 112080c3..ef8980a1 100644 --- a/jni.c +++ b/jni.c @@ -1467,6 +1467,22 @@ JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setSNI( SET_STRING_END(); } +JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTLSHSRecFragSize( + JNIEnv *jenv, jobject jobj, jint jarg) +{ + SET_STRING_START_VOID() + openconnect_set_tls_hs_rec_frag_size(ctx->vpninfo, arg); + SET_STRING_END(); +} + +JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTLSHSTCPFragSize( + JNIEnv *jenv, jobject jobj, jint jarg) +{ + SET_STRING_START_VOID() + openconnect_set_tls_hs_tcp_frag_size(ctx->vpninfo, arg); + SET_STRING_END(); +} + JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setCAFile( JNIEnv *jenv, jobject jobj, jstring jarg) { diff --git a/libopenconnect.map.in b/libopenconnect.map.in index f036c071..3ff90c49 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -130,6 +130,9 @@ OPENCONNECT_5_8 { OPENCONNECT_5_9 { global: openconnect_set_sni; + openconnect_set_tls_hs_rec_frag_size; + openconnect_set_tls_hs_tcp_frag_size; + } OPENCONNECT_5_8; OPENCONNECT_PRIVATE { diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 7cb04d05..8aca7a0e 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -103,3 +103,5 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 openconnect_set_sni@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_9 9.12 diff --git a/library.c b/library.c index 16f1d311..75f88f1d 100644 --- a/library.c +++ b/library.c @@ -896,6 +896,20 @@ int openconnect_set_sni(struct openconnect_info *vpninfo, return 0; } +int openconnect_set_tls_hs_rec_frag_size(struct openconnect_info *vpninfo, int size) +{ + printf("tls frag size is: %d\n", size); + vpninfo->tls_hs_record_frag_size = size; + return 0; +} + +int openconnect_set_tls_hs_tcp_frag_size(struct openconnect_info *vpninfo, int size) +{ + printf("tcp frag size is: %d\n", size); + vpninfo->tls_hs_tcp_frag_size = size; + return 0; +} + void openconnect_set_xmlsha1(struct openconnect_info *vpninfo, const char *xmlsha1, int size) { diff --git a/openconnect.h b/openconnect.h index 136f181a..ca351c34 100644 --- a/openconnect.h +++ b/openconnect.h @@ -581,6 +581,14 @@ int openconnect_set_urlpath(struct openconnect_info *, const char *); int openconnect_set_localname(struct openconnect_info *, const char *); int openconnect_set_sni(struct openconnect_info *, const char *); +/* + * Comment here for explaining what are these functions + * + * + */ +int openconnect_set_tls_hs_rec_frag_size(struct openconnect_info *vpninfo, int size); +int openconnect_set_tls_hs_tcp_frag_size(struct openconnect_info *vpninfo, int size); + /* Some software tokens, such as HOTP tokens, include a counter which * needs to be stored in persistent storage. * -- GitLab From d945dfbe00f2a4601bcd8749fb62756cae5beb63 Mon Sep 17 00:00:00 2001 From: Moorko Date: Mon, 2 Sep 2024 11:41:02 +0200 Subject: [PATCH 04/27] feat: add TLS fragmentation to the CLI Signed-off-by: Moorko --- main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/main.c b/main.c index be0e3cd7..e832431f 100644 --- a/main.c +++ b/main.c @@ -202,6 +202,8 @@ enum { OPT_SERVERCERT, OPT_RESOLVE, OPT_SNI, + OPT_TLS_REC_FRAG, + OPT_TLS_TCP_FRAG, OPT_USERAGENT, OPT_NON_INTER, OPT_DTLS_LOCAL_PORT, @@ -291,6 +293,8 @@ static const struct option long_options[] = { OPTION("servercert", 1, OPT_SERVERCERT), OPTION("resolve", 1, OPT_RESOLVE), OPTION("sni", 1, OPT_SNI), + OPTION("tls-hs-record-frag", 1, OPT_TLS_REC_FRAG), + OPTION("tls-hs-tcp-frag", 1, OPT_TLS_TCP_FRAG), OPTION("key-password-from-fsid", 0, OPT_KEY_PASSWORD_FROM_FSID), OPTION("useragent", 1, OPT_USERAGENT), OPTION("version-string", 1, OPT_VERSION), @@ -1064,6 +1068,10 @@ static void usage(void) printf(" --reconnect-timeout=SECONDS %s\n", _("Reconnection retry timeout (default is 300 seconds)")); printf(" --resolve=HOST:IP %s\n", _("Use IP when connecting to HOST")); printf(" --sni=HOST %s\n", _("Always send HOST as TLS client SNI (domain fronting)")); + printf(" --tls-hs-record-frag=SIZE %s\n", _("TLS handshake Record size for fragmentation")); + printf(" %s\n", _("(zero means no fragmentation in TLS Record layer)")); + printf(" --tls-hs-tcp-frag=SIZE %s\n", _("TLS handshake TCP segment size for fragmentation")); + printf(" %s\n", _("(zero means no segmentation in TCP layer)")); printf(" --passtos %s\n", _("Copy TOS / TCLASS field into DTLS and ESP packets")); printf(" --dtls-local-port=PORT %s\n", _("Set local port for DTLS and ESP datagrams")); @@ -1585,6 +1593,8 @@ static int autocomplete(int argc, char **argv) case OPT_AUTHGROUP: /* --authgroup */ case OPT_RESOLVE: /* --resolve */ case OPT_SNI: /* --sni */ + case OPT_TLS_REC_FRAG: /* --tls-hs-record-frag */ + case OPT_TLS_TCP_FRAG: /* --tls-hs-tcp-frag */ case OPT_USERAGENT: /* --useragent */ case OPT_VERSION: /* --version-string */ case OPT_FORCE_DPD: /* --force-dpd */ @@ -2008,6 +2018,14 @@ int main(int argc, char *argv[]) case OPT_SNI: openconnect_set_sni(vpninfo, config_arg); break; + case OPT_TLS_REC_FRAG: + assert_nonnull_config_arg("tls-hs-record-frag", config_arg); + openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); + break; + case OPT_TLS_TCP_FRAG: + assert_nonnull_config_arg("tls-hs-tcp-frag", config_arg); + openconnect_set_tls_hs_tcp_frag_size(vpninfo, atoi(config_arg)); + break; case OPT_NO_DTLS: openconnect_disable_dtls(vpninfo); break; -- GitLab From 36858d7c09d068ca7608114abd5cf8b452dbe0fc Mon Sep 17 00:00:00 2001 From: Moorko Date: Tue, 3 Sep 2024 00:21:19 +0200 Subject: [PATCH 05/27] chore: clean up some codes and comment Signed-off-by: Moorko --- library.c | 2 -- openconnect.h | 10 +++++-- openssl.c | 78 +++++++++++++++++++++++++-------------------------- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/library.c b/library.c index 75f88f1d..e8679c08 100644 --- a/library.c +++ b/library.c @@ -898,14 +898,12 @@ int openconnect_set_sni(struct openconnect_info *vpninfo, int openconnect_set_tls_hs_rec_frag_size(struct openconnect_info *vpninfo, int size) { - printf("tls frag size is: %d\n", size); vpninfo->tls_hs_record_frag_size = size; return 0; } int openconnect_set_tls_hs_tcp_frag_size(struct openconnect_info *vpninfo, int size) { - printf("tcp frag size is: %d\n", size); vpninfo->tls_hs_tcp_frag_size = size; return 0; } diff --git a/openconnect.h b/openconnect.h index ca351c34..41066fb6 100644 --- a/openconnect.h +++ b/openconnect.h @@ -582,11 +582,15 @@ int openconnect_set_localname(struct openconnect_info *, const char *); int openconnect_set_sni(struct openconnect_info *, const char *); /* - * Comment here for explaining what are these functions - * - * + * Set TLS handshake record size for fragmentation purpose (so data would be + * split in multiple records). */ int openconnect_set_tls_hs_rec_frag_size(struct openconnect_info *vpninfo, int size); + +/* + * Set TLS handshake TCP segment size for fragmentation purpose (splits + * TLS records into multiple TCP segments) + */ int openconnect_set_tls_hs_tcp_frag_size(struct openconnect_info *vpninfo, int size); /* Some software tokens, such as HOTP tokens, include a counter which diff --git a/openssl.c b/openssl.c index 7f5c834b..aa403662 100644 --- a/openssl.c +++ b/openssl.c @@ -158,6 +158,40 @@ int openconnect_random(void *bytes, int len) /* Helper functions for reading/writing lines over TLS/DTLS. We could use cURL for the HTTP stuff, but it's overkill */ +static int tls_tcp_frag_write_ex_func (BIO* bio, const char* data, const size_t size, size_t* written) +{ + int socket_fd = 0; + size_t current_written = 0; + BIO_get_fd(bio, &socket_fd); + + while (current_written < size) + { + const ssize_t ret = send(socket_fd, &data[current_written], MIN(tls_hs_tcp_frag_size, size - current_written), 0); + if (ret <= 0) + { + if (BIO_sock_should_retry(ret)) + { + BIO_set_retry_write(bio); + *written = current_written; + return 0; + } + } + current_written += ret; + } + + *written = current_written; + + + return 1; +} + +static int tls_tcp_frag_write_func (BIO* bio, const char* data, const int size) +{ + size_t written = 0; + tls_tcp_frag_write_ex_func(bio, data, size, &written); + return written; +} + static int _openconnect_openssl_write(SSL *ssl, int fd, struct openconnect_info *vpninfo, char *buf, size_t len) { size_t orig_len = len; @@ -1865,8 +1899,7 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } -/* the name should be changed */ -static BIO_METHOD* getCustomBIOMethod () +static BIO_METHOD* mutable_socket_method () { BIO_METHOD* bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "custom_socket"); @@ -1894,40 +1927,6 @@ static BIO_METHOD* getCustomBIOMethod () return bio_method; } -/* Probably should be moved somewhere else */ -static int tls_tcp_frag_write_ex_func (BIO* bio, const char* data, const size_t size, size_t* written) -{ - int socket_fd = 0; - size_t current_written = 0; - BIO_get_fd(bio, &socket_fd); - - while (current_written < size) - { - const ssize_t ret = send(socket_fd, &data[current_written], MIN(tls_hs_tcp_frag_size, size - current_written), 0); - if (ret <= 0) - { - if (BIO_sock_should_retry(ret)) - { - BIO_set_retry_write(bio); - *written = current_written; - return 0; - } - } - current_written += ret; - } - - *written = current_written; - - - return 1; -} - -static int tls_tcp_frag_write_func (BIO* bio, const char* data, const int size) -{ - size_t written = 0; - tls_tcp_frag_write_ex_func(bio, data, size, &written); - return written; -} int openconnect_open_https(struct openconnect_info *vpninfo) { @@ -2054,13 +2053,14 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); - bio_socket_method = getCustomBIOMethod(); + bio_socket_method = mutable_socket_method(); https_bio = BIO_new(bio_socket_method); - BIO_set_fd(https_bio, ssl_sock, BIO_NOCLOSE); - // https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE); + BIO_set_fd(https_bio, ssl_sock, BIO_NOCLOSE); BIO_set_nbio(https_bio, 1); + SSL_set_bio(https_ssl, https_bio, https_bio); + /* * If a ClientHello is between 256 and 511 bytes, the * server cannot distinguish between a SSLv2 formatted -- GitLab From de0f07c1ac00749fc6679724a9c1a63fa52d1a6c Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 15:57:32 +0200 Subject: [PATCH 06/27] feat: implement TLS fragmentation for gnutls. Signed-off-by: Moorko --- gnutls.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/gnutls.c b/gnutls.c index d0c3acde..9bdc01bd 100644 --- a/gnutls.c +++ b/gnutls.c @@ -21,11 +21,13 @@ #include "gnutls.h" +#include #include #include #include #include #include +#include #ifdef HAVE_P11KIT #include @@ -33,9 +35,9 @@ #include #endif -#include +#include #include -#include +#include #include #include @@ -67,7 +69,30 @@ static int gnutls_pin_callback(void *priv, int attempt, const char *uri, #define GNUTLS_FORCE_CLIENT_CERT 0 #endif +#pragma pack(push, 1) +struct tls_record_header_st +{ + uint8_t content_type; + uint16_t version; + uint16_t length; +}; +#pragma pack(pop) + +typedef enum { + ChangeCipherSpec = 20, + Alert, + Handshake, + ApplicationData +} tls_record_content_type_t; + +typedef enum { + ClientHello = 1, + ClientKeyExchange = 16 +} tls_record_handshake_type_t; + static char tls_library_version[32] = ""; +static size_t tls_hs_tcp_frag_size = 0; +static size_t tls_hs_record_frag_size = 0; const char *openconnect_get_tls_library_version(void) { @@ -360,6 +385,102 @@ int ssl_nonblock_write(struct openconnect_info *vpninfo, int dtls, void *buf, in return -1; } +static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, const void* original_data, size_t original_size) +{ + const int socket_fd = (intptr_t)transport_ptr; + char* data_to_be_written = (char*)original_data; + size_t data_size_to_be_written = original_size; + size_t tcp_seg_size = tls_hs_tcp_frag_size; + size_t tls_rec_size = tls_hs_record_frag_size; + + + if (data_size_to_be_written <= sizeof(struct tls_record_header_st)) { + tls_rec_size = 0; + } + + struct tls_record_header_st base_header = *((struct tls_record_header_st*)data_to_be_written); + const tls_record_handshake_type_t handshake_type = *(uint8_t*)(data_to_be_written + sizeof(struct tls_record_header_st)); + + /* + * We can only fragment TLS records that have ContentType of Handshake + * and the HandshakeType of ClientHello or ClientKeyExchange. + * OpenSSL has the same behavior through `SSL_set_split_send_fragment()`. + */ + if (tls_rec_size > 0 + && base_header.content_type == Handshake + && (handshake_type == ClientHello || handshake_type == ClientKeyExchange)) { + + const size_t data_without_base_header_size = data_size_to_be_written - sizeof(struct tls_record_header_st); + const char* data_without_base_header = data_to_be_written + sizeof(struct tls_record_header_st); + + const int needs_carry = data_without_base_header_size % tls_rec_size == 0 ? 0 : 1; + const int number_of_headers = (data_without_base_header_size / tls_rec_size) + needs_carry; + const size_t tls_frag_rec_overhead = number_of_headers * sizeof(struct tls_record_header_st); + + /* + * We need to allocate a new buffer as we need to copy new headers + * in between our data. + */ + data_size_to_be_written = data_without_base_header_size + tls_frag_rec_overhead; + data_to_be_written = (char*)malloc(data_without_base_header_size + tls_frag_rec_overhead); + if (data_to_be_written == NULL) { + return -1; + } + + /* + * Create a new buffer with fragmented TLS records. + */ + ssize_t remained_bytes = data_without_base_header_size; + ssize_t copied_bytes = 0; + for (int i = 0; i < number_of_headers; i++) { + // Copy TLS Record base header + base_header.length = htons(MIN(tls_rec_size, remained_bytes)); + memcpy(data_to_be_written + copied_bytes, (char*)(&base_header), sizeof(struct tls_record_header_st)); + copied_bytes += sizeof(struct tls_record_header_st); + + // Copy fragment of data + const size_t frag_size = MIN(tls_rec_size, remained_bytes); + memcpy(data_to_be_written + copied_bytes, data_without_base_header + (i * tls_rec_size), frag_size); + copied_bytes += frag_size; + remained_bytes -= frag_size; + } + } + + if (tcp_seg_size == 0) { + tcp_seg_size = data_size_to_be_written; + } + + size_t current_written = 0; + while (current_written < data_size_to_be_written) + { + const ssize_t ret = send(socket_fd, &data_to_be_written[current_written], + MIN(tcp_seg_size, data_size_to_be_written - current_written), 0); + if (ret <= 0) + { + if (data_to_be_written != original_data) { + free(data_to_be_written); + } + return -1; + } + current_written += ret; + } + + if (data_to_be_written != original_data) { + free(data_to_be_written); + if (current_written == data_size_to_be_written) { + current_written = original_size; + } + } + return current_written; +} + +static ssize_t tls_default_push_func(gnutls_transport_ptr_t ptr, const void *data, + size_t len) +{ + const int socket_fd = (intptr_t)ptr; + return send(socket_fd, data, len, 0); +} + static int check_certificate_expiry(struct openconnect_info *vpninfo, struct cert_info *certinfo, gnutls_x509_crt_t cert) { @@ -2498,6 +2619,12 @@ int cstp_handshake(struct openconnect_info *vpninfo, unsigned init) ssl_sock = (intptr_t)gnutls_transport_get_ptr(vpninfo->https_sess); + if (vpninfo->tls_hs_record_frag_size > 0 || vpninfo->tls_hs_tcp_frag_size > 0) { + tls_hs_tcp_frag_size = vpninfo->tls_hs_tcp_frag_size; + tls_hs_record_frag_size = vpninfo->tls_hs_record_frag_size; + gnutls_transport_set_push_function(vpninfo->https_sess, tls_fragment_push_func); + } + while ((err = gnutls_handshake(vpninfo->https_sess))) { if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) { fd_set rd_set, wr_set; @@ -2544,6 +2671,13 @@ int cstp_handshake(struct openconnect_info *vpninfo, unsigned init) } } + /* + * Restore the default push function for the rest of the application flow. + */ + if (vpninfo->tls_hs_record_frag_size > 0 || vpninfo->tls_hs_tcp_frag_size > 0) { + gnutls_transport_set_push_function(vpninfo->https_sess, tls_default_push_func); + } + gnutls_free(vpninfo->cstp_cipher); vpninfo->cstp_cipher = get_gnutls_cipher(vpninfo->https_sess); -- GitLab From 99373439b2bf0f1f12fced5da2c1517a55798d36 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 16:18:34 +0200 Subject: [PATCH 07/27] fix: remove sendmmsg and recvmmsg from openssl bio method creation to avoid CI error Signed-off-by: Moorko --- openssl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openssl.c b/openssl.c index aa403662..8bcc6ebb 100644 --- a/openssl.c +++ b/openssl.c @@ -1914,9 +1914,12 @@ static BIO_METHOD* mutable_socket_method () BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); - BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); - BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); - + /* + * These two functions are not set because they're not available in the CI: + * + * BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + * BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + */ BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); -- GitLab From 4381eb44a6c8ef4df501d1306ab35940b5b023e4 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 16:34:28 +0200 Subject: [PATCH 08/27] chore: random try to fix CI complaints about symbols Signed-off-by: Moorko --- libopenconnect.map.in | 7 +++++-- libopenconnect5.symbols | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libopenconnect.map.in b/libopenconnect.map.in index 3ff90c49..d66a3e56 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -130,10 +130,13 @@ OPENCONNECT_5_8 { OPENCONNECT_5_9 { global: openconnect_set_sni; +} OPENCONNECT_5_8; + +OPENCONNECT_5_10 { + global: openconnect_set_tls_hs_rec_frag_size; openconnect_set_tls_hs_tcp_frag_size; - -} OPENCONNECT_5_8; +} OPENCONNECT_5_9; OPENCONNECT_PRIVATE { global: @SYMVER_TIME@ @SYMVER_GETLINE@ @SYMVER_JAVA@ @SYMVER_ASPRINTF@ @SYMVER_VASPRINTF@ @SYMVER_WIN32_STRERROR@ @SYMVER_WIN32_SETENV@ diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 8aca7a0e..89652509 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -103,5 +103,5 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 openconnect_set_sni@OPENCONNECT_5_9 9.12 - openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_9 9.12 - openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_10 9.13 + openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_10 9.13 -- GitLab From e2a41c9c8a8c75587f475ccd10a4d0eff8fa36d4 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 17:26:17 +0200 Subject: [PATCH 09/27] Revert "chore: random try to fix CI complaints about symbols" This reverts commit 98f9fbd5c6f95ace2d4a1128fe082223e20bb73a. Signed-off-by: Moorko --- libopenconnect.map.in | 7 ++----- libopenconnect5.symbols | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/libopenconnect.map.in b/libopenconnect.map.in index d66a3e56..3ff90c49 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -130,13 +130,10 @@ OPENCONNECT_5_8 { OPENCONNECT_5_9 { global: openconnect_set_sni; -} OPENCONNECT_5_8; - -OPENCONNECT_5_10 { - global: openconnect_set_tls_hs_rec_frag_size; openconnect_set_tls_hs_tcp_frag_size; -} OPENCONNECT_5_9; + +} OPENCONNECT_5_8; OPENCONNECT_PRIVATE { global: @SYMVER_TIME@ @SYMVER_GETLINE@ @SYMVER_JAVA@ @SYMVER_ASPRINTF@ @SYMVER_VASPRINTF@ @SYMVER_WIN32_STRERROR@ @SYMVER_WIN32_SETENV@ diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 89652509..8aca7a0e 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -103,5 +103,5 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 openconnect_set_sni@OPENCONNECT_5_9 9.12 - openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_10 9.13 - openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_10 9.13 + openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_9 9.12 -- GitLab From 5d33c74f31dbbcf58e9e927d9301c8ceaa65bcb3 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 18:46:57 +0200 Subject: [PATCH 10/27] chore: random try to fix CI complaints about symbols Signed-off-by: Moorko --- libopenconnect5.symbols | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 8aca7a0e..990812db 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -102,6 +102,4 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_cert@OPENCONNECT_5_8 9.00 openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 - openconnect_set_sni@OPENCONNECT_5_9 9.12 - openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_9 9.12 - openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_9 9.12 + openconnect_set_sni@OPENCONNECT_5_9 9.12 \ No newline at end of file -- GitLab From c3a43cd2456204390feaa4e92f848360d7c614f2 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 18:59:17 +0200 Subject: [PATCH 11/27] chore: random try to fix CI complaints about symbols Signed-off-by: Moorko --- libopenconnect.map.in | 2 +- libopenconnect5.symbols | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libopenconnect.map.in b/libopenconnect.map.in index 3ff90c49..dcf16f1e 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -131,7 +131,7 @@ OPENCONNECT_5_9 { global: openconnect_set_sni; openconnect_set_tls_hs_rec_frag_size; - openconnect_set_tls_hs_tcp_frag_size; + openconnect_set_tls_hs_tcp_frag_size; } OPENCONNECT_5_8; diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 990812db..7cb04d05 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -102,4 +102,4 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_cert@OPENCONNECT_5_8 9.00 openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 - openconnect_set_sni@OPENCONNECT_5_9 9.12 \ No newline at end of file + openconnect_set_sni@OPENCONNECT_5_9 9.12 -- GitLab From 3a0b0b15ea0ca9be65295bb1ccfbc8820a0b8c03 Mon Sep 17 00:00:00 2001 From: Moorko Date: Wed, 11 Sep 2024 23:47:04 +0200 Subject: [PATCH 12/27] chore: add symbols to libopenconnect5.symbols to see if CI fails again Signed-off-by: Moorko --- libopenconnect5.symbols | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libopenconnect5.symbols b/libopenconnect5.symbols index 7cb04d05..8aca7a0e 100644 --- a/libopenconnect5.symbols +++ b/libopenconnect5.symbols @@ -103,3 +103,5 @@ libopenconnect.so.5 libopenconnect5 #MINVER# openconnect_set_mca_key_password@OPENCONNECT_5_8 9.00 openconnect_set_useragent@OPENCONNECT_5_8 9.00 openconnect_set_sni@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_rec_frag_size@OPENCONNECT_5_9 9.12 + openconnect_set_tls_hs_tcp_frag_size@OPENCONNECT_5_9 9.12 -- GitLab From f03a8f49140667daadb60e8c891fc15de76913e0 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 12 Sep 2024 01:06:59 +0200 Subject: [PATCH 13/27] chore: remove gnutls/socket.h include expression as centos doesn't have it Signed-off-by: Moorko --- gnutls.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gnutls.c b/gnutls.c index 9bdc01bd..a774af21 100644 --- a/gnutls.c +++ b/gnutls.c @@ -27,7 +27,6 @@ #include #include #include -#include #ifdef HAVE_P11KIT #include @@ -39,7 +38,6 @@ #include #include -#include #include #include #include -- GitLab From 7bd27d0523c0c1afdf2c081bc1f2be9c4687a394 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 12 Sep 2024 01:11:13 +0200 Subject: [PATCH 14/27] fix: remove jni set_string Signed-off-by: Moorko --- jni.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/jni.c b/jni.c index ef8980a1..37fca093 100644 --- a/jni.c +++ b/jni.c @@ -1468,19 +1468,25 @@ JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setSNI( } JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTLSHSRecFragSize( - JNIEnv *jenv, jobject jobj, jint jarg) + JNIEnv *jenv, jobject jobj, jint arg) { - SET_STRING_START_VOID() + struct libctx *ctx = getctx(jenv, jobj); + + if (!ctx) + return; + openconnect_set_tls_hs_rec_frag_size(ctx->vpninfo, arg); - SET_STRING_END(); } JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setTLSHSTCPFragSize( - JNIEnv *jenv, jobject jobj, jint jarg) + JNIEnv *jenv, jobject jobj, jint arg) { - SET_STRING_START_VOID() - openconnect_set_tls_hs_tcp_frag_size(ctx->vpninfo, arg); - SET_STRING_END(); + struct libctx *ctx = getctx(jenv, jobj); + + if (!ctx) + return; + + openconnect_set_tls_hs_tcp_frag_size(ctx->vpninfo, arg); } JNIEXPORT void JNICALL Java_org_infradead_libopenconnect_LibOpenConnect_setCAFile( -- GitLab From 0beb4bc144d036a2e4bcd7aacb6b5ac8400a54bc Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 22:07:55 +0200 Subject: [PATCH 15/27] fix: resolve compilation error on centOS7 with openssl Signed-off-by: Moorko --- main.c | 12 ++++++++++++ openssl.c | 17 +++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index e832431f..1462bb86 100644 --- a/main.c +++ b/main.c @@ -2019,10 +2019,22 @@ int main(int argc, char *argv[]) openconnect_set_sni(vpninfo, config_arg); break; case OPT_TLS_REC_FRAG: +#if OPENSSL_VERSION_NUMBER < 0x10001070L + fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " + "Ignoring this option\n")); + break; +#endif + assert_nonnull_config_arg("tls-hs-record-frag", config_arg); openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); break; case OPT_TLS_TCP_FRAG: +#if OPENSSL_VERSION_NUMBER < 0x10001070L + fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " + "Ignoring this option\n")); + break; +#endif + assert_nonnull_config_arg("tls-hs-tcp-frag", config_arg); openconnect_set_tls_hs_tcp_frag_size(vpninfo, atoi(config_arg)); break; diff --git a/openssl.c b/openssl.c index 8bcc6ebb..ca2d641e 100644 --- a/openssl.c +++ b/openssl.c @@ -1899,6 +1899,8 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10001070L + static BIO_METHOD* mutable_socket_method () { BIO_METHOD* bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "custom_socket"); @@ -1914,12 +1916,10 @@ static BIO_METHOD* mutable_socket_method () BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); - /* - * These two functions are not set because they're not available in the CI: - * - * BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); - * BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); - */ + + BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); @@ -1930,6 +1930,7 @@ static BIO_METHOD* mutable_socket_method () return bio_method; } +#endif int openconnect_open_https(struct openconnect_info *vpninfo) { @@ -2056,8 +2057,12 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); +#if OPENSSL_VERSION_NUMBER >= 0x10001070L bio_socket_method = mutable_socket_method(); https_bio = BIO_new(bio_socket_method); +#else + https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE); +#endif BIO_set_fd(https_bio, ssl_sock, BIO_NOCLOSE); BIO_set_nbio(https_bio, 1); -- GitLab From d1a3b71d2b3b9f30fa48596c6ce60b7ab1e9e69c Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 22:34:02 +0200 Subject: [PATCH 16/27] fix: remove functions not available in CI static analyzer Signed-off-by: Moorko --- openssl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openssl.c b/openssl.c index ca2d641e..52757b1f 100644 --- a/openssl.c +++ b/openssl.c @@ -1916,9 +1916,12 @@ static BIO_METHOD* mutable_socket_method () BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); - - BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); - BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + /* + * These two functions are not set because they're not available in the CI: + * + * BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + * BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + */ BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); -- GitLab From bbe877fa37a57d8eb407bb5f46434376901cdc6e Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 22:49:10 +0200 Subject: [PATCH 17/27] fix: change required openssl version to 1.1 Signed-off-by: Moorko --- main.c | 5 +++-- openssl.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/main.c b/main.c index 1462bb86..6a4eed11 100644 --- a/main.c +++ b/main.c @@ -2019,7 +2019,8 @@ int main(int argc, char *argv[]) openconnect_set_sni(vpninfo, config_arg); break; case OPT_TLS_REC_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x10001070L +#if OPENSSL_VERSION_NUMBER < 0x11000000L + fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); break; @@ -2029,7 +2030,7 @@ int main(int argc, char *argv[]) openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); break; case OPT_TLS_TCP_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x10001070L +#if OPENSSL_VERSION_NUMBER < 0x11000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); break; diff --git a/openssl.c b/openssl.c index 52757b1f..50f9e8e8 100644 --- a/openssl.c +++ b/openssl.c @@ -1899,7 +1899,7 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x10001070L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L static BIO_METHOD* mutable_socket_method () { @@ -2060,7 +2060,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); -#if OPENSSL_VERSION_NUMBER >= 0x10001070L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L bio_socket_method = mutable_socket_method(); https_bio = BIO_new(bio_socket_method); #else -- GitLab From 7852c985388b6baa24de48d0e1433761ec5b46b1 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 22:52:12 +0200 Subject: [PATCH 18/27] fix: change required openssl version to 1.1 Signed-off-by: Moorko --- openssl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openssl.c b/openssl.c index 50f9e8e8..0bde632c 100644 --- a/openssl.c +++ b/openssl.c @@ -2099,6 +2099,10 @@ int openconnect_open_https(struct openconnect_info *vpninfo) else if (string_is_hostname(vpninfo->hostname)) SSL_set_tlsext_host_name(https_ssl, vpninfo->hostname); +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x11000000L + if (vpninfo->tls_hs_record_frag_size > 0) { if (!SSL_set_split_send_fragment(https_ssl, vpninfo->tls_hs_record_frag_size)) { vpn_progress(vpninfo, PRG_ERR, _("SSL ClientHello record fragmentation failed\n")); @@ -2112,6 +2116,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) } #endif + SSL_set_verify(https_ssl, SSL_VERIFY_PEER, NULL); vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"), -- GitLab From 7261c5ce3559a840c2f559ad1eb02d747ed7de2d Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 23:39:02 +0200 Subject: [PATCH 19/27] fix: change required openssl version to 1.1 Signed-off-by: Moorko --- openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openssl.c b/openssl.c index 0bde632c..9f4b7e03 100644 --- a/openssl.c +++ b/openssl.c @@ -2166,7 +2166,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) vpninfo->ssl_write = openconnect_openssl_write; vpninfo->ssl_gets = openconnect_openssl_gets; -#if OPENSSL_VERSION_NUMBER >= 0x10001070L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L /* Revert fragmentation settings as handshake is finished */ -- GitLab From cee36b2c3203fe9cc2e10226c195099f9c4eda14 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 23:41:04 +0200 Subject: [PATCH 20/27] refactor: another try to enable recvmmsg/sendmmsg functions Signed-off-by: Moorko --- openssl.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openssl.c b/openssl.c index 9f4b7e03..167480b8 100644 --- a/openssl.c +++ b/openssl.c @@ -1916,12 +1916,8 @@ static BIO_METHOD* mutable_socket_method () BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); - /* - * These two functions are not set because they're not available in the CI: - * - * BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); - * BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); - */ + BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); -- GitLab From fe73a751a8e1c76950a1d4d85d5c97c5e3d5c2b8 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 23:49:24 +0200 Subject: [PATCH 21/27] fix: change required openssl version to 1.1 (for real) Signed-off-by: Moorko --- main.c | 4 ++-- openssl.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index 6a4eed11..e0e88c9f 100644 --- a/main.c +++ b/main.c @@ -2019,7 +2019,7 @@ int main(int argc, char *argv[]) openconnect_set_sni(vpninfo, config_arg); break; case OPT_TLS_REC_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x11000000L +#if OPENSSL_VERSION_NUMBER < 0x110000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); @@ -2030,7 +2030,7 @@ int main(int argc, char *argv[]) openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); break; case OPT_TLS_TCP_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x11000000L +#if OPENSSL_VERSION_NUMBER < 0x110000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); break; diff --git a/openssl.c b/openssl.c index 167480b8..b2341d17 100644 --- a/openssl.c +++ b/openssl.c @@ -1899,7 +1899,7 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x11000000L +#if OPENSSL_VERSION_NUMBER >= 0x110000000L static BIO_METHOD* mutable_socket_method () { @@ -2056,7 +2056,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); -#if OPENSSL_VERSION_NUMBER >= 0x11000000L +#if OPENSSL_VERSION_NUMBER >= 0x110000000L bio_socket_method = mutable_socket_method(); https_bio = BIO_new(bio_socket_method); #else @@ -2097,7 +2097,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) #endif -#if OPENSSL_VERSION_NUMBER >= 0x11000000L +#if OPENSSL_VERSION_NUMBER >= 0x110000000L if (vpninfo->tls_hs_record_frag_size > 0) { if (!SSL_set_split_send_fragment(https_ssl, vpninfo->tls_hs_record_frag_size)) { @@ -2162,7 +2162,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) vpninfo->ssl_write = openconnect_openssl_write; vpninfo->ssl_gets = openconnect_openssl_gets; -#if OPENSSL_VERSION_NUMBER >= 0x11000000L +#if OPENSSL_VERSION_NUMBER >= 0x110000000L /* Revert fragmentation settings as handshake is finished */ -- GitLab From 6c2107c312283265c19a21bae0925ed3a053977a Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 26 Sep 2024 23:57:06 +0200 Subject: [PATCH 22/27] Revert "fix: change required openssl version to 1.1 (for real)" This reverts commit 0f7f61bffd823ef3b6d26435c52dfe1389d3d5f3. Signed-off-by: Moorko --- main.c | 4 ++-- openssl.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index e0e88c9f..6a4eed11 100644 --- a/main.c +++ b/main.c @@ -2019,7 +2019,7 @@ int main(int argc, char *argv[]) openconnect_set_sni(vpninfo, config_arg); break; case OPT_TLS_REC_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x110000000L +#if OPENSSL_VERSION_NUMBER < 0x11000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); @@ -2030,7 +2030,7 @@ int main(int argc, char *argv[]) openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); break; case OPT_TLS_TCP_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x110000000L +#if OPENSSL_VERSION_NUMBER < 0x11000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); break; diff --git a/openssl.c b/openssl.c index b2341d17..167480b8 100644 --- a/openssl.c +++ b/openssl.c @@ -1899,7 +1899,7 @@ int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ct return 0; } -#if OPENSSL_VERSION_NUMBER >= 0x110000000L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L static BIO_METHOD* mutable_socket_method () { @@ -2056,7 +2056,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) https_ssl = SSL_new(vpninfo->https_ctx); workaround_openssl_certchain_bug(vpninfo, https_ssl); -#if OPENSSL_VERSION_NUMBER >= 0x110000000L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L bio_socket_method = mutable_socket_method(); https_bio = BIO_new(bio_socket_method); #else @@ -2097,7 +2097,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) #endif -#if OPENSSL_VERSION_NUMBER >= 0x110000000L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L if (vpninfo->tls_hs_record_frag_size > 0) { if (!SSL_set_split_send_fragment(https_ssl, vpninfo->tls_hs_record_frag_size)) { @@ -2162,7 +2162,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) vpninfo->ssl_write = openconnect_openssl_write; vpninfo->ssl_gets = openconnect_openssl_gets; -#if OPENSSL_VERSION_NUMBER >= 0x110000000L +#if OPENSSL_VERSION_NUMBER >= 0x11000000L /* Revert fragmentation settings as handshake is finished */ -- GitLab From 46c5b0f6bb627895170ca7d67a3da9be8a0df34c Mon Sep 17 00:00:00 2001 From: Moorko Date: Fri, 27 Sep 2024 00:03:14 +0200 Subject: [PATCH 23/27] fix: disable sendmmsg/recvmmsg again Signed-off-by: Moorko --- openssl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openssl.c b/openssl.c index 167480b8..9f4b7e03 100644 --- a/openssl.c +++ b/openssl.c @@ -1916,8 +1916,12 @@ static BIO_METHOD* mutable_socket_method () BIO_meth_set_gets(bio_method, BIO_meth_get_gets(default_method)); BIO_meth_set_puts(bio_method, BIO_meth_get_puts(default_method)); - BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); - BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + /* + * These two functions are not set because they're not available in the CI: + * + * BIO_meth_set_sendmmsg(bio_method, BIO_meth_get_sendmmsg(default_method)); + * BIO_meth_set_recvmmsg(bio_method, BIO_meth_get_recvmmsg(default_method)); + */ BIO_meth_set_ctrl(bio_method, BIO_meth_get_ctrl(default_method)); BIO_meth_set_callback_ctrl(bio_method, BIO_meth_get_callback_ctrl(default_method)); -- GitLab From a3e0fc8ea74306d9559546eef6c1ad6e10b6949f Mon Sep 17 00:00:00 2001 From: Moorko Date: Fri, 27 Sep 2024 00:43:33 +0200 Subject: [PATCH 24/27] fix: MinGW32 doesn't recognize fcntl headers Signed-off-by: Moorko --- gnutls.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gnutls.c b/gnutls.c index a774af21..8389ce32 100644 --- a/gnutls.c +++ b/gnutls.c @@ -43,6 +43,7 @@ #include #include #include +#include #if defined(HAVE_P11KIT) || defined(HAVE_GNUTLS_SYSTEM_KEYS) static int gnutls_pin_callback(void *priv, int attempt, const char *uri, -- GitLab From 9b3d57285ce3a94ec9f3d7696b37a0c2d98929d3 Mon Sep 17 00:00:00 2001 From: Moorko Date: Fri, 29 Nov 2024 03:23:45 +0100 Subject: [PATCH 25/27] fix: fix wrong openssl macro checking Signed-off-by: Moorko --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 6a4eed11..7f9ba4ef 100644 --- a/main.c +++ b/main.c @@ -2019,7 +2019,7 @@ int main(int argc, char *argv[]) openconnect_set_sni(vpninfo, config_arg); break; case OPT_TLS_REC_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x11000000L +#if defined(OPENCONNECT_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x11000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); @@ -2030,7 +2030,7 @@ int main(int argc, char *argv[]) openconnect_set_tls_hs_rec_frag_size(vpninfo, atoi(config_arg)); break; case OPT_TLS_TCP_FRAG: -#if OPENSSL_VERSION_NUMBER < 0x11000000L +#if defined(OPENCONNECT_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x11000000L fprintf(stderr, _("The version of your OpenSSL doesn't support fragmentation. " "Ignoring this option\n")); break; -- GitLab From 9cac983371f48c7003108b83154f0cee811be824 Mon Sep 17 00:00:00 2001 From: Moorko Date: Fri, 29 Nov 2024 03:24:09 +0100 Subject: [PATCH 26/27] refactor: apply MR review suggestions Signed-off-by: Moorko --- gnutls.c | 60 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/gnutls.c b/gnutls.c index 8389ce32..44faebb8 100644 --- a/gnutls.c +++ b/gnutls.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -68,14 +69,12 @@ static int gnutls_pin_callback(void *priv, int attempt, const char *uri, #define GNUTLS_FORCE_CLIENT_CERT 0 #endif -#pragma pack(push, 1) struct tls_record_header_st { uint8_t content_type; uint16_t version; uint16_t length; -}; -#pragma pack(pop) +} __attribute__((packed)); typedef enum { ChangeCipherSpec = 20, @@ -86,7 +85,8 @@ typedef enum { typedef enum { ClientHello = 1, - ClientKeyExchange = 16 + ClientKeyExchange = 16, + UNKNOWN = -1 } tls_record_handshake_type_t; static char tls_library_version[32] = ""; @@ -384,32 +384,49 @@ int ssl_nonblock_write(struct openconnect_info *vpninfo, int dtls, void *buf, in return -1; } +/** + * The purpose of this function is to be a replacement + * for the default GnuTLS' "push" function (i.e. transport layer "send" function). + * This function will "fragment" the TLS requests (at both TLS Record and TCP segment layers). + * The fragmentation is applied based on the values stored in + * the global variables `tls_hs_record_frag_size` and `tls_hs_tcp_frag_size`. + */ static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, const void* original_data, size_t original_size) { const int socket_fd = (intptr_t)transport_ptr; char* data_to_be_written = (char*)original_data; - size_t data_size_to_be_written = original_size; - size_t tcp_seg_size = tls_hs_tcp_frag_size; + size_t data_to_be_written_size = original_size; + size_t tls_rec_size = tls_hs_record_frag_size; + size_t tcp_seg_size = tls_hs_tcp_frag_size; + bool is_tls_rec_frag_enabled = tls_rec_size > 0 ? true : false; + bool is_tcp_seg_frag_enabled = tcp_seg_size > 0 ? true : false; + struct tls_record_header_st base_header; + tls_record_handshake_type_t handshake_type = UNKNOWN; - if (data_size_to_be_written <= sizeof(struct tls_record_header_st)) { - tls_rec_size = 0; - } + /** + * For TLS Record fragmentation we expect the data + * to be at least as big as a handshake TLS Record header + */ + if (data_to_be_written_size <= sizeof(struct tls_record_header_st)) { + is_tls_rec_frag_enabled = false; - struct tls_record_header_st base_header = *((struct tls_record_header_st*)data_to_be_written); - const tls_record_handshake_type_t handshake_type = *(uint8_t*)(data_to_be_written + sizeof(struct tls_record_header_st)); + } else { + memcpy(&base_header, data_to_be_written, sizeof(struct tls_record_header_st)); + handshake_type = ((uint8_t*)data_to_be_written)[sizeof(struct tls_record_header_st)]; + } /* * We can only fragment TLS records that have ContentType of Handshake * and the HandshakeType of ClientHello or ClientKeyExchange. * OpenSSL has the same behavior through `SSL_set_split_send_fragment()`. */ - if (tls_rec_size > 0 + if (is_tls_rec_frag_enabled && base_header.content_type == Handshake && (handshake_type == ClientHello || handshake_type == ClientKeyExchange)) { - const size_t data_without_base_header_size = data_size_to_be_written - sizeof(struct tls_record_header_st); + const size_t data_without_base_header_size = data_to_be_written_size - sizeof(struct tls_record_header_st); const char* data_without_base_header = data_to_be_written + sizeof(struct tls_record_header_st); const int needs_carry = data_without_base_header_size % tls_rec_size == 0 ? 0 : 1; @@ -420,7 +437,7 @@ static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, cons * We need to allocate a new buffer as we need to copy new headers * in between our data. */ - data_size_to_be_written = data_without_base_header_size + tls_frag_rec_overhead; + data_to_be_written_size = data_without_base_header_size + tls_frag_rec_overhead; data_to_be_written = (char*)malloc(data_without_base_header_size + tls_frag_rec_overhead); if (data_to_be_written == NULL) { return -1; @@ -445,15 +462,15 @@ static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, cons } } - if (tcp_seg_size == 0) { - tcp_seg_size = data_size_to_be_written; + if (!is_tcp_seg_frag_enabled) { + tcp_seg_size = data_to_be_written_size; } size_t current_written = 0; - while (current_written < data_size_to_be_written) + while (current_written < data_to_be_written_size) { const ssize_t ret = send(socket_fd, &data_to_be_written[current_written], - MIN(tcp_seg_size, data_size_to_be_written - current_written), 0); + MIN(tcp_seg_size, data_to_be_written_size - current_written), 0); if (ret <= 0) { if (data_to_be_written != original_data) { @@ -466,13 +483,18 @@ static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, cons if (data_to_be_written != original_data) { free(data_to_be_written); - if (current_written == data_size_to_be_written) { + if (current_written == data_to_be_written_size) { current_written = original_size; } } return current_written; } +/** + * This is the default 'push' function (i.e. transport layer 'send' function). + * This is based on the GnuTLS implementation. + * See https://gnutls.org/manual/gnutls.html#Setting-up-the-transport-layer + */ static ssize_t tls_default_push_func(gnutls_transport_ptr_t ptr, const void *data, size_t len) { -- GitLab From 0622c53cbe5e0f8ed6f72c8536d2f43e286b7977 Mon Sep 17 00:00:00 2001 From: Moorko Date: Thu, 12 Dec 2024 18:39:40 +0100 Subject: [PATCH 27/27] refactor: remove malloc result's casting as suggested in the MR review Signed-off-by: Moorko --- gnutls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnutls.c b/gnutls.c index 44faebb8..c1cc0d8a 100644 --- a/gnutls.c +++ b/gnutls.c @@ -438,7 +438,7 @@ static ssize_t tls_fragment_push_func(gnutls_transport_ptr_t transport_ptr, cons * in between our data. */ data_to_be_written_size = data_without_base_header_size + tls_frag_rec_overhead; - data_to_be_written = (char*)malloc(data_without_base_header_size + tls_frag_rec_overhead); + data_to_be_written = malloc(data_without_base_header_size + tls_frag_rec_overhead); if (data_to_be_written == NULL) { return -1; } -- GitLab