[openssl] master update

Matt Caswell matt at openssl.org
Tue May 7 13:28:55 UTC 2019


The branch master has been updated
       via  7c3a7561b536264b282f604efc959edad18807d7 (commit)
       via  72fb59c72186c327e047cd29d8a66a4a323b9f3b (commit)
      from  260a16f33682a819414fcba6161708a5e6bdff50 (commit)


- Log -----------------------------------------------------------------
commit 7c3a7561b536264b282f604efc959edad18807d7
Author: Boris Pismenny <borisp at mellanox.com>
Date:   Sat Apr 13 17:20:35 2019 +0300

    ssl: Add SSL_sendfile
    
    This commit adds the SSL_sendfile call, which allows KTLS sockets to
    transmit file using zero-copy semantics.
    
    Signed-off-by: Boris Pismenny <borisp at mellanox.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8727)

commit 72fb59c72186c327e047cd29d8a66a4a323b9f3b
Author: Boris Pismenny <borisp at mellanox.com>
Date:   Thu Apr 11 16:24:42 2019 +0300

    Linux ktls sendfile
    
    This commit introduces support for Linux KTLS sendfile.
    Sendfile semantics require the use of a kernel TLS socket to construct the TLS
    record headers, encrypt and authenticate the data.
    KTLS sendfile improves performance by avoiding the copy of file data into user
    space, which is required today.
    
    Signed-off-by: Boris Pismenny <borisp at mellanox.com>
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8727)

-----------------------------------------------------------------------

Summary of changes:
 crypto/err/openssl.txt   |   1 +
 doc/man3/SSL_write.pod   |  31 +++++++++++++-
 include/internal/ktls.h  |  15 +++++++
 include/openssl/err.h    |   1 +
 include/openssl/ssl.h    |   2 +
 include/openssl/sslerr.h |   1 +
 ssl/ssl_err.c            |   1 +
 ssl/ssl_lib.c            |  67 +++++++++++++++++++++++++++++
 test/sslapitest.c        | 109 ++++++++++++++++++++++++++++++++++++++++++++++-
 util/libssl.num          |   1 +
 10 files changed, 226 insertions(+), 3 deletions(-)

diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 8ad85f5..14a7e3b 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1437,6 +1437,7 @@ SSL_F_SSL_RENEGOTIATE:516:SSL_renegotiate
 SSL_F_SSL_RENEGOTIATE_ABBREVIATED:546:SSL_renegotiate_abbreviated
 SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT:320:*
 SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT:321:*
+SSL_F_SSL_SENDFILE:639:SSL_sendfile
 SSL_F_SSL_SESSION_DUP:348:ssl_session_dup
 SSL_F_SSL_SESSION_NEW:189:SSL_SESSION_new
 SSL_F_SSL_SESSION_PRINT_FP:190:SSL_SESSION_print_fp
diff --git a/doc/man3/SSL_write.pod b/doc/man3/SSL_write.pod
index a73bc06..b18c215 100644
--- a/doc/man3/SSL_write.pod
+++ b/doc/man3/SSL_write.pod
@@ -2,12 +2,13 @@
 
 =head1 NAME
 
-SSL_write_ex, SSL_write - write bytes to a TLS/SSL connection
+SSL_write_ex, SSL_write, SSL_sendfile - write bytes to a TLS/SSL connection
 
 =head1 SYNOPSIS
 
  #include <openssl/ssl.h>
 
+ ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, int flags);
  int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written);
  int SSL_write(SSL *ssl, const void *buf, int num);
 
@@ -17,6 +18,14 @@ SSL_write_ex() and SSL_write() write B<num> bytes from the buffer B<buf> into
 the specified B<ssl> connection. On success SSL_write_ex() will store the number
 of bytes written in B<*written>.
 
+SSL_sendfile() writes B<size> bytes from offset B<offset> in the file
+descriptor B<fd> to the specified SSL connection B<s>. This function provides
+efficient zero-copy semantics. SSL_sendfile() is available only when
+Kernel TLS is enabled, which can be checked by calling BIO_get_ktls_send().
+It is provided here to allow users to maintain the same interface.
+The meaning of B<flags> is platform dependent.
+Currently, under Linux it is ignored.
+
 =head1 NOTES
 
 In the paragraphs below a "write function" is defined as one of either
