Skip to content

Commit 368c88f

Browse files
shinrichzwoop
authored andcommitted
Fix support for openssl async engine (#6910)
(cherry picked from commit c32fc64)
1 parent 137ea61 commit 368c88f

File tree

6 files changed

+185
-44
lines changed

6 files changed

+185
-44
lines changed

contrib/openssl/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ It should be built as follows. It must be build against openssl 1.1 or better f
55

66
gcc -fPIC -shared -g -o async-test.so -I<path to openssl headers> -L<path to openssl library> -lssl -lcrypto -lpthread async_engine.c
77

8-
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.
8+
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.
99
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
1010
RSA operations

contrib/openssl/async_engine.c

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ static int async_rsa_finish(RSA *rsa);
7272

7373
static RSA_METHOD *async_rsa_method = NULL;
7474

75+
EVP_PKEY *async_load_privkey(ENGINE *e, const char *s_key_id, UI_METHOD *ui_method, void *callback_data)
76+
{
77+
printf("Loading key %s\n", s_key_id);
78+
FILE *f = fopen(s_key_id, "r");
79+
EVP_PKEY *key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
80+
fclose(f);
81+
return key;
82+
}
83+
7584
static int bind_async(ENGINE *e)
7685
{
7786
/* Setup RSA_METHOD */
@@ -96,7 +105,8 @@ static int bind_async(ENGINE *e)
96105
|| !ENGINE_set_RSA(e, async_rsa_method)
97106
|| !ENGINE_set_destroy_function(e, async_destroy)
98107
|| !ENGINE_set_init_function(e, engine_async_init)
99-
|| !ENGINE_set_finish_function(e, async_finish)) {
108+
|| !ENGINE_set_finish_function(e, async_finish)
109+
|| !ENGINE_set_load_privkey_function(e, async_load_privkey)) {
100110
fprintf(stderr, "Failed to initialize\n");
101111
return 0;
102112
}
@@ -176,14 +186,17 @@ static void async_pause_job(void) {
176186
OSSL_ASYNC_FD *writefd;
177187
char buf = DUMMY_CHAR;
178188

179-
if ((job = ASYNC_get_current_job()) == NULL)
189+
if ((job = ASYNC_get_current_job()) == NULL) {
190+
printf("No job\n");
180191
return;
192+
}
181193

182194
waitctx = ASYNC_get_wait_ctx(job);
183195

184196
if (ASYNC_WAIT_CTX_get_fd(waitctx, engine_id, &pipefds[0],
185197
(void **)&writefd)) {
186-
pipefds[1] = *writefd;
198+
printf("Existing wait ctx\n");
199+
return;
187200
} else {
188201
writefd = (OSSL_ASYNC_FD *)OPENSSL_malloc(sizeof(*writefd));
189202
if (writefd == NULL)
@@ -194,6 +207,8 @@ static void async_pause_job(void) {
194207
}
195208
*writefd = pipefds[1];
196209

210+
printf("New wait ctx %d %d\n", pipefds[0], pipefds[1]);
211+
197212
if(!ASYNC_WAIT_CTX_set_wait_fd(waitctx, engine_id, pipefds[0],
198213
writefd, wait_cleanup)) {
199214
wait_cleanup(waitctx, engine_id, pipefds[0], writefd);
@@ -213,35 +228,39 @@ void *
213228
delay_method(void *arg)
214229
{
215230
int signal_fd = (intptr_t)arg;
216-
sleep(5);
217-
uint64_t buf = 1;
231+
sleep(2);
232+
char buf = DUMMY_CHAR;
218233
write(signal_fd, &buf, sizeof(buf));
234+
printf("Send signal to %d\n", signal_fd);
235+
return NULL;
219236
}
220237

221238

222239
void
223240
spawn_delay_thread()
224241
{
225242
pthread_t thread_id;
226-
OSSL_ASYNC_FD signal_fd;
227-
OSSL_ASYNC_FD pipefds[2] = {0, 0};
228243
ASYNC_JOB *job;
229-
if ((job = ASYNC_get_current_job()) == NULL)
244+
if ((job = ASYNC_get_current_job()) == NULL) {
245+
printf("Spawn no job\n");
230246
return;
247+
}
231248

232249
ASYNC_WAIT_CTX *waitctx = ASYNC_get_wait_ctx(job);
233250

234251
size_t numfds;
235-
if (ASYNC_WAIT_CTX_get_all_fds(waitctx, &signal_fd, &numfds) && numfds > 0) {
252+
if (ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) && numfds > 0) {
253+
printf("Spawn, wait_ctx exists. Go away, something else is using this job\n");
236254
} else {
255+
OSSL_ASYNC_FD signal_fd;
237256
OSSL_ASYNC_FD pipefds[2] = {0,0};
238257
OSSL_ASYNC_FD *writefd = OPENSSL_malloc(sizeof(*writefd));
239258
pipe(pipefds);
240259
signal_fd = *writefd = pipefds[1];
241260
ASYNC_WAIT_CTX_set_wait_fd(waitctx, engine_id, pipefds[0], writefd, wait_cleanup);
261+
printf("Spawn, create wait_ctx %d %d\n", pipefds[0], pipefds[1]);
262+
pthread_create(&thread_id, NULL, delay_method, (void *)((intptr_t)signal_fd));
242263
}
243-
244-
pthread_create(&thread_id, NULL, delay_method, (void *)((intptr_t)signal_fd));
245264
}
246265

247266

@@ -264,7 +283,7 @@ static int async_pub_dec(int flen, const unsigned char *from,
264283
static int async_rsa_priv_enc(int flen, const unsigned char *from,
265284
unsigned char *to, RSA *rsa, int padding)
266285
{
267-
//printf("async_priv_enc\n");
286+
printf("async_priv_enc\n");
268287
spawn_delay_thread();
269288
async_pause_job();
270289
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,
274293
static int async_rsa_priv_dec(int flen, const unsigned char *from,
275294
unsigned char *to, RSA *rsa, int padding)
276295
{
277-
//printf("async_priv_dec\n");
296+
printf("async_priv_dec\n");
278297
spawn_delay_thread();
279298
async_pause_job();
280299
return RSA_meth_get_priv_dec(RSA_PKCS1_OpenSSL())

iocore/net/P_UnixNet.h

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,21 @@ struct EventIO {
9797
UnixUDPConnection *uc;
9898
} data; ///< a kind of continuation
9999

100-
int start(EventLoop l, DNSConnection *vc, int events);
101-
int start(EventLoop l, NetAccept *vc, int events);
102-
int start(EventLoop l, NetEvent *ne, int events);
103-
int start(EventLoop l, UnixUDPConnection *vc, int events);
104-
/** Setup a continuation to be called when a file descriptor is available for read or write.
100+
/** The start methods all logically Setup a class to be called
101+
when a file descriptor is available for read or write.
102+
The type of the classes vary. Generally the file descriptor
103+
is pulled from the class, but there is one option that lets
104+
the file descriptor be expressed directly.
105105
@param l the event loop
106-
@param fd file descriptor (or port)
107-
@param c the continuation to call
108106
@param events a mask of flags (for details `man epoll_ctl`)
109107
@return int the number of events created, -1 is error
110108
*/
111-
int start(EventLoop l, int fd, Continuation *c, int events);
109+
int start(EventLoop l, DNSConnection *vc, int events);
110+
int start(EventLoop l, NetAccept *vc, int events);
111+
int start(EventLoop l, NetEvent *ne, int events);
112+
int start(EventLoop l, UnixUDPConnection *vc, int events);
113+
int start(EventLoop l, int fd, NetEvent *ne, int events);
114+
int start_common(EventLoop l, int fd, int events);
112115

113116
/** Alter the events that will trigger the continuation, for level triggered I/O.
114117
@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)
577580
TS_INLINE int
578581
EventIO::start(EventLoop l, DNSConnection *vc, int events)
579582
{
580-
type = EVENTIO_DNS_CONNECTION;
581-
return start(l, vc->fd, (Continuation *)vc, events);
583+
type = EVENTIO_DNS_CONNECTION;
584+
data.dnscon = vc;
585+
return start_common(l, vc->fd, events);
582586
}
583587
TS_INLINE int
584588
EventIO::start(EventLoop l, NetAccept *vc, int events)
585589
{
586-
type = EVENTIO_NETACCEPT;
587-
return start(l, vc->server.fd, (Continuation *)vc, events);
590+
type = EVENTIO_NETACCEPT;
591+
data.na = vc;
592+
return start_common(l, vc->server.fd, events);
588593
}
589594
TS_INLINE int
590595
EventIO::start(EventLoop l, NetEvent *ne, int events)
591596
{
592-
type = EVENTIO_READWRITE_VC;
593-
return start(l, ne->get_fd(), (Continuation *)ne, events);
597+
type = EVENTIO_READWRITE_VC;
598+
data.ne = ne;
599+
return start_common(l, ne->get_fd(), events);
594600
}
601+
595602
TS_INLINE int
596603
EventIO::start(EventLoop l, UnixUDPConnection *vc, int events)
597604
{
598-
type = EVENTIO_UDP_CONNECTION;
599-
return start(l, vc->fd, (Continuation *)vc, events);
605+
type = EVENTIO_UDP_CONNECTION;
606+
data.uc = vc;
607+
return start_common(l, vc->fd, events);
600608
}
609+
601610
TS_INLINE int
602611
EventIO::close()
603612
{
@@ -624,13 +633,19 @@ EventIO::close()
624633
}
625634

626635
TS_INLINE int
627-
EventIO::start(EventLoop l, int afd, Continuation *c, int e)
636+
EventIO::start(EventLoop l, int afd, NetEvent *ne, int e)
637+
{
638+
data.ne = ne;
639+
return start_common(l, afd, e);
640+
}
641+
642+
TS_INLINE int
643+
EventIO::start_common(EventLoop l, int afd, int e)
628644
{
629645
if (!this->syscall) {
630646
return 0;
631647
}
632648

633-
data.c = c;
634649
fd = afd;
635650
event_loop = l;
636651
#if TS_USE_EPOLL

iocore/net/SSLNetVConnection.cc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,18 +1233,22 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
12331233
#if TS_USE_TLS_ASYNC
12341234
if (ssl_error == SSL_ERROR_WANT_ASYNC) {
12351235
size_t numfds;
1236-
OSSL_ASYNC_FD waitfd;
1236+
OSSL_ASYNC_FD *waitfds;
12371237
// Set up the epoll entry for the signalling
1238-
if (SSL_get_all_async_fds(ssl, &waitfd, &numfds) && numfds > 0) {
1239-
// Temporarily disable regular net
1240-
read_disable(nh, this);
1241-
this->ep.stop(); // Modify used in read_disable doesn't work for edge triggered epol
1242-
// Have to have the read NetState enabled because we are using it for the signal vc
1243-
read.enabled = true;
1244-
write_disable(nh, this);
1245-
PollDescriptor *pd = get_PollDescriptor(this_ethread());
1246-
this->ep.start(pd, waitfd, this, EVENTIO_READ);
1247-
this->ep.type = EVENTIO_READWRITE_VC;
1238+
if (SSL_get_all_async_fds(ssl, nullptr, &numfds) && numfds > 0) {
1239+
// Allocate space for the waitfd on the stack, should only be one most all of the time
1240+
waitfds = reinterpret_cast<OSSL_ASYNC_FD *>(alloca(sizeof(OSSL_ASYNC_FD) * numfds));
1241+
if (SSL_get_all_async_fds(ssl, waitfds, &numfds) && numfds > 0) {
1242+
// Temporarily disable regular net
1243+
this->read.triggered = false;
1244+
this->write.triggered = false;
1245+
this->ep.stop(); // Modify used in read_disable doesn't work for edge triggered epol
1246+
// Have to have the read NetState enabled because we are using it for the signal vc
1247+
read.enabled = true;
1248+
PollDescriptor *pd = get_PollDescriptor(this_ethread());
1249+
this->ep.start(pd, waitfds[0], static_cast<NetEvent *>(this), EVENTIO_READ);
1250+
this->ep.type = EVENTIO_READWRITE_VC;
1251+
}
12481252
}
12491253
} else if (SSLConfigParams::async_handshake_enabled) {
12501254
// Clean up the epoll entry for signalling

iocore/net/SSLUtils.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,12 @@ SSLPrivateKeyHandler(SSL_CTX *ctx, const SSLConfigParams *params, const std::str
921921
#ifndef OPENSSL_IS_BORINGSSL
922922
ENGINE *e = ENGINE_get_default_RSA();
923923
if (e != nullptr) {
924-
const char *argkey = (keyPath == nullptr || keyPath[0] == '\0') ? completeServerCertPath.c_str() : keyPath;
924+
ats_scoped_str argkey;
925+
if (keyPath == nullptr || keyPath[0] == '\0') {
926+
argkey = completeServerCertPath.c_str();
927+
} else {
928+
argkey = Layout::get()->relative_to(params->serverKeyPathOnly, keyPath);
929+
}
925930
if (!SSL_CTX_use_PrivateKey(ctx, ENGINE_load_private_key(e, argkey, nullptr, nullptr))) {
926931
SSLError("failed to load server private key from engine");
927932
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'''
2+
'''
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
20+
import os
21+
22+
23+
# Someday should add client cert to origin to exercise the
24+
# engine interface on the other side
25+
26+
Test.Summary = '''
27+
Test tls via the async interface with the sample async_engine
28+
'''
29+
30+
Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
31+
32+
# Define default ATS
33+
ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
34+
server = Test.MakeOriginServer("server")
35+
36+
# Compile with tsxs. That should bring in the consisten versions of openssl
37+
ts.Setup.Copy(os.path.join(Test.Variables.AtsTestToolsDir, '../../contrib/openssl', 'async_engine.c'), Test.RunDirectory)
38+
ts.Setup.RunCommand("tsxs -o async_engine.so async_engine.c")
39+
40+
# Add info the origin server responses
41+
server.addResponse("sessionlog.json",
42+
{"headers": "GET / HTTP/1.1\r\nuuid: basic\r\n\r\n", "timestamp": "1469733493.993", "body": ""},
43+
{"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"})
44+
45+
# add ssl materials like key, certificates for the server
46+
ts.addSSLfile("ssl/server.pem")
47+
ts.addSSLfile("ssl/server.key")
48+
49+
ts.Disk.remap_config.AddLine(
50+
'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
51+
)
52+
53+
ts.Disk.ssl_multicert_config.AddLine(
54+
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
55+
)
56+
ts.Disk.records_config.update({
57+
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
58+
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
59+
'proxy.config.ssl.client.verify.server': 0,
60+
'proxy.config.exec_thread.autoconfig.scale': 1.0,
61+
'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',
62+
'proxy.config.ssl.engine.conf_file': '{0}/ts/config/load_engine.cnf'.format(Test.RunDirectory),
63+
'proxy.config.ssl.async.handshake.enabled': 1,
64+
'proxy.config.diags.debug.enabled': 0,
65+
'proxy.config.diags.debug.tags': 'ssl'
66+
})
67+
68+
ts.Disk.MakeConfigFile('load_engine.cnf').AddLines([
69+
'openssl_conf = openssl_init',
70+
'',
71+
'[openssl_init]',
72+
'',
73+
'engines = engine_section',
74+
'',
75+
'[engine_section]',
76+
'',
77+
'async = async_section',
78+
'',
79+
'[async_section]',
80+
'',
81+
'dynamic_path = {0}/async_engine.so'.format(Test.RunDirectory),
82+
'',
83+
'engine_id = async-test',
84+
'',
85+
'default_algorithms = RSA',
86+
'',
87+
'init = 1'])
88+
89+
# Make a basic request. Hopefully it goes through
90+
tr = Test.AddTestRun("Run-Test")
91+
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)
92+
tr.ReturnCode = 0
93+
tr.Processes.Default.StartBefore(server)
94+
tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
95+
tr.Processes.Default.Streams.All = Testers.ContainsExpression("HTTP/(2|1\.1) 200", "Request succeeds")
96+
tr.StillRunningAfter = server
97+
98+
ts.Streams.All += Testers.ContainsExpression("Send signal to ", "The Async engine triggers")

0 commit comments

Comments
 (0)