Add support for in-kernel TLS (KTLS) on FreeBSD.
authorAndrew Gallatin <gallatin@gmail.com>
Mon, 22 Oct 2018 15:02:19 +0000 (11:02 -0400)
committerMatt Caswell <matt@openssl.org>
Thu, 31 Oct 2019 10:24:32 +0000 (10:24 +0000)
- Check for the <sys/ktls.h> 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 <tmraz@fedoraproject.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10045)

Configure
crypto/bio/bss_sock.c
include/internal/ktls.h
ssl/t1_enc.c

index 6bba3aeebaf8f4539498c1a3a57f4ea15dda25de..17d5fb5502726af2ef5fa2079d3a023f20f4ce98 100755 (executable)
--- 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 <sys/types.h>\n#include <sys/ktls.h>' | $cc -E - >/dev/null 2>&1");
+        if ($? != 0) {
+            disable('too-old-freebsd', 'ktls');
+        }
     } else {
-        disable('not-linux', 'ktls');
+        disable('not-linux-or-freebsd', 'ktls');
     }
 }
 
index ed513495ffa256290039ea3deb51876ba15f611f..09cc4e30a02f579bd1c99c5043745b2eba11682d 100644 (file)
@@ -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);
index 9f2af1200c922b4fb0be4838995af42a30f196ed..209dff1689ffd312977c79836c9f9979f14084a6 100644 (file)
 # ifndef HEADER_INTERNAL_KTLS
 #  define HEADER_INTERNAL_KTLS
 
+#  if defined(__FreeBSD__)
+#   include <sys/types.h>
+#   include <sys/socket.h>
+#   include <sys/ktls.h>
+#   include <netinet/in.h>
+#   include <netinet/tcp.h>
+#   include <crypto/cryptodev.h>
+
+/*
+ * 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 <linux/version.h>
 
index 64806a333c572993fb2496e3d933dd03dcfe3206..09bfb45884af19f559a00ff3e5ed4151d4743753 100644 (file)
@@ -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)) {