@@ -104,17 +113,35 @@ You should instead call SSL_get_error() to find out if it's retryable.
 
 =back
 
+For SSL_sendfile(), the following return values can occur:
+
+=over 4
+
+=item Z<>>= 0
+
+The write operation was successful, the return value is the number
+of bytes of the file written to the TLS/SSL connection.
+
+=item E<lt> 0
+
+The write operation was not successful, because either the connection was
+closed, an error occured or action must be taken by the calling process.
+Call SSL_get_error() with the return value to find out the reason.
+
+=back
+
 =head1 SEE ALSO
 
 L<SSL_get_error(3)>, L<SSL_read_ex(3)>, L<SSL_read(3)>
 L<SSL_CTX_set_mode(3)>, L<SSL_CTX_new(3)>,
 L<SSL_connect(3)>, L<SSL_accept(3)>
-L<SSL_set_connect_state(3)>,
+L<SSL_set_connect_state(3)>, L<BIO_ctrl(3)>,
 L<ssl(7)>, L<bio(7)>
 
 =head1 HISTORY
 
 The SSL_write_ex() function was added in OpenSSL 1.1.1.
+The SSL_sendfile() function was added in OpenSSL 3.0.0.
 
 =head1 COPYRIGHT
 
diff --git a/include/internal/ktls.h b/include/internal/ktls.h
index d7bd1f3..9f2af12 100644
--- a/include/internal/ktls.h
+++ b/include/internal/ktls.h
@@ -73,8 +73,14 @@ static ossl_inline int ktls_read_record(int fd, void *data, size_t length)
     return -1;
 }
 
+static ossl_inline ossl_ssize_t ktls_sendfile(int s, int fd, off_t off, size_t size, int flags)
+{
+    return -1;
+}
+
 #   else                        /* KERNEL_VERSION */
 
+#    include <sys/sendfile.h>
 #    include <netinet/tcp.h>
 #    include <linux/tls.h>
 #    include <linux/socket.h>
@@ -158,6 +164,15 @@ static ossl_inline int ktls_send_ctrl_message(int fd, unsigned char record_type,
     return sendmsg(fd, &msg, 0);
 }
 
+/*
+ * KTLS enables the sendfile system call to send data from a file over TLS.
+ * @flags are ignored on Linux. (placeholder for FreeBSD sendfile)
+ * */
+static ossl_inline ossl_ssize_t ktls_sendfile(int s, int fd, off_t off, size_t size, int flags)
+{
+    return sendfile(s, fd, &off, size);
+}
+
 #    define K_MIN1_RX  17
 #    if LINUX_VERSION_CODE < KERNEL_VERSION(K_MAJ, K_MIN1_RX, K_MIN2)
 
diff --git a/include/openssl/err.h b/include/openssl/err.h
index 8fcdfb4..7398029 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -177,6 +177,7 @@ typedef struct err_state_st {
 # define SYS_F_STAT              22
 # define SYS_F_FCNTL             23
 # define SYS_F_FSTAT             24
+# define SYS_F_SENDFILE          25
 
 /* reasons */
 # define ERR_R_SYS_LIB   ERR_LIB_SYS/* 2 */
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index f4b17f1..60712d0 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1848,6 +1848,8 @@ __owur int SSL_read_early_data(SSL *s, void *buf, size_t num,
                                size_t *readbytes);
 __owur int SSL_peek(SSL *ssl, void *buf, int num);
 __owur int SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
+__owur ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size,
+                                 int flags);
 __owur int SSL_write(SSL *ssl, const void *buf, int num);
 __owur int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written);
 __owur int SSL_write_early_data(SSL *s, const void *buf, size_t num,
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index 7f776f9..385fda3 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -217,6 +217,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_RENEGOTIATE_ABBREVIATED                546
 # define SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT                320
 # define SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT                321
+# define SSL_F_SSL_SENDFILE                               639
 # define SSL_F_SSL_SESSION_DUP                            348
 # define SSL_F_SSL_SESSION_NEW                            189
 # define SSL_F_SSL_SESSION_PRINT_FP                       190
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index afe1b58..daeee1e 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -311,6 +311,7 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "SSL_renegotiate_abbreviated"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT, 0), ""},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, 0), ""},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SENDFILE, 0), "SSL_sendfile"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_DUP, 0), "ssl_session_dup"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_NEW, 0), "SSL_SESSION_new"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_PRINT_FP, 0),
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 89a4100..910f82b 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -11,6 +11,7 @@
 
 #include <stdio.h>
 #include "ssl_locl.h"
