From 2111f5c2834a838c4fc1ca981fddf80cbc589dfc Mon Sep 17 00:00:00 2001 From: Andrew Gallatin Date: Mon, 22 Oct 2018 11:02:19 -0400 Subject: [PATCH] Add support for in-kernel TLS (KTLS) on FreeBSD. - Check for the header to determine if KTLS support is available. - Populate a tls_enable structure with session key material for supported algorithms. At present, AES-GCM128/256 and AES-CBC128/256 with SHA1 and SHA2-256 HMACs are supported. For AES-CBC, only MtE is supported. Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/10045) --- Configure | 8 +++- crypto/bio/bss_sock.c | 8 ++++ include/internal/ktls.h | 97 +++++++++++++++++++++++++++++++++++++++++ ssl/t1_enc.c | 45 ++++++++++++++++++- 4 files changed, 156 insertions(+), 2 deletions(-) diff --git a/Configure b/Configure index 6bba3aeeba..17d5fb5502 100755 --- a/Configure +++ b/Configure @@ -1586,8 +1586,14 @@ unless ($disabled{ktls}) { if ($verstr[2] < $minver) { disable('too-old-kernel', 'ktls'); } + } elsif ($target =~ m/^BSD/) { + my $cc = $config{CROSS_COMPILE}.$config{CC}; + system("printf '#include \n#include ' | $cc -E - >/dev/null 2>&1"); + if ($? != 0) { + disable('too-old-freebsd', 'ktls'); + } } else { - disable('not-linux', 'ktls'); + disable('not-linux-or-freebsd', 'ktls'); } } diff --git a/crypto/bio/bss_sock.c b/crypto/bio/bss_sock.c index ed513495ff..09cc4e30a0 100644 --- a/crypto/bio/bss_sock.c +++ b/crypto/bio/bss_sock.c @@ -152,7 +152,11 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) long ret = 1; int *ip; # ifndef OPENSSL_NO_KTLS +# ifdef __FreeBSD__ + struct tls_enable *crypto_info; +# else struct tls12_crypto_info_aes_gcm_128 *crypto_info; +# endif # endif switch (cmd) { @@ -183,7 +187,11 @@ static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) break; # ifndef OPENSSL_NO_KTLS case BIO_CTRL_SET_KTLS: +# ifdef __FreeBSD__ + crypto_info = (struct tls_enable *)ptr; +# else crypto_info = (struct tls12_crypto_info_aes_gcm_128 *)ptr; +# endif ret = ktls_start(b->num, crypto_info, sizeof(*crypto_info), num); if (ret) BIO_set_ktls_flag(b, num); diff --git a/include/internal/ktls.h b/include/internal/ktls.h index 9f2af1200c..209dff1689 100644 --- a/include/internal/ktls.h +++ b/include/internal/ktls.h @@ -11,6 +11,103 @@ # ifndef HEADER_INTERNAL_KTLS # define HEADER_INTERNAL_KTLS +# if defined(__FreeBSD__) +# include +# include +# include +# include +# include +# include + +/* + * Only used by the tests in sslapitest.c. + */ +# define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8 + +/* + * FreeBSD does not require any additional steps to enable KTLS before + * setting keys. + */ +static ossl_inline int ktls_enable(int fd) +{ + return 1; +} + +/* + * The TCP_TXTLS_ENABLE socket option marks the outgoing socket buffer + * as using TLS. If successful, then data sent using this socket will + * be encrypted and encapsulated in TLS records using the tls_en. + * provided here. + */ +static ossl_inline int ktls_start(int fd, + struct tls_enable *tls_en, + size_t len, int is_tx) +{ + if (is_tx) + return setsockopt(fd, IPPROTO_TCP, TCP_TXTLS_ENABLE, + tls_en, sizeof(*tls_en)) ? 0 : 1; + else + return 0; +} + +/* + * Send a TLS record using the tls_en provided in ktls_start and use + * record_type instead of the default SSL3_RT_APPLICATION_DATA. + * When the socket is non-blocking, then this call either returns EAGAIN or + * the entire record is pushed to TCP. It is impossible to send a partial + * record using this control message. + */ +static ossl_inline int ktls_send_ctrl_message(int fd, unsigned char record_type, + const void *data, size_t length) +{ + struct msghdr msg = { 0 }; + int cmsg_len = sizeof(record_type); + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(cmsg_len)]; + struct iovec msg_iov; /* Vector of data to send/receive into */ + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = IPPROTO_TCP; + cmsg->cmsg_type = TLS_SET_RECORD_TYPE; + cmsg->cmsg_len = CMSG_LEN(cmsg_len); + *((unsigned char *)CMSG_DATA(cmsg)) = record_type; + msg.msg_controllen = cmsg->cmsg_len; + + msg_iov.iov_base = (void *)data; + msg_iov.iov_len = length; + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + + return sendmsg(fd, &msg, 0); +} + +static ossl_inline int ktls_read_record(int fd, void *data, size_t length) +{ + return -1; +} + +/* + * KTLS enables the sendfile system call to send data from a file over + * TLS. + */ +static ossl_inline ossl_ssize_t ktls_sendfile(int s, int fd, off_t off, + size_t size, int flags) +{ + off_t sbytes; + int ret; + + ret = sendfile(fd, s, off, size, NULL, &sbytes, flags); + if (ret == -1) { + if (errno == EAGAIN && sbytes != 0) + return sbytes; + return -1; + } + return sbytes; +} +# endif /* __FreeBSD__ */ + # if defined(OPENSSL_SYS_LINUX) # include diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 64806a333c..09bfb45884 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -151,11 +151,15 @@ int tls1_change_cipher_state(SSL *s, int which) size_t n, i, j, k, cl; int reuse_dd = 0; #ifndef OPENSSL_NO_KTLS +# ifdef __FreeBSD__ + struct tls_enable crypto_info; +# else struct tls12_crypto_info_aes_gcm_128 crypto_info; - BIO *bio; unsigned char geniv[12]; int count_unprocessed; int bit; +# endif + BIO *bio; #endif c = s->s3.tmp.new_sym_enc; @@ -387,6 +391,42 @@ int tls1_change_cipher_state(SSL *s, int which) if (ssl_get_max_send_fragment(s) != SSL3_RT_MAX_PLAIN_LENGTH) goto skip_ktls; +# ifdef __FreeBSD__ + memset(&crypto_info, 0, sizeof(crypto_info)); + switch (s->s3.tmp.new_cipher->algorithm_enc) { + case SSL_AES128GCM: + case SSL_AES256GCM: + crypto_info.cipher_algorithm = CRYPTO_AES_NIST_GCM_16; + crypto_info.iv_len = EVP_GCM_TLS_FIXED_IV_LEN; + break; + case SSL_AES128: + case SSL_AES256: + if (s->ext.use_etm) + goto skip_ktls; + switch (s->s3.tmp.new_cipher->algorithm_mac) { + case SSL_SHA1: + crypto_info.auth_algorithm = CRYPTO_SHA1_HMAC; + break; + case SSL_SHA256: + crypto_info.auth_algorithm = CRYPTO_SHA2_256_HMAC; + break; + default: + goto skip_ktls; + } + crypto_info.cipher_algorithm = CRYPTO_AES_CBC; + crypto_info.iv_len = EVP_CIPHER_iv_length(c); + crypto_info.auth_key = ms; + crypto_info.auth_key_len = *mac_secret_size; + break; + default: + goto skip_ktls; + } + crypto_info.cipher_key = key; + crypto_info.cipher_key_len = EVP_CIPHER_key_length(c); + crypto_info.iv = iv; + crypto_info.tls_vmajor = (s->version >> 8) & 0x000000ff; + crypto_info.tls_vminor = (s->version & 0x000000ff); +# else /* check that cipher is AES_GCM_128 */ if (EVP_CIPHER_nid(c) != NID_aes_128_gcm || EVP_CIPHER_mode(c) != EVP_CIPH_GCM_MODE @@ -396,6 +436,7 @@ int tls1_change_cipher_state(SSL *s, int which) /* check version is 1.2 */ if (s->version != TLS1_2_VERSION) goto skip_ktls; +# endif if (which & SSL3_CC_WRITE) bio = s->wbio; @@ -422,6 +463,7 @@ int tls1_change_cipher_state(SSL *s, int which) goto err; } +# ifndef __FreeBSD__ memset(&crypto_info, 0, sizeof(crypto_info)); crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; crypto_info.info.version = s->version; @@ -455,6 +497,7 @@ int tls1_change_cipher_state(SSL *s, int which) count_unprocessed--; } } +# endif /* ktls works with user provided buffers directly */ if (BIO_set_ktls(bio, &crypto_info, which & SSL3_CC_WRITE)) { -- 2.34.1