You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by sh...@apache.org on 2020/07/01 15:54:21 UTC

[trafficserver] branch master updated: Fix support for openssl async engine (#6910)

This is an automated email from the ASF dual-hosted git repository.

shinrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new c32fc64  Fix support for openssl async engine (#6910)
c32fc64 is described below

commit c32fc645ea44ba0615461d10057ef5a8e9516b29
Author: Susan Hinrichs <sh...@yahoo-inc.com>
AuthorDate: Wed Jul 1 10:54:09 2020 -0500

    Fix support for openssl async engine (#6910)
---
 contrib/openssl/README.md               |  2 +-
 contrib/openssl/async_engine.c          | 45 ++++++++++-----
 iocore/net/P_UnixNet.h                  | 51 +++++++++++------
 iocore/net/SSLNetVConnection.cc         | 26 +++++----
 iocore/net/SSLUtils.cc                  |  7 ++-
 tests/gold_tests/tls/tls_engine.test.py | 98 +++++++++++++++++++++++++++++++++
 6 files changed, 185 insertions(+), 44 deletions(-)

diff --git a/contrib/openssl/README.md b/contrib/openssl/README.md
index 428f9a6..faa89c7 100644
--- a/contrib/openssl/README.md
+++ b/contrib/openssl/README.md
@@ -5,6 +5,6 @@ It should be built as follows.  It must be build against openssl 1.1 or better f
 
 gcc -fPIC -shared -g -o async-test.so -I<path to openssl headers> -L<path to openssl library> -lssl -lcrypto -lpthread async_engine.c
 
-load_engine.cnf is an example openssl config file that can be passed to Traffic Server via the proxy.config.ssl.engine_cnf_file setting.
+load_engine.cnf is an example openssl config file that can be passed to Traffic Server via the proxy.config.ssl.engine.conf_file setting.
 It describes which crypto engines should be loaded and how they should be used.  In the case of our async-test crypto engine it will be used for
 RSA operations
diff --git a/contrib/openssl/async_engine.c b/contrib/openssl/async_engine.c
index facffe0..7c3604d 100644
--- a/contrib/openssl/async_engine.c
+++ b/contrib/openssl/async_engine.c
@@ -72,6 +72,15 @@ static int async_rsa_finish(RSA *rsa);
 
 static RSA_METHOD *async_rsa_method = NULL;
 
+EVP_PKEY *async_load_privkey(ENGINE *e, const char *s_key_id, UI_METHOD *ui_method, void *callback_data)
+{
+  printf("Loading key %s\n", s_key_id);
+  FILE *f = fopen(s_key_id, "r");
+  EVP_PKEY *key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+  fclose(f);
+  return key;
+}
+
 static int bind_async(ENGINE *e)
 {
     /* Setup RSA_METHOD */
@@ -96,7 +105,8 @@ static int bind_async(ENGINE *e)
         || !ENGINE_set_RSA(e, async_rsa_method)
         || !ENGINE_set_destroy_function(e, async_destroy)
         || !ENGINE_set_init_function(e, engine_async_init)
-        || !ENGINE_set_finish_function(e, async_finish)) {
+        || !ENGINE_set_finish_function(e, async_finish)
+        || !ENGINE_set_load_privkey_function(e, async_load_privkey)) {
         fprintf(stderr, "Failed to initialize\n");
         return 0;
     }
@@ -176,14 +186,17 @@ static void async_pause_job(void) {
     OSSL_ASYNC_FD *writefd;
     char buf = DUMMY_CHAR;
 
-    if ((job = ASYNC_get_current_job()) == NULL)
+    if ((job = ASYNC_get_current_job()) == NULL) {
+        printf("No job\n");
         return;
+    }
 
     waitctx = ASYNC_get_wait_ctx(job);
 
     if (ASYNC_WAIT_CTX_get_fd(waitctx, engine_id, &pipefds[0],
                               (void **)&writefd)) {
-        pipefds[1] = *writefd;
+        printf("Existing wait ctx\n");
+        return;
     } else {
         writefd = (OSSL_ASYNC_FD *)OPENSSL_malloc(sizeof(*writefd));
         if (writefd == NULL)
@@ -194,6 +207,8 @@ static void async_pause_job(void) {
         }
         *writefd = pipefds[1];
 
+        printf("New wait ctx %d %d\n", pipefds[0], pipefds[1]);
+
         if(!ASYNC_WAIT_CTX_set_wait_fd(waitctx, engine_id, pipefds[0],
                                        writefd, wait_cleanup)) {
             wait_cleanup(waitctx, engine_id, pipefds[0], writefd);
@@ -213,9 +228,11 @@ void *
 delay_method(void *arg)
 {
   int signal_fd = (intptr_t)arg;
-  sleep(5);
-  uint64_t buf = 1;
+  sleep(2);
+  char buf = DUMMY_CHAR;
   write(signal_fd, &buf, sizeof(buf));
+  printf("Send signal to %d\n", signal_fd);
+  return NULL;
 }
 
 
@@ -223,25 +240,27 @@ void
 spawn_delay_thread()
 {
   pthread_t thread_id;
-  OSSL_ASYNC_FD signal_fd;
-  OSSL_ASYNC_FD pipefds[2] = {0, 0};
   ASYNC_JOB *job;
-  if ((job = ASYNC_get_current_job()) == NULL)
+  if ((job = ASYNC_get_current_job()) == NULL) {
+      printf("Spawn no job\n");
       return;
+  }
 
   ASYNC_WAIT_CTX *waitctx = ASYNC_get_wait_ctx(job);
 
   size_t numfds;
-  if (ASYNC_WAIT_CTX_get_all_fds(waitctx, &signal_fd, &numfds) && numfds > 0) {
+  if (ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) && numfds > 0) {
+    printf("Spawn, wait_ctx exists.  Go away, something else is using this job\n");
   } else {
+    OSSL_ASYNC_FD signal_fd;
     OSSL_ASYNC_FD pipefds[2] = {0,0};
     OSSL_ASYNC_FD *writefd = OPENSSL_malloc(sizeof(*writefd));
     pipe(pipefds);
     signal_fd = *writefd = pipefds[1];
     ASYNC_WAIT_CTX_set_wait_fd(waitctx, engine_id, pipefds[0], writefd, wait_cleanup);
+    printf("Spawn, create wait_ctx %d %d\n", pipefds[0], pipefds[1]);
+    pthread_create(&thread_id, NULL, delay_method, (void *)((intptr_t)signal_fd));
   }
-
-  pthread_create(&thread_id, NULL, delay_method, (void *)((intptr_t)signal_fd));
 }
 
 
@@ -264,7 +283,7 @@ static int async_pub_dec(int flen, const unsigned char *from,
 static int async_rsa_priv_enc(int flen, const unsigned char *from,
                       unsigned char *to, RSA *rsa, int padding)
 {
-    //printf("async_priv_enc\n");
+    printf("async_priv_enc\n");
     spawn_delay_thread();
     async_pause_job();
     return RSA_meth_get_priv_enc(RSA_PKCS1_OpenSSL())
@@ -274,7 +293,7 @@ static int async_rsa_priv_enc(int flen, const unsigned char *from,
 static int async_rsa_priv_dec(int flen, const unsigned char *from,
                       unsigned char *to, RSA *rsa, int padding)
 {
-    //printf("async_priv_dec\n");
+    printf("async_priv_dec\n");
     spawn_delay_thread();
     async_pause_job();
     return RSA_meth_get_priv_dec(RSA_PKCS1_OpenSSL())
diff --git a/iocore/net/P_UnixNet.h b/iocore/net/P_UnixNet.h
index d369284..caae65d 100644
--- a/iocore/net/P_UnixNet.h
+++ b/iocore/net/P_UnixNet.h
@@ -97,18 +97,21 @@ struct EventIO {
     UnixUDPConnection *uc;
   } data; ///< a kind of continuation
 
-  int start(EventLoop l, DNSConnection *vc, int events);
-  int start(EventLoop l, NetAccept *vc, int events);
-  int start(EventLoop l, NetEvent *ne, int events);
-  int start(EventLoop l, UnixUDPConnection *vc, int events);
-  /** Setup a continuation to be called when a file descriptor is available for read or write.
+  /** The start methods all logically Setup a class to be called
+     when a file descriptor is available for read or write.
+     The type of the classes vary.  Generally the file descriptor
+     is pulled from the class, but there is one option that lets
+     the file descriptor be expressed directly.
      @param l the event loop
-     @param fd file descriptor (or port)
-     @param c the continuation to call
      @param events a mask of flags (for details `man epoll_ctl`)
      @return int the number of events created, -1 is error
    */
-  int start(EventLoop l, int fd, Continuation *c, int events);
+  int start(EventLoop l, DNSConnection *vc, int events);
+  int start(EventLoop l, NetAccept *vc, int events);
+  int start(EventLoop l, NetEvent *ne, int events);
+  int start(EventLoop l, UnixUDPConnection *vc, int events);
+  int start(EventLoop l, int fd, NetEvent *ne, int events);
+  int start_common(EventLoop l, int fd, int events);
 
   /** Alter the events that will trigger the continuation, for level triggered I/O.
      @param events add with positive mask(+EVENTIO_READ), or remove with negative mask (-EVENTIO_READ)
@@ -577,27 +580,33 @@ write_disable(NetHandler *nh, NetEvent *ne)
 TS_INLINE int
 EventIO::start(EventLoop l, DNSConnection *vc, int events)
 {
-  type = EVENTIO_DNS_CONNECTION;
-  return start(l, vc->fd, (Continuation *)vc, events);
+  type        = EVENTIO_DNS_CONNECTION;
+  data.dnscon = vc;
+  return start_common(l, vc->fd, events);
 }
 TS_INLINE int
 EventIO::start(EventLoop l, NetAccept *vc, int events)
 {
-  type = EVENTIO_NETACCEPT;
-  return start(l, vc->server.fd, (Continuation *)vc, events);
+  type    = EVENTIO_NETACCEPT;
+  data.na = vc;
+  return start_common(l, vc->server.fd, events);
 }
 TS_INLINE int
 EventIO::start(EventLoop l, NetEvent *ne, int events)
 {
-  type = EVENTIO_READWRITE_VC;
-  return start(l, ne->get_fd(), (Continuation *)ne, events);
+  type    = EVENTIO_READWRITE_VC;
+  data.ne = ne;
+  return start_common(l, ne->get_fd(), events);
 }
+
 TS_INLINE int
 EventIO::start(EventLoop l, UnixUDPConnection *vc, int events)
 {
-  type = EVENTIO_UDP_CONNECTION;
-  return start(l, vc->fd, (Continuation *)vc, events);
+  type    = EVENTIO_UDP_CONNECTION;
+  data.uc = vc;
+  return start_common(l, vc->fd, events);
 }
+
 TS_INLINE int
 EventIO::close()
 {
@@ -624,13 +633,19 @@ EventIO::close()
 }
 
 TS_INLINE int
-EventIO::start(EventLoop l, int afd, Continuation *c, int e)
+EventIO::start(EventLoop l, int afd, NetEvent *ne, int e)
+{
+  data.ne = ne;
+  return start_common(l, afd, e);
+}
+
+TS_INLINE int
+EventIO::start_common(EventLoop l, int afd, int e)
 {
   if (!this->syscall) {
     return 0;
   }
 
-  data.c     = c;
   fd         = afd;
   event_loop = l;
 #if TS_USE_EPOLL
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index 8e8976c..2f641e9 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -1233,18 +1233,22 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
 #if TS_USE_TLS_ASYNC
   if (ssl_error == SSL_ERROR_WANT_ASYNC) {
     size_t numfds;
-    OSSL_ASYNC_FD waitfd;
+    OSSL_ASYNC_FD *waitfds;
     // Set up the epoll entry for the signalling
-    if (SSL_get_all_async_fds(ssl, &waitfd, &numfds) && numfds > 0) {
-      // Temporarily disable regular net
-      read_disable(nh, this);
-      this->ep.stop(); // Modify used in read_disable doesn't work for edge triggered epol
-      // Have to have the read NetState enabled because we are using it for the signal vc
-      read.enabled = true;
-      write_disable(nh, this);
-      PollDescriptor *pd = get_PollDescriptor(this_ethread());
-      this->ep.start(pd, waitfd, this, EVENTIO_READ);
-      this->ep.type = EVENTIO_READWRITE_VC;
+    if (SSL_get_all_async_fds(ssl, nullptr, &numfds) && numfds > 0) {
+      // Allocate space for the waitfd on the stack, should only be one most all of the time
+      waitfds = reinterpret_cast<OSSL_ASYNC_FD *>(alloca(sizeof(OSSL_ASYNC_FD) * numfds));
+      if (SSL_get_all_async_fds(ssl, waitfds, &numfds) && numfds > 0) {
+        // Temporarily disable regular net
+        this->read.triggered  = false;
+        this->write.triggered = false;
+        this->ep.stop(); // Modify used in read_disable doesn't work for edge triggered epol
+        // Have to have the read NetState enabled because we are using it for the signal vc
+        read.enabled       = true;
+        PollDescriptor *pd = get_PollDescriptor(this_ethread());
+        this->ep.start(pd, waitfds[0], static_cast<NetEvent *>(this), EVENTIO_READ);
+        this->ep.type = EVENTIO_READWRITE_VC;
+      }
     }
   } else if (SSLConfigParams::async_handshake_enabled) {
     // Clean up the epoll entry for signalling
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index db0fea4..9965120 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -921,7 +921,12 @@ SSLPrivateKeyHandler(SSL_CTX *ctx, const SSLConfigParams *params, const std::str
 #ifndef OPENSSL_IS_BORINGSSL
   ENGINE *e = ENGINE_get_default_RSA();
   if (e != nullptr) {
-    const char *argkey = (keyPath == nullptr || keyPath[0] == '\0') ? completeServerCertPath.c_str() : keyPath;
+    ats_scoped_str argkey;
+    if (keyPath == nullptr || keyPath[0] == '\0') {
+      argkey = completeServerCertPath.c_str();
+    } else {
+      argkey = Layout::get()->relative_to(params->serverKeyPathOnly, keyPath);
+    }
     if (!SSL_CTX_use_PrivateKey(ctx, ENGINE_load_private_key(e, argkey, nullptr, nullptr))) {
       SSLError("failed to load server private key from engine");
     }
diff --git a/tests/gold_tests/tls/tls_engine.test.py b/tests/gold_tests/tls/tls_engine.test.py
new file mode 100644
index 0000000..bdfe946
--- /dev/null
+++ b/tests/gold_tests/tls/tls_engine.test.py
@@ -0,0 +1,98 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+
+import os
+
+
+# Someday should add client cert to origin to exercise the
+# engine interface on the other side
+
+Test.Summary = '''
+Test tls via the async interface with the sample async_engine
+'''
+
+Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server")
+
+# Compile with tsxs.  That should bring in the consisten versions of openssl
+ts.Setup.Copy(os.path.join(Test.Variables.AtsTestToolsDir, '../../contrib/openssl', 'async_engine.c'), Test.RunDirectory)
+ts.Setup.RunCommand("tsxs -o async_engine.so async_engine.c")
+
+# Add info the origin server responses
+server.addResponse("sessionlog.json",
+                   {"headers": "GET / HTTP/1.1\r\nuuid: basic\r\n\r\n", "timestamp": "1469733493.993", "body": ""},
+                   {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\nConnection: close\r\nCache-Control: max-age=3600\r\nContent-Length: 2\r\n\r\n", "timestamp": "1469733493.993", "body": "ok"})
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.verify.server':  0,
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.ssl.engine.conf_file': '{0}/ts/config/load_engine.cnf'.format(Test.RunDirectory),
+    'proxy.config.ssl.async.handshake.enabled': 1,
+    'proxy.config.diags.debug.enabled': 0,
+    'proxy.config.diags.debug.tags': 'ssl'
+})
+
+ts.Disk.MakeConfigFile('load_engine.cnf').AddLines([
+    'openssl_conf = openssl_init',
+    '',
+    '[openssl_init]',
+    '',
+    'engines = engine_section',
+    '',
+    '[engine_section]',
+    '',
+    'async = async_section',
+    '',
+    '[async_section]',
+    '',
+    'dynamic_path = {0}/async_engine.so'.format(Test.RunDirectory),
+    '',
+    'engine_id = async-test',
+    '',
+    'default_algorithms = RSA',
+    '',
+    'init = 1'])
+
+# Make a basic request.  Hopefully it goes through
+tr = Test.AddTestRun("Run-Test")
+tr.Processes.Default.Command = "curl -k -v -H uuid:basic -H host:example.com  https://127.0.0.1:{0}/".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Streams.All = Testers.ContainsExpression("HTTP/(2|1\.1) 200", "Request succeeds")
+tr.StillRunningAfter = server
+
+ts.Streams.All += Testers.ContainsExpression("Send signal to ", "The Async engine triggers")