+#include "e_os.h"
 #include <openssl/objects.h>
 #include <openssl/x509v3.h>
 #include <openssl/rand.h>
@@ -2008,6 +2009,72 @@ int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written)
     }
 }
 
+ossl_ssize_t SSL_sendfile(SSL *s, int fd, off_t offset, size_t size, int flags)
+{
+    ossl_ssize_t ret;
+
+    if (s->handshake_func == NULL) {
+        SSLerr(SSL_F_SSL_SENDFILE, SSL_R_UNINITIALIZED);
+        return -1;
+    }
+
+    if (s->shutdown & SSL_SENT_SHUTDOWN) {
+        s->rwstate = SSL_NOTHING;
+        SSLerr(SSL_F_SSL_SENDFILE, SSL_R_PROTOCOL_IS_SHUTDOWN);
+        return -1;
+    }
+
+    if (!BIO_get_ktls_send(s->wbio)) {
+        SSLerr(SSL_F_SSL_SENDFILE, SSL_R_UNINITIALIZED);
+        return -1;
+    }
+
+    /* If we have an alert to send, lets send it */
+    if (s->s3.alert_dispatch) {
+        ret = (ossl_ssize_t)s->method->ssl_dispatch_alert(s);
+        if (ret <= 0) {
+            /* SSLfatal() already called if appropriate */
+            return ret;
+        }
+        /* if it went, fall through and send more stuff */
+    }
+
+    s->rwstate = SSL_WRITING;
+    if (BIO_flush(s->wbio) <= 0) {
+        if (!BIO_should_retry(s->wbio)) {
+            s->rwstate = SSL_NOTHING;
+        } else {
+#ifdef EAGAIN
+            set_sys_error(EAGAIN);
+#endif
+        }
+        return -1;
+    }
+
+#ifndef OPENSSL_NO_KTLS
+    ret = ktls_sendfile(SSL_get_wfd(s), fd, offset, size, flags);
+#else
+    ret = -1;
+#endif
+    if (ret < 0) {
+#if defined(EAGAIN) && defined(EINTR) && defined(EBUSY)
+        if ((get_last_sys_error() == EAGAIN) ||
+            (get_last_sys_error() == EINTR) ||
+            (get_last_sys_error() == EBUSY))
+            BIO_set_retry_write(s->wbio);
+        else
+#endif
+#ifdef OPENSSL_NO_KTLS
+            SYSerr(SYS_F_SENDFILE, get_last_sys_error());
+#else
+            SSLerr(SSL_F_SSL_SENDFILE, SSL_R_UNINITIALIZED);
+#endif
+        return ret;
+    }
+    s->rwstate = SSL_NOTHING;
+    return ret;
+}
+
 int SSL_write(SSL *s, const void *buf, int num)
 {
     int ret;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 7ca8c75..3504eea 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <stdio.h>
 #include <string.h>
 
 #include <openssl/opensslconf.h>
@@ -17,6 +18,7 @@
 #include <openssl/srp.h>
 #include <openssl/txt_db.h>
 #include <openssl/aes.h>
+#include <openssl/rand.h>
 
 #include "ssltestlib.h"
 #include "testutil.h"
@@ -918,6 +920,111 @@ end:
     return testresult;
 }
 
