From: Yongye Zhu Date: Wed, 4 Oct 2023 09:26:14 +0000 (+0000) Subject: ktls: add support for FreeBSD X-Git-Tag: 3.8.2~11^2 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=22fddddc53cfb2b2d9f73a27bc1ea147f53cd746;p=thirdparty%2Fgnutls.git ktls: add support for FreeBSD This extend the KTLS support to FreeBSD, with the AES-GCM-128, AES-GCM-256, and ChaCha20-Poly1305 ciphersuites. Signed-off-by: Yongye Zhu Reviewed-by: Frantisek Krenzelok --- diff --git a/lib/system/ktls.c b/lib/system/ktls.c index 060194bdf6..aea8331cf4 100644 --- a/lib/system/ktls.c +++ b/lib/system/ktls.c @@ -25,14 +25,23 @@ #ifdef ENABLE_KTLS -#include -#include #include #include #include #include +#include + +#if defined(__FreeBSD__) +#include +#include +#include +#include + +#else +#include #include "ext/session_ticket.h" #include +#endif /** * gnutls_transport_is_ktls_enabled: @@ -57,6 +66,10 @@ gnutls_transport_is_ktls_enabled(gnutls_session_t session) void _gnutls_ktls_enable(gnutls_session_t session) { +#if defined(__FreeBSD__) + session->internals.ktls_enabled |= GNUTLS_KTLS_RECV; + session->internals.ktls_enabled |= GNUTLS_KTLS_SEND; +#else int sockin, sockout; gnutls_transport_get_int2(session, &sockin, &sockout); @@ -81,7 +94,372 @@ void _gnutls_ktls_enable(gnutls_session_t session) errno); } } +#endif +} + +#if defined(__FreeBSD__) + +int _gnutls_ktls_set_keys(gnutls_session_t session, + gnutls_transport_ktls_enable_flags_t in) +{ + gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(session); + gnutls_datum_t mac_key; + gnutls_datum_t iv; + gnutls_datum_t cipher_key; + unsigned char seq_number[12]; + int sockin, sockout; + int ret; + + gnutls_transport_get_int2(session, &sockin, &sockout); + /* check whether or not cipher suite supports ktls + */ + int version = gnutls_protocol_get_version(session); + if ((version != GNUTLS_TLS1_3 && version != GNUTLS_TLS1_2) || + (cipher != GNUTLS_CIPHER_AES_128_GCM && + cipher != GNUTLS_CIPHER_AES_256_GCM && + cipher != GNUTLS_CIPHER_CHACHA20_POLY1305)) { + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + + ret = gnutls_record_get_state(session, 1, &mac_key, &iv, &cipher_key, + seq_number); + if (ret < 0) { + return ret; + } + + in &= session->internals.ktls_enabled; + + if (in & GNUTLS_KTLS_RECV) { + struct tls_enable crypto_info; + memset(&crypto_info, 0, sizeof(crypto_info)); + switch (cipher) { + case GNUTLS_CIPHER_AES_128_GCM: { + crypto_info.cipher_algorithm = CRYPTO_AES_NIST_GCM_16; + assert(cipher_key.size == AES_128_GMAC_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + crypto_info.iv = + gnutls_malloc(TLS_AEAD_GCM_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_AEAD_GCM_LEN; + memcpy(crypto_info.iv, seq_number, + TLS_AEAD_GCM_LEN); + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + assert(iv.size == TLS_1_3_GCM_IV_LEN); + + crypto_info.iv = + gnutls_malloc(TLS_1_3_GCM_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_1_3_GCM_IV_LEN; + memcpy(crypto_info.iv, iv.data, + TLS_1_3_GCM_IV_LEN); + } + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = AES_128_GMAC_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(AES_128_GMAC_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + AES_128_GMAC_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_RXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_RECV; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + } break; + case GNUTLS_CIPHER_AES_256_GCM: { + crypto_info.cipher_algorithm = CRYPTO_AES_NIST_GCM_16; + assert(cipher_key.size == AES_256_GMAC_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + crypto_info.iv = + gnutls_malloc(TLS_AEAD_GCM_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_AEAD_GCM_LEN; + memcpy(crypto_info.iv, seq_number, + TLS_AEAD_GCM_LEN); + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + assert(iv.size == TLS_1_3_GCM_IV_LEN); + + crypto_info.iv = + gnutls_malloc(TLS_1_3_GCM_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_1_3_GCM_IV_LEN; + memcpy(crypto_info.iv, iv.data, + TLS_1_3_GCM_IV_LEN); + } + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = AES_256_GMAC_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(AES_256_GMAC_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + AES_256_GMAC_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_RXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_RECV; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + + } break; + case GNUTLS_CIPHER_CHACHA20_POLY1305: { + crypto_info.cipher_algorithm = CRYPTO_CHACHA20_POLY1305; + assert(cipher_key.size == POLY1305_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + } + + assert(iv.size == CHACHA20_POLY1305_IV_LEN); + crypto_info.iv = + gnutls_malloc(CHACHA20_POLY1305_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_CHACHA20_IV_LEN; + memcpy(crypto_info.iv, iv.data, TLS_CHACHA20_IV_LEN); + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = POLY1305_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(POLY1305_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + POLY1305_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_RXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_RECV; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + + } break; + default: + assert(0); + } + } + + ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key, + seq_number); + if (ret < 0) { + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + if (in & GNUTLS_KTLS_SEND) { + struct tls_enable crypto_info; + memset(&crypto_info, 0, sizeof(crypto_info)); + switch (cipher) { + case GNUTLS_CIPHER_AES_128_GCM: { + crypto_info.cipher_algorithm = CRYPTO_AES_NIST_GCM_16; + assert(cipher_key.size == AES_128_GMAC_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + crypto_info.iv = + gnutls_malloc(TLS_AEAD_GCM_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_AEAD_GCM_LEN; + memcpy(crypto_info.iv, seq_number, + TLS_AEAD_GCM_LEN); + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + assert(iv.size == TLS_1_3_GCM_IV_LEN); + + crypto_info.iv = + gnutls_malloc(TLS_1_3_GCM_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_1_3_GCM_IV_LEN; + memcpy(crypto_info.iv, iv.data, + TLS_1_3_GCM_IV_LEN); + } + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = AES_128_GMAC_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(AES_128_GMAC_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + AES_128_GMAC_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_TXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_SEND; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + } break; + case GNUTLS_CIPHER_AES_256_GCM: { + crypto_info.cipher_algorithm = CRYPTO_AES_NIST_GCM_16; + assert(cipher_key.size == AES_256_GMAC_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + crypto_info.iv = + gnutls_malloc(TLS_AEAD_GCM_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_AEAD_GCM_LEN; + memcpy(crypto_info.iv, seq_number, + TLS_AEAD_GCM_LEN); + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + assert(iv.size == TLS_1_3_GCM_IV_LEN); + + crypto_info.iv = + gnutls_malloc(TLS_1_3_GCM_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_1_3_GCM_IV_LEN; + memcpy(crypto_info.iv, iv.data, + TLS_1_3_GCM_IV_LEN); + } + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = AES_256_GMAC_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(AES_256_GMAC_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + AES_256_GMAC_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_TXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_SEND; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + + } break; + case GNUTLS_CIPHER_CHACHA20_POLY1305: { + crypto_info.cipher_algorithm = CRYPTO_CHACHA20_POLY1305; + assert(cipher_key.size == POLY1305_KEY_LEN); + + if (version == GNUTLS_TLS1_2) { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_TWO; + } else { + crypto_info.tls_vmajor = TLS_MAJOR_VER_ONE; + crypto_info.tls_vminor = TLS_MINOR_VER_THREE; + } + + assert(iv.size == CHACHA20_POLY1305_IV_LEN); + crypto_info.iv = + gnutls_malloc(CHACHA20_POLY1305_IV_LEN); + if (!crypto_info.iv) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + crypto_info.iv_len = TLS_CHACHA20_IV_LEN; + memcpy(crypto_info.iv, iv.data, TLS_CHACHA20_IV_LEN); + + memcpy(crypto_info.rec_seq, seq_number, 8); + + crypto_info.cipher_key_len = POLY1305_KEY_LEN; + crypto_info.cipher_key = + gnutls_malloc(POLY1305_KEY_LEN); + if (!crypto_info.cipher_key) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy(crypto_info.cipher_key, cipher_key.data, + POLY1305_KEY_LEN); + + if (setsockopt(sockin, IPPROTO_TCP, TCP_TXTLS_ENABLE, + &crypto_info, sizeof(crypto_info))) { + session->internals.ktls_enabled &= + ~GNUTLS_KTLS_SEND; + return gnutls_assert_val( + GNUTLS_E_INTERNAL_ERROR); + } + + } break; + default: + assert(0); + } + + // set callback for sending handshake messages + gnutls_handshake_set_read_function( + session, _gnutls_ktls_send_handshake_msg); + + // set callback for sending alert messages + gnutls_alert_set_read_function(session, + _gnutls_ktls_send_alert_msg); + } + + return in; } +#else int _gnutls_ktls_set_keys(gnutls_session_t session, gnutls_transport_ktls_enable_flags_t in) @@ -468,6 +846,7 @@ int _gnutls_ktls_set_keys(gnutls_session_t session, return in; } +#endif ssize_t _gnutls_ktls_send_file(gnutls_session_t session, int fd, off_t *offset, size_t count) @@ -476,10 +855,17 @@ ssize_t _gnutls_ktls_send_file(gnutls_session_t session, int fd, off_t *offset, int sockin, sockout; assert(session != NULL); +#if defined(__FreeBSD__) + off_t sbytes = 0; + assert(offset != NULL); +#endif gnutls_transport_get_int2(session, &sockin, &sockout); - +#if defined(__FreeBSD__) + ret = sendfile(fd, sockout, *offset, count, NULL, &sbytes, 0); +#else ret = sendfile(sockout, fd, offset, count); +#endif if (ret == -1) { switch (errno) { case EINTR: @@ -490,8 +876,12 @@ ssize_t _gnutls_ktls_send_file(gnutls_session_t session, int fd, off_t *offset, return GNUTLS_E_PUSH_ERROR; } } - +#if defined(__FreeBSD__) + *offset += sbytes; /* follow linux sendfile behavior */ + return sbytes; +#else return ret; +#endif } int _gnutls_ktls_send_control_msg(gnutls_session_t session, @@ -517,7 +907,11 @@ int _gnutls_ktls_send_control_msg(gnutls_session_t session, msg.msg_controllen = sizeof cmsg; hdr = CMSG_FIRSTHDR(&msg); +#if defined(__FreeBSD__) + hdr->cmsg_level = IPPROTO_TCP; +#else hdr->cmsg_level = SOL_TLS; +#endif hdr->cmsg_type = TLS_SET_RECORD_TYPE; hdr->cmsg_len = CMSG_LEN(sizeof(unsigned char)); @@ -638,7 +1032,11 @@ int _gnutls_ktls_recv_control_msg(gnutls_session_t session, if (hdr == NULL) { return GNUTLS_E_PULL_ERROR; } +#if defined(__FreeBSD__) + if (hdr->cmsg_level == IPPROTO_TCP && hdr->cmsg_type == TLS_GET_RECORD) +#else if (hdr->cmsg_level == SOL_TLS && hdr->cmsg_type == TLS_GET_RECORD_TYPE) +#endif *record_type = *(unsigned char *)CMSG_DATA(hdr); else *record_type = GNUTLS_APPLICATION_DATA; @@ -689,7 +1087,6 @@ int _gnutls_ktls_recv_int(gnutls_session_t session, content_type_t type, } return ret; } - #else //ENABLE_KTLS gnutls_transport_ktls_enable_flags_t gnutls_transport_is_ktls_enabled(gnutls_session_t session) diff --git a/m4/hooks.m4 b/m4/hooks.m4 index be73a5011f..013d3d243e 100644 --- a/m4/hooks.m4 +++ b/m4/hooks.m4 @@ -367,11 +367,19 @@ LIBTASN1_MINIMUM=4.9 AC_MSG_RESULT($enable_ktls) if test "$enable_ktls" = "yes"; then - AC_CHECK_HEADERS([linux/tls.h], [ - AC_DEFINE([HAVE_KTLS],[1],[KTLS headers found at compile time]) - ], [ - AC_MSG_ERROR([ not found]) - ]) + AC_MSG_CHECKING([whethwe KTLS is supported by the OS]) + AS_CASE([$host_os], + [freebsd*], [AC_CHECK_HEADERS([sys/ktls.h], [ + AC_DEFINE([HAVE_KTLS],[1],[KTLS headers found at compile time]) + ], [ + AC_MSG_ERROR([ not found]) + ])], + [linux*], [AC_CHECK_HEADERS([linux/tls.h], [ + AC_DEFINE([HAVE_KTLS],[1],[KTLS headers found at compile time]) + ], [ + AC_MSG_ERROR([ not found]) + ])] + ) AC_DEFINE([ENABLE_KTLS], 1, [Enable KTLS support]) fi AM_CONDITIONAL(ENABLE_KTLS, test "$enable_ktls" != "no") diff --git a/tests/gnutls_ktls.c b/tests/gnutls_ktls.c index 0103a51a2b..daee77fcc6 100644 --- a/tests/gnutls_ktls.c +++ b/tests/gnutls_ktls.c @@ -347,12 +347,14 @@ void doit(void) { run("NORMAL:-VERS-ALL:+VERS-TLS1.2:-CIPHER-ALL:+AES-128-GCM"); run("NORMAL:-VERS-ALL:+VERS-TLS1.2:-CIPHER-ALL:+AES-256-GCM"); - run("NORMAL:-VERS-ALL:+VERS-TLS1.2:-CIPHER-ALL:+AES-128-CCM"); run("NORMAL:-VERS-ALL:+VERS-TLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305"); run("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM"); run("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-256-GCM"); - run("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-CCM"); run("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+CHACHA20-POLY1305"); +#if defined(__linux__) + run("NORMAL:-VERS-ALL:+VERS-TLS1.2:-CIPHER-ALL:+AES-128-CCM"); + run("NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-CCM"); +#endif } #endif /* _WIN32 */ diff --git a/tests/ktls.sh b/tests/ktls.sh old mode 100755 new mode 100644 index 28dd4d1449..418ef78a5a --- a/tests/ktls.sh +++ b/tests/ktls.sh @@ -13,25 +13,31 @@ # # GnuTLS is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with GnuTLS. If not, see . +# along with GnuTLS. If not, see . -: ${builddir=.} +: ${builddir=.} ${host_os=`uname`} . "$srcdir/scripts/common.sh" -if ! grep '^tls ' /proc/modules 2>&1 /dev/null; then - exit 77 +if [ "$host_os" = "FreeBSD" ]; then + if ! sysctl -n kern.ipc.tls.enable | grep 1 > /dev/null; then + exit 77 + fi +else #check KTLS on Linux + if ! grep '^tls ' /proc/modules 2>&1 /dev/null; then + exit 77 + fi fi testdir=`create_testdir ktls` cfg="$testdir/config" -cat < "$cfg" +cat << EOF > "$cfg" [global] ktls = true EOF