This is an automated email from the ASF dual-hosted git repository.
maskit 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 4cf91f8acd Use FetchSM for OCSP HTTP requests (#9591)
4cf91f8acd is described below
commit 4cf91f8acd7290e960edefc6b5f87d8a50d2271f
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Tue Apr 18 08:04:17 2023 +0900
Use FetchSM for OCSP HTTP requests (#9591)
* Use FetchSM for OCSP HTTP requests
* Don't use Hdrheap
* Check OCSP availability by a function used by ATS
* Fix a link issue
* Fix a couple of more link issues
* Fix a null pointer dereference
* Add error handling
* Rename a variable
* Add anonymous namespace
* Print more error log
* Fix memory leak
* Use ats_malloc
---
build/crypto.m4 | 2 +-
iocore/cache/test/stub.cc | 37 ++++++
iocore/net/Makefile.am | 2 +
iocore/net/OCSPStapling.cc | 258 ++++++++++++++++++++++++++++++---------
iocore/net/libinknet_stub.cc | 39 ++++++
src/traffic_quic/traffic_quic.cc | 37 ++++++
src/traffic_server/FetchSM.cc | 18 ++-
7 files changed, 333 insertions(+), 60 deletions(-)
diff --git a/build/crypto.m4 b/build/crypto.m4
index ce91ea60e8..87666d07fd 100644
--- a/build/crypto.m4
+++ b/build/crypto.m4
@@ -282,7 +282,7 @@ AC_DEFUN([TS_CHECK_CRYPTO_OCSP], [
AC_CHECK_HEADERS(openssl/ocsp.h, [ocsp_have_headers=1], [enable_tls_ocsp=no])
if test "$ocsp_have_headers" == "1"; then
- AC_CHECK_FUNCS(OCSP_sendreq_new OCSP_REQ_CTX_add1_header
OCSP_REQ_CTX_set1_req, [enable_tls_ocsp=yes], [enable_tls_ocsp=no])
+ AC_CHECK_FUNCS(OCSP_response_status, [enable_tls_ocsp=yes],
[enable_tls_ocsp=no])
LIBS=$_ocsp_saved_LIBS
fi
diff --git a/iocore/cache/test/stub.cc b/iocore/cache/test/stub.cc
index cad02ab6dc..d315b2a02a 100644
--- a/iocore/cache/test/stub.cc
+++ b/iocore/cache/test/stub.cc
@@ -284,3 +284,40 @@ INKContInternal::free()
INKVConnInternal::INKVConnInternal() : INKContInternal() {}
INKVConnInternal::INKVConnInternal(TSEventFunc funcp, TSMutex mutexp) :
INKContInternal(funcp, mutexp) {}
+
+#include "../src/traffic_server/FetchSM.h"
+ClassAllocator<FetchSM> FetchSMAllocator("unusedFetchSMAllocator");
+void
+FetchSM::ext_launch()
+{
+}
+void
+FetchSM::ext_destroy()
+{
+}
+ssize_t
+FetchSM::ext_read_data(char *, unsigned long)
+{
+ return 0;
+}
+void
+FetchSM::ext_add_header(char const *, int, char const *, int)
+{
+}
+void
+FetchSM::ext_write_data(void const *, unsigned long)
+{
+}
+void *
+FetchSM::ext_get_user_data()
+{
+ return nullptr;
+}
+void
+FetchSM::ext_set_user_data(void *)
+{
+}
+void
+FetchSM::ext_init(Continuation *, char const *, char const *, char const *,
sockaddr const *, int)
+{
+}
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 2e9e800f1d..f393c525aa 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -30,6 +30,8 @@ AM_CPPFLAGS += \
-I$(abs_top_srcdir)/mgmt \
-I$(abs_top_srcdir)/mgmt/utils \
-I$(abs_top_srcdir)/proxy/http \
+ -I$(abs_top_srcdir)/proxy/http/remap \
+ -I$(abs_top_srcdir)/src/traffic_server \
$(TS_INCLUDES) \
@OPENSSL_INCLUDES@ \
@BORINGOCSP_INCLUDES@ \
diff --git a/iocore/net/OCSPStapling.cc b/iocore/net/OCSPStapling.cc
index 58df7c77b6..f0d5e73c37 100644
--- a/iocore/net/OCSPStapling.cc
+++ b/iocore/net/OCSPStapling.cc
@@ -32,16 +32,23 @@
#include <openssl/ocsp.h>
#endif
+#include "tscore/ink_memory.h"
#include "P_Net.h"
#include "P_SSLConfig.h"
#include "P_SSLUtils.h"
#include "SSLStats.h"
+#include "FetchSM.h"
// Maximum OCSP stapling response size.
// This should be the response for a single certificate and will typically
include the responder certificate chain,
// so 10K should be more than enough.
#define MAX_STAPLING_DER 10240
+extern ClassAllocator<FetchSM> FetchSMAllocator;
+
+namespace
+{
+
// Cached info stored in SSL_CTX ex_info
struct certinfo {
unsigned char idx[20]; // Index in session cache SHA1 hash of certificate
@@ -57,6 +64,142 @@ struct certinfo {
time_t expire_time;
};
+class HTTPRequest : public Continuation
+{
+public:
+ static constexpr int MAX_RESP_LEN = 100 * 1024;
+
+ HTTPRequest()
+ {
+ mutex = new_ProxyMutex();
+ SET_HANDLER(&HTTPRequest::event_handler);
+ }
+
+ ~HTTPRequest()
+ {
+ this->_fsm->ext_destroy();
+ OPENSSL_free(this->_req_body);
+ }
+
+ int
+ event_handler(int event, Event *e)
+ {
+ if (event == TS_EVENT_IMMEDIATE) {
+ this->fetch();
+ } else {
+ auto fsm = reinterpret_cast<FetchSM *>(e);
+ auto req = reinterpret_cast<HTTPRequest *>(fsm->ext_get_user_data());
+ if (event == TS_FETCH_EVENT_EXT_BODY_DONE) {
+ req->set_done();
+ } else if (event == TS_EVENT_ERROR) {
+ req->set_error();
+ }
+ }
+
+ return 0;
+ }
+
+ void
+ set_request_line(bool use_post, const char *uri)
+ {
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+ sin.sin_port = 65535;
+
+ this->_fsm = FetchSMAllocator.alloc();
+ this->_fsm->ext_set_user_data(this);
+ if (use_post) {
+ this->_fsm->ext_init(this, "POST", uri, "HTTP/1.1",
reinterpret_cast<sockaddr *>(&sin), 0);
+ } else {
+ this->_fsm->ext_init(this, "GET", uri, "HTTP/1.1",
reinterpret_cast<sockaddr *>(&sin), 0);
+ }
+ }
+
+ int
+ set_body(const char *content_type, const ASN1_ITEM *it, const ASN1_VALUE
*req)
+ {
+ this->_req_body = nullptr;
+
+ if (req != nullptr) {
+ // const_cast is needed for OpenSSL 1.1.1
+ this->_req_body_len = ASN1_item_i2d(const_cast<ASN1_VALUE *>(req),
&this->_req_body, it);
+ if (this->_req_body_len == -1) {
+ return 0;
+ }
+ }
+ this->add_header("Content-Type", content_type);
+ char req_body_len_str[10];
+ int req_body_len_str_len;
+ req_body_len_str_len = ink_fast_itoa(this->_req_body_len,
req_body_len_str, sizeof(req_body_len_str));
+ this->add_header("Content-Length", 14, req_body_len_str,
req_body_len_str_len);
+
+ return 1;
+ }
+
+ void
+ add_header(const char *name, int name_len, const char *value, int value_len)
+ {
+ this->_fsm->ext_add_header(name, name_len, value, value_len);
+ }
+
+ void
+ add_header(const char *name, const char *value)
+ {
+ this->add_header(name, strlen(name), value, strlen(value));
+ }
+
+ void
+ fetch()
+ {
+ SCOPED_MUTEX_LOCK(lock, mutex, this_ethread());
+ this->_fsm->ext_launch();
+ this->_fsm->ext_write_data(this->_req_body, this->_req_body_len);
+ }
+
+ void
+ set_done()
+ {
+ this->_result = 1;
+ }
+
+ void
+ set_error()
+ {
+ this->_result = -1;
+ }
+
+ bool
+ is_done()
+ {
+ return this->_result != 0;
+ }
+
+ bool
+ is_success()
+ {
+ return this->_result == 1;
+ }
+
+ unsigned char *
+ get_response_body(int *len)
+ {
+ SCOPED_MUTEX_LOCK(lock, mutex, this_ethread());
+ char *buf = static_cast<char *>(ats_malloc(MAX_RESP_LEN));
+ *len = this->_fsm->ext_read_data(buf, MAX_RESP_LEN);
+ return reinterpret_cast<unsigned char *>(buf);
+ }
+
+private:
+ FetchSM *_fsm = nullptr;
+ unsigned char *_req_body = nullptr;
+ int _req_body_len = 0;
+ int _result = 0;
+};
+
+} // End of namespace
+
/*
* In the case of multiple certificates associated with a SSL_CTX, we must
store a map
* of cached responses
@@ -373,84 +516,86 @@ stapling_check_response(certinfo *cinf, OCSP_RESPONSE
*rsp)
}
static OCSP_RESPONSE *
-query_responder(BIO *b, const char *host, const char *path, const char
*user_agent, OCSP_REQUEST *req, int req_timeout)
+query_responder(const char *uri, const char *user_agent, OCSP_REQUEST *req,
int req_timeout)
{
ink_hrtime start, end;
OCSP_RESPONSE *resp = nullptr;
- OCSP_REQ_CTX *ctx;
- int rv;
start = Thread::get_hrtime();
end = ink_hrtime_add(start, ink_hrtime_from_sec(req_timeout));
- ctx = OCSP_sendreq_new(b, path, nullptr, -1);
- OCSP_REQ_CTX_add1_header(ctx, "Host", host);
- if (user_agent != nullptr) {
- OCSP_REQ_CTX_add1_header(ctx, "User-Agent", user_agent);
- }
- OCSP_REQ_CTX_set1_req(ctx, req);
+ HTTPRequest httpreq;
+ bool use_post = true;
- do {
- rv = OCSP_sendreq_nbio(&resp, ctx);
- ink_hrtime_sleep(HRTIME_MSECONDS(1));
- } while ((rv == -1) && BIO_should_retry(b) && (Thread::get_hrtime() < end));
+ httpreq.set_request_line(use_post, uri);
- OCSP_REQ_CTX_free(ctx);
+ // Host header
+ const char *host = strstr(uri, "://") + 3;
+ const char *slash = strchr(host, '/');
+ if (slash == nullptr) {
+ slash = host + strlen(host);
+ }
+ int host_len = slash - host;
+ httpreq.add_header("Host", 4, host, host_len);
- if (rv == 1) {
- return resp;
+ // User-Agent header
+ if (user_agent != nullptr) {
+ httpreq.add_header("User-Agent", user_agent);
}
- Error("failed to connect to OCSP server; host=%s path=%s", host, path);
+ // Content-Type, Content-Length, Request Body
+ if (use_post) {
+ if (httpreq.set_body("application/ocsp-request",
ASN1_ITEM_rptr(OCSP_REQUEST), (const ASN1_VALUE *)req) != 1) {
+ Error("failed to make a request for OCSP server; uri=%s", uri);
+ return nullptr;
+ }
+ }
- return nullptr;
-}
+ // Send request
+ eventProcessor.schedule_imm(&httpreq, ET_NET);
-static OCSP_RESPONSE *
-process_responder(OCSP_REQUEST *req, const char *host, const char *path, const
char *port, const char *user_agent, int req_timeout)
-{
- BIO *cbio = nullptr;
- OCSP_RESPONSE *resp = nullptr;
- cbio = BIO_new_connect(host);
- if (!cbio) {
- goto end;
- }
- if (port) {
- BIO_set_conn_port(cbio, port);
- }
+ // Wait until the request completes
+ do {
+ ink_hrtime_sleep(HRTIME_MSECONDS(1));
+ } while (!httpreq.is_done() && (Thread::get_hrtime() < end));
+
+ if (httpreq.is_success()) {
+ // Parse the response
+ int len;
+ unsigned char *res = httpreq.get_response_body(&len);
+ const unsigned char *p = res;
+ resp = reinterpret_cast<OCSP_RESPONSE
*>(ASN1_item_d2i(nullptr, &p, len, ASN1_ITEM_rptr(OCSP_RESPONSE)));
+
+ if (resp) {
+ ats_free(res);
+ return resp;
+ }
- BIO_set_nbio(cbio, 1);
- if (BIO_do_connect(cbio) <= 0 && !BIO_should_retry(cbio)) {
- Debug("ssl_ocsp", "process_responder: failed to connect to OCSP server;
host=%s port=%s path=%s", host, port, path);
- goto end;
+ if (len < 5) {
+ Error("failed to parse a response from OCSP server; uri=%s len=%d", uri,
len);
+ } else {
+ Error("failed to parse a response from OCSP server; uri=%s len=%d
data=%02x%02x%02x%02x%02x...", uri, len, res[0], res[1],
+ res[2], res[3], res[4]);
+ }
+ ats_free(res);
+ return nullptr;
}
- resp = query_responder(cbio, host, path, user_agent, req, req_timeout);
-end:
- if (cbio) {
- BIO_free_all(cbio);
- }
- return resp;
+ Error("failed to get a response from OCSP server; uri=%s", uri);
+ return nullptr;
}
static bool
stapling_refresh_response(certinfo *cinf, OCSP_RESPONSE **prsp)
{
- bool rv = true;
- OCSP_REQUEST *req = nullptr;
- OCSP_CERTID *id = nullptr;
- char *host = nullptr, *port = nullptr, *path = nullptr;
- int ssl_flag = 0;
+ bool rv = true;
+ OCSP_REQUEST *req = nullptr;
+ OCSP_CERTID *id = nullptr;
int response_status = 0;
*prsp = nullptr;
- if (!OCSP_parse_url(cinf->uri, &host, &port, &path, &ssl_flag)) {
- Debug("ssl_ocsp", "stapling_refresh_response: OCSP_parse_url failed;
uri=%s", cinf->uri);
- goto err;
- }
-
- Debug("ssl_ocsp", "stapling_refresh_response: querying responder; host=%s
port=%s path=%s", host, port, path);
+ Debug("ssl_ocsp", "stapling_refresh_response: querying responder; uri=%s",
cinf->uri);
req = OCSP_REQUEST_new();
if (!req) {
@@ -464,7 +609,7 @@ stapling_refresh_response(certinfo *cinf, OCSP_RESPONSE
**prsp)
goto err;
}
- *prsp = process_responder(req, host, path, port, cinf->user_agent,
SSLConfigParams::ssl_ocsp_request_timeout);
+ *prsp = query_responder(cinf->uri, cinf->user_agent, req,
SSLConfigParams::ssl_ocsp_request_timeout);
if (*prsp == nullptr) {
goto done;
}
@@ -474,8 +619,7 @@ stapling_refresh_response(certinfo *cinf, OCSP_RESPONSE
**prsp)
Debug("ssl_ocsp", "stapling_refresh_response: query response received");
stapling_check_response(cinf, *prsp);
} else {
- Error("stapling_refresh_response: responder response error; host=%s
port=%s path=%s response_status=%d", host, port, path,
- response_status);
+ Error("stapling_refresh_response: responder response error; uri=%s
response_status=%d", cinf->uri, response_status);
}
if (!stapling_cache_response(*prsp, cinf)) {
@@ -496,9 +640,6 @@ done:
if (*prsp) {
OCSP_RESPONSE_free(*prsp);
}
- OPENSSL_free(host);
- OPENSSL_free(path);
- OPENSSL_free(port);
return rv;
}
@@ -611,6 +752,7 @@ ssl_callback_ocsp_stapling(SSL *ssl, void *)
ink_mutex_release(&cinf->stapling_mutex);
SSL_set_tlsext_status_ocsp_resp(ssl, p, cinf->resp_derlen);
Debug("ssl_ocsp", "ssl_callback_ocsp_stapling: successfully got
certificate status for %s", cinf->certname);
+ Debug("ssl_ocsp", "is_prefetched:%d uri:%s", cinf->is_prefetched,
cinf->uri);
return SSL_TLSEXT_ERR_OK;
}
}
diff --git a/iocore/net/libinknet_stub.cc b/iocore/net/libinknet_stub.cc
index b7a28788dc..d08ae17015 100644
--- a/iocore/net/libinknet_stub.cc
+++ b/iocore/net/libinknet_stub.cc
@@ -155,3 +155,42 @@ PreWarmManager::reconfigure()
}
PreWarmManager prewarmManager;
+
+#include "../src/traffic_server/FetchSM.h"
+ClassAllocator<FetchSM> FetchSMAllocator("unusedFetchSMAllocator");
+void
+FetchSM::ext_launch()
+{
+}
+void
+FetchSM::ext_destroy()
+{
+}
+ssize_t
+FetchSM::ext_read_data(char *, unsigned long)
+{
+ return 0;
+}
+void
+FetchSM::ext_add_header(char const *, int, char const *, int)
+{
+}
+void
+FetchSM::ext_write_data(void const *, unsigned long)
+{
+}
+void *
+FetchSM::ext_get_user_data()
+{
+ return nullptr;
+}
+void
+FetchSM::ext_set_user_data(void *)
+{
+}
+void
+FetchSM::ext_init(Continuation *, char const *, char const *, char const *,
sockaddr const *, int)
+{
+}
+
+ChunkedHandler::ChunkedHandler() {}
diff --git a/src/traffic_quic/traffic_quic.cc b/src/traffic_quic/traffic_quic.cc
index 5e1212b340..7bd59e1229 100644
--- a/src/traffic_quic/traffic_quic.cc
+++ b/src/traffic_quic/traffic_quic.cc
@@ -347,3 +347,40 @@ PreWarmManager::reconfigure()
}
PreWarmManager prewarmManager;
+
+#include "../src/traffic_server/FetchSM.h"
+ClassAllocator<FetchSM> FetchSMAllocator("unusedFetchSMAllocator");
+void
+FetchSM::ext_launch()
+{
+}
+void
+FetchSM::ext_destroy()
+{
+}
+ssize_t
+FetchSM::ext_read_data(char *, unsigned long)
+{
+ return 0;
+}
+void
+FetchSM::ext_add_header(char const *, int, char const *, int)
+{
+}
+void
+FetchSM::ext_write_data(void const *, unsigned long)
+{
+}
+void *
+FetchSM::ext_get_user_data()
+{
+ return nullptr;
+}
+void
+FetchSM::ext_set_user_data(void *)
+{
+}
+void
+FetchSM::ext_init(Continuation *, char const *, char const *, char const *,
sockaddr const *, int)
+{
+}
diff --git a/src/traffic_server/FetchSM.cc b/src/traffic_server/FetchSM.cc
index 38af0d0dd0..38a053a391 100644
--- a/src/traffic_server/FetchSM.cc
+++ b/src/traffic_server/FetchSM.cc
@@ -53,7 +53,9 @@ FetchSM::cleanUp()
client_response_hdr.destroy();
ats_free(client_response);
cont_mutex.clear();
- http_vc->do_io_close();
+ if (http_vc) {
+ http_vc->do_io_close();
+ }
FetchSMAllocator.free(this);
}
@@ -66,6 +68,17 @@ FetchSM::httpConnect()
Debug(DEBUG_TAG, "[%s] calling httpconnect write pi=%p tag=%s id=%" PRId64,
__FUNCTION__, pi, tag, id);
http_vc = reinterpret_cast<PluginVC *>(TSHttpConnectWithPluginId(&_addr.sa,
tag, id));
+ if (http_vc == nullptr) {
+ Debug(DEBUG_TAG, "Not ready to use");
+ if (contp) {
+ if (fetch_flags & TS_FETCH_FLAGS_STREAM) {
+ return InvokePluginExt(TS_EVENT_ERROR);
+ }
+ InvokePlugin(callback_events.failure_event_id, nullptr);
+ }
+ cleanUp();
+ return;
+ }
/*
* TS-2906: We need a way to unset internal request when using FetchSM, the
use case for this
@@ -649,6 +662,9 @@ FetchSM::ext_launch()
void
FetchSM::ext_write_data(const void *data, size_t len)
{
+ if (write_vio == nullptr) {
+ return;
+ }
if (fetch_flags & TS_FETCH_FLAGS_NEWLOCK) {
MUTEX_TAKE_LOCK(mutex, this_ethread());
}