mirror of
https://git.freebsd.org/ports.git
synced 2025-07-07 04:19:17 -04:00
This functionality is available with the following prerequisites: o) security/openssl built from ports with the kTLS options defined; o) FreeBSD 13. Obtained from: www/nginx-devel Sponsored by: Netzkommune GmbH
312 lines
9.8 KiB
Text
312 lines
9.8 KiB
Text
From 11ad5d15c487ecc0a37f9747bb4bfa5bb96893c1 Mon Sep 17 00:00:00 2001
|
|
From: John Baldwin <jhb@FreeBSD.org>
|
|
Date: Thu, 22 Aug 2019 12:18:32 -0700
|
|
Subject: [PATCH] Add support for using SSL_sendfile from OpenSSL.
|
|
|
|
This uses kernel TLS on systems supported by OpenSSL to send
|
|
files via sendfile() over TLS connections.
|
|
---
|
|
auto/lib/openssl/conf | 8 ++
|
|
src/event/ngx_event_openssl.c | 172 ++++++++++++++++++++++++++++++++++
|
|
src/event/ngx_event_openssl.h | 7 ++
|
|
src/http/ngx_http_request.c | 14 ++-
|
|
src/http/ngx_http_upstream.c | 5 +
|
|
5 files changed, 203 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf
|
|
index 4fb52df7fe..c4772248ae 100644
|
|
--- a/auto/lib/openssl/conf
|
|
+++ b/auto/lib/openssl/conf
|
|
@@ -123,6 +123,14 @@ else
|
|
CORE_INCS="$CORE_INCS $ngx_feature_path"
|
|
CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
|
|
OPENSSL=YES
|
|
+
|
|
+ ngx_feature="SSL_sendfile()"
|
|
+ ngx_feature_name="NGX_SSL_SENDFILE"
|
|
+ ngx_feature_run=no
|
|
+ ngx_feature_test="SSL *ssl;
|
|
+ (void)BIO_get_ktls_send(SSL_get_wbio(ssl));
|
|
+ SSL_sendfile(ssl, -1, 0, 0, 0);"
|
|
+ . auto/feature
|
|
fi
|
|
fi
|
|
|
|
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
|
|
index 93a6ae46ea..04759827fc 100644
|
|
--- a/src/event/ngx_event_openssl.c
|
|
+++ b/src/event/ngx_event_openssl.c
|
|
@@ -52,6 +52,10 @@ static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
|
|
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
|
|
ngx_err_t err, char *text);
|
|
static void ngx_ssl_clear_error(ngx_log_t *log);
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, int fd, off_t off,
|
|
+ size_t size, int flags);
|
|
+#endif
|
|
|
|
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
|
|
ngx_str_t *sess_ctx, ngx_array_t *certificates);
|
|
@@ -1712,7 +1716,11 @@ ngx_ssl_handshake(ngx_connection_t *c)
|
|
c->recv = ngx_ssl_recv;
|
|
c->send = ngx_ssl_write;
|
|
c->recv_chain = ngx_ssl_recv_chain;
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ c->send_chain = ngx_ssl_sendfile_chain;
|
|
+#else
|
|
c->send_chain = ngx_ssl_send_chain;
|
|
+#endif
|
|
|
|
#ifndef SSL_OP_NO_RENEGOTIATION
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
@@ -1741,6 +1749,13 @@ ngx_ssl_handshake(ngx_connection_t *c)
|
|
|
|
c->ssl->handshaked = 1;
|
|
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ c->ssl->can_use_sendfile = !!BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection));
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
+ "BIO_get_ktls_send: %d", c->ssl->can_use_sendfile);
|
|
+ c->sendfile = c->ssl->can_use_sendfile ? 1 : 0;
|
|
+#endif
|
|
+
|
|
return NGX_OK;
|
|
}
|
|
|
|
@@ -2609,6 +2624,163 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
|
|
return in;
|
|
}
|
|
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ngx_chain_t *
|
|
+ngx_ssl_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
|
|
+{
|
|
+ int can_use_sendfile;
|
|
+ ssize_t n;
|
|
+
|
|
+ can_use_sendfile = BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection));
|
|
+
|
|
+ ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
+ "Sending chain %p can_use_sendfile:%d c->sendfile:%d " \
|
|
+ "c->ssl->buffer:%d limit:%O",
|
|
+ in, can_use_sendfile, c->sendfile, c->ssl->buffer, limit);
|
|
+
|
|
+ if (! (can_use_sendfile && c->sendfile) || c->ssl->buffer) {
|
|
+ return ngx_ssl_send_chain(c, in, limit);
|
|
+ }
|
|
+
|
|
+ /* the maximum limit size is the maximum int32_t value - the page size */
|
|
+ if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {
|
|
+ limit = NGX_MAX_INT32_VALUE - ngx_pagesize;
|
|
+ }
|
|
+
|
|
+ while (in) {
|
|
+ if (ngx_buf_special(in->buf)) {
|
|
+ in = in->next;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (in->buf->in_file) {
|
|
+ ngx_chain_t *cl;
|
|
+ int sendfile_flags;
|
|
+ off_t sendfile_size;
|
|
+
|
|
+ cl = in;
|
|
+#ifdef __FreeBSD__
|
|
+ sendfile_flags = /* in->buf->sendfile_flags |*/ SF_NODISKIO;
|
|
+#else
|
|
+ sendfile_flags = in->buf->sendfile_flags;
|
|
+#endif
|
|
+ sendfile_size = ngx_chain_coalesce_file(&cl, limit);
|
|
+
|
|
+ n = ngx_ssl_sendfile(c, in->buf->file->fd, in->buf->file_pos,
|
|
+ sendfile_size, sendfile_flags);
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
+ "ngx_ssl_sendfile returns:%z", n);
|
|
+ } else {
|
|
+ n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
+ "ngx_ssl_write returns:%z", n);
|
|
+ }
|
|
+
|
|
+ if (n == NGX_ERROR) {
|
|
+ return NGX_CHAIN_ERROR;
|
|
+ }
|
|
+ if (n == NGX_AGAIN) {
|
|
+ return in;
|
|
+ }
|
|
+ if (n == NGX_BUSY) {
|
|
+ c->busy_count = 1;
|
|
+ c->write->delayed = 1;
|
|
+ ngx_add_timer(c->write, 10);
|
|
+ return in;
|
|
+ }
|
|
+
|
|
+ in = ngx_chain_update_sent(in, (off_t) n);
|
|
+ }
|
|
+
|
|
+ return in;
|
|
+}
|
|
+
|
|
+static ssize_t
|
|
+ngx_ssl_sendfile(ngx_connection_t *c, int fd, off_t off, size_t size, int flags)
|
|
+{
|
|
+ int n, sslerr;
|
|
+ ngx_err_t err;
|
|
+
|
|
+ ngx_ssl_clear_error(c->log);
|
|
+
|
|
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
+ "SSL to sendfile: %uz at %O with %Xd", size, off, flags);
|
|
+
|
|
+ n = SSL_sendfile(c->ssl->connection, fd, off, size, flags);
|
|
+
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_sendfile: %d", n);
|
|
+
|
|
+ if (n > 0) {
|
|
+
|
|
+ if (c->ssl->saved_read_handler) {
|
|
+
|
|
+ c->read->handler = c->ssl->saved_read_handler;
|
|
+ c->ssl->saved_read_handler = NULL;
|
|
+ c->read->ready = 1;
|
|
+
|
|
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
|
+ return NGX_ERROR;
|
|
+ }
|
|
+
|
|
+ ngx_post_event(c->read, &ngx_posted_events);
|
|
+ }
|
|
+
|
|
+ c->sent += n;
|
|
+
|
|
+ return n;
|
|
+ }
|
|
+
|
|
+ sslerr = SSL_get_error(c->ssl->connection, n);
|
|
+
|
|
+#ifdef __FreeBSD__
|
|
+ if (sslerr == SSL_ERROR_WANT_WRITE && ngx_errno == EBUSY) {
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "bioerr=NGX_EBUSY, sslerr=%d", sslerr);
|
|
+ return NGX_BUSY;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
|
|
+
|
|
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
|
|
+
|
|
+ if (sslerr == SSL_ERROR_WANT_WRITE) {
|
|
+ c->write->ready = 0;
|
|
+ return NGX_AGAIN;
|
|
+ }
|
|
+
|
|
+ if (sslerr == SSL_ERROR_WANT_READ) {
|
|
+
|
|
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
|
+ "peer started SSL renegotiation");
|
|
+
|
|
+ c->read->ready = 0;
|
|
+
|
|
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
|
+ return NGX_ERROR;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * we do not set the timer because there is already
|
|
+ * the write event timer
|
|
+ */
|
|
+
|
|
+ if (c->ssl->saved_read_handler == NULL) {
|
|
+ c->ssl->saved_read_handler = c->read->handler;
|
|
+ c->read->handler = ngx_ssl_read_handler;
|
|
+ }
|
|
+
|
|
+ return NGX_AGAIN;
|
|
+ }
|
|
+
|
|
+ c->ssl->no_wait_shutdown = 1;
|
|
+ c->ssl->no_send_shutdown = 1;
|
|
+ c->write->error = 1;
|
|
+
|
|
+ ngx_ssl_connection_error(c, sslerr, err, "SSL_sendfile() failed");
|
|
+
|
|
+ return NGX_ERROR;
|
|
+}
|
|
+#endif
|
|
|
|
ssize_t
|
|
ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
|
|
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
|
|
index 329760d093..233b7f20c8 100644
|
|
--- a/src/event/ngx_event_openssl.h
|
|
+++ b/src/event/ngx_event_openssl.h
|
|
@@ -106,6 +106,9 @@ struct ngx_ssl_connection_s {
|
|
unsigned in_ocsp:1;
|
|
unsigned early_preread:1;
|
|
unsigned write_blocked:1;
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ unsigned can_use_sendfile:1;
|
|
+#endif
|
|
};
|
|
|
|
|
|
@@ -289,6 +292,10 @@ ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
|
|
ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);
|
|
ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
|
|
off_t limit);
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ngx_chain_t *ngx_ssl_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
|
|
+ off_t limit);
|
|
+#endif
|
|
void ngx_ssl_free_buffer(ngx_connection_t *c);
|
|
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
|
|
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
|
|
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
|
|
index 68d81e9320..e4a922a83a 100644
|
|
--- a/src/http/ngx_http_request.c
|
|
+++ b/src/http/ngx_http_request.c
|
|
@@ -608,7 +608,10 @@ ngx_http_alloc_request(ngx_connection_t *c)
|
|
|
|
#if (NGX_HTTP_SSL)
|
|
if (c->ssl) {
|
|
- r->main_filter_need_in_memory = 1;
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ if (c->ssl->can_use_sendfile == 0)
|
|
+#endif
|
|
+ r->main_filter_need_in_memory = 1;
|
|
}
|
|
#endif
|
|
|
|
@@ -747,8 +750,13 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
|
|
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
|
|
ngx_http_ssl_module);
|
|
|
|
- if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
|
|
- != NGX_OK)
|
|
+ if (ngx_ssl_create_connection(&sscf->ssl, c,
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ 0
|
|
+#else
|
|
+ NGX_SSL_BUFFER
|
|
+#endif
|
|
+ ) != NGX_OK)
|
|
{
|
|
ngx_http_close_connection(c);
|
|
return;
|
|
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
|
|
index 9cbb5a3b0c..f93f2ae244 100644
|
|
--- a/src/http/ngx_http_upstream.c
|
|
+++ b/src/http/ngx_http_upstream.c
|
|
@@ -1715,6 +1715,11 @@ ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
|
|
return;
|
|
}
|
|
|
|
+#if (NGX_SSL_SENDFILE)
|
|
+ c->sendfile = 0;
|
|
+ u->output.sendfile = 0;
|
|
+#endif
|
|
+
|
|
ngx_http_upstream_ssl_handshake(r, u, c);
|
|
}
|
|
|