+#define SENDFILE_SZ                     (16 * 4096)
+#define SENDFILE_CHUNK                  (4 * 4096)
+#define min(a,b)                        ((a) > (b) ? (b) : (a))
+
+static int test_ktls_sendfile(void)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    unsigned char *buf, *buf_dst;
+    BIO *out = NULL, *in = NULL;
+    int cfd, sfd, ffd, err;
+    ssize_t chunk_size = 0;
+    off_t chunk_off = 0;
+    int testresult = 0;
+    FILE *ffdp;
+
+    buf = OPENSSL_zalloc(SENDFILE_SZ);
+    buf_dst = OPENSSL_zalloc(SENDFILE_SZ);
+    if (!TEST_ptr(buf) || !TEST_ptr(buf_dst)
+        || !TEST_true(create_test_sockets(&cfd, &sfd)))
+        goto end;
+
+    /* Skip this test if the platform does not support ktls */
+    if (!ktls_chk_platform(sfd)) {
+        testresult = 1;
+        goto end;
+    }
+
+    /* Create a session based on SHA-256 */
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(),
+                                       TLS_client_method(),
+                                       TLS1_2_VERSION, TLS1_2_VERSION,
+                                       &sctx, &cctx, cert, privkey))
+        || !TEST_true(SSL_CTX_set_cipher_list(cctx,
+                                              "AES128-GCM-SHA256"))
+        || !TEST_true(create_ssl_objects2(sctx, cctx, &serverssl,
+                                          &clientssl, sfd, cfd)))
+        goto end;
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                                         SSL_ERROR_NONE))
+        || !TEST_true(BIO_get_ktls_send(serverssl->wbio)))
+        goto end;
+
+    RAND_bytes(buf, SENDFILE_SZ);
+    out = BIO_new_file(tmpfilename, "wb");
+    if (!TEST_ptr(out))
+        goto end;
+
+    if (BIO_write(out, buf, SENDFILE_SZ) != SENDFILE_SZ)
+        goto end;
+
+    BIO_free(out);
+    out = NULL;
+    in = BIO_new_file(tmpfilename, "rb");
+    BIO_get_fp(in, &ffdp);
+    ffd = fileno(ffdp);
+
+    while (chunk_off < SENDFILE_SZ) {
+        chunk_size = min(SENDFILE_CHUNK, SENDFILE_SZ - chunk_off);
+        while ((err = SSL_sendfile(serverssl,
+                                   ffd,
+                                   chunk_off,
+                                   chunk_size,
+                                   0)) != chunk_size) {
+            if (SSL_get_error(serverssl, err) != SSL_ERROR_WANT_WRITE)
+                goto end;
+        }
+        while ((err = SSL_read(clientssl,
+                               buf_dst + chunk_off,
+                               chunk_size)) != chunk_size) {
+            if (SSL_get_error(clientssl, err) != SSL_ERROR_WANT_READ)
+                goto end;
+        }
+
+        /* verify the payload */
+        if (!TEST_mem_eq(buf_dst + chunk_off,
+                         chunk_size,
+                         buf + chunk_off,
+                         chunk_size))
+            goto end;
+
+        chunk_off += chunk_size;
+    }
+
+    testresult = 1;
+end:
+    if (clientssl) {
+        SSL_shutdown(clientssl);
+        SSL_free(clientssl);
+    }
+    if (serverssl) {
+        SSL_shutdown(serverssl);
+        SSL_free(serverssl);
+    }
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    serverssl = clientssl = NULL;
+    BIO_free(out);
+    BIO_free(in);
+    OPENSSL_free(buf);
+    OPENSSL_free(buf_dst);
+    return testresult;
+}
+
 static int test_ktls_no_txrx_client_no_txrx_server(void)
 {
     return execute_test_ktls(0, 0, 0, 0);
@@ -997,7 +1104,6 @@ static int test_ktls_client_server(void)
 {
     return execute_test_ktls(1, 1, 1, 1);
 }
-
 #endif
 
 static int test_large_message_tls(void)
@@ -6280,6 +6386,7 @@ int setup_tests(void)
     ADD_TEST(test_ktls_no_rx_client_server);
     ADD_TEST(test_ktls_no_tx_client_server);
     ADD_TEST(test_ktls_client_server);
+    ADD_TEST(test_ktls_sendfile);
 #endif
     ADD_TEST(test_large_message_tls);
     ADD_TEST(test_large_message_tls_read_ahead);
diff --git a/util/libssl.num b/util/libssl.num
index d59ccf9..c34200f 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -503,3 +503,4 @@ SSL_CTX_set_async_callback_arg          503	3_0_0	EXIST::FUNCTION:
 SSL_set_async_callback                  504	3_0_0	EXIST::FUNCTION:
 SSL_set_async_callback_arg              505	3_0_0	EXIST::FUNCTION:
 SSL_get_async_status                    506	3_0_0	EXIST::FUNCTION:
+SSL_sendfile                            507	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list