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 2019/06/11 19:13:12 UTC

[trafficserver] branch master updated: Added cert_reporting_tool plugin based off example/client_context_dump

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 7f933d2  Added cert_reporting_tool plugin based off example/client_context_dump
7f933d2 is described below

commit 7f933d232cf869b1ec7f9533484e13be0e8ae193
Author: dyrock <ze...@gmail.com>
AuthorDate: Fri Jun 7 15:27:14 2019 -0500

    Added cert_reporting_tool plugin based off example/client_context_dump
---
 doc/admin-guide/plugins/cert_reporting_tool.en.rst |  44 +++++
 doc/admin-guide/plugins/index.en.rst               |   4 +
 plugins/Makefile.am                                |   1 +
 .../experimental/cert_reporting_tool/Makefile.inc  |  19 +++
 plugins/experimental/cert_reporting_tool/README    |  15 ++
 .../cert_reporting_tool/cert_reporting_tool.cc     | 190 +++++++++++++++++++++
 6 files changed, 273 insertions(+)

diff --git a/doc/admin-guide/plugins/cert_reporting_tool.en.rst b/doc/admin-guide/plugins/cert_reporting_tool.en.rst
new file mode 100644
index 0000000..ecce028
--- /dev/null
+++ b/doc/admin-guide/plugins/cert_reporting_tool.en.rst
@@ -0,0 +1,44 @@
+.. _admin-plugins-cert-reporting-tool:
+
+Cert Reporting Tool Plugin
+**************************
+
+.. 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.
+
+Description
+===========
+
+The ``cert reporting tool`` can examine and log loaded certificates information like SAN and expiration date.
+User will send plugin message to trigger the reporting/logging.
+
+The log format for ``cert_reporting_tool.log`` is as followed:
+[time] [Lookup Name] [Subject] [SAN] [serial] [NotAfter]
+
+Plugin Configuration
+====================
+.. program:: cert_reporting_tool.so
+
+* Simply put the name `cert_reporting_tool.so` in plugin.config
+
+* ``traffic_ctl`` command.
+   ``traffic_ctl plugin msg cert_reporting_tool.client 1`` - Triggers reporting/logging for client certs.
+
+Example Usage
+=============
+Start traffic server with cert_reporting_tool loaded, then use traffic_ctl to send plugin message:
+``traffic_ctl plugin msg cert_reporting_tool.client 1``
diff --git a/doc/admin-guide/plugins/index.en.rst b/doc/admin-guide/plugins/index.en.rst
index da38b3c..3d422dc 100644
--- a/doc/admin-guide/plugins/index.en.rst
+++ b/doc/admin-guide/plugins/index.en.rst
@@ -147,6 +147,7 @@ directory of the |TS| source tree. Experimental plugins can be compiled by passi
    Balancer <balancer.en>
    Buffer Upload <buffer_upload.en>
    Certifier <certifier.en>
+   Cert Reporting Tool <cert_reporting_tool.en>
    Collapsed-Forwarding <collapsed_forwarding.en>
    GeoIP ACL <geoip_acl.en>
    FQ Pacing <fq_pacing.en>
@@ -181,6 +182,9 @@ directory of the |TS| source tree. Experimental plugins can be compiled by passi
 :doc:`Certifier <certifier.en>`
    Manages and/or generates certificates for incoming HTTPS requests.
 
+:doc:`Cert Reporting Tool <cert_reporting_tool.en>`
+   Examines and logs information on loaded certificates.
+
 :doc:`Collapsed-Forwarding <collapsed_forwarding.en>`
    Allows to Collapse multiple Concurrent requests by downloading once from the Origin and serving
    all clients in parallel.
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index c00a4fb..b5e7d17 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -57,6 +57,7 @@ include experimental/balancer/Makefile.inc
 include experimental/buffer_upload/Makefile.inc
 include experimental/cache_range_requests/Makefile.inc
 include experimental/certifier/Makefile.inc
+include experimental/cert_reporting_tool/Makefile.inc
 include experimental/collapsed_forwarding/Makefile.inc
 include experimental/cookie_remap/Makefile.inc
 include experimental/custom_redirect/Makefile.inc
diff --git a/plugins/experimental/cert_reporting_tool/Makefile.inc b/plugins/experimental/cert_reporting_tool/Makefile.inc
new file mode 100644
index 0000000..73fb186
--- /dev/null
+++ b/plugins/experimental/cert_reporting_tool/Makefile.inc
@@ -0,0 +1,19 @@
+#  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.
+
+pkglib_LTLIBRARIES += experimental/cert_reporting_tool/cert_reporting_tool.la
+
+experimental_cert_reporting_tool_cert_reporting_tool_la_SOURCES = experimental/cert_reporting_tool/cert_reporting_tool.cc
diff --git a/plugins/experimental/cert_reporting_tool/README b/plugins/experimental/cert_reporting_tool/README
new file mode 100644
index 0000000..c9f2788
--- /dev/null
+++ b/plugins/experimental/cert_reporting_tool/README
@@ -0,0 +1,15 @@
+ATS (Apache Traffic Server) Cert Reporting Tool Plugin
+
+General description
+-------------------
+
+This plugin can be used to examine loaded certificates and logs related information onto disk.
+
+The log format is as followed:
+[time] [Lookup Name] [Subject] [SAN] [serial] [NotAfter]
+
+To use the plugin, simply supply the name in plugin.config:
+Example: cert_reporting_tool.so
+
+And then send plugin messages to trigger reports/logs:
+Example: `traffic_ctl plugin msg cert_reporting_tool.client 1` to log all client certificates.
diff --git a/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc b/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc
new file mode 100644
index 0000000..5c94f01
--- /dev/null
+++ b/plugins/experimental/cert_reporting_tool/cert_reporting_tool.cc
@@ -0,0 +1,190 @@
+/** @cert_reporting_tool.cc
+   Cert reporting tool collects and logs TLS certificate/context related information.
+   @section license License
+   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.
+ */
+
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <string_view>
+
+#ifdef OPENSSL_NO_SSL_INTERN
+#undef OPENSSL_NO_SSL_INTERN
+#endif
+
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "ts/ts.h"
+#include "tscpp/util/TextView.h"
+
+#define PLUGIN_NAME "cert_reporting_tool"
+TSTextLogObject cert_reporting_log;
+
+char *
+asn1_string_extract(ASN1_STRING *s)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x010100000
+  return reinterpret_cast<char *>(const_cast<unsigned char *>(ASN1_STRING_get0_data(s)));
+#else
+  return reinterpret_cast<char *>(ASN1_STRING_data(s));
+#endif
+}
+
+// For 1.0.2, needs access to internal structure
+// For 1.1.0 and 1.1.1, use API
+void
+dump_context(const char *ca_path, const char *ck_path)
+{
+  SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(TSSslClientContextFindByName(ca_path, ck_path));
+  if (ctx) {
+    SSL *s = SSL_new(ctx);
+    if (s) {
+      char *data  = nullptr;
+      long length = 0;
+      std::string subject_s, san_s, serial_s, time_s;
+      X509 *cert = SSL_get_certificate(s);
+      if (cert) {
+        // Retrieve state info and write to log object
+        // expiration date, serial number, common name, and subject alternative names
+        const ASN1_TIME *not_after = X509_get_notAfter(cert);
+        const ASN1_INTEGER *serial = X509_get_serialNumber(cert);
+        X509_NAME *subject_name    = X509_get_subject_name(cert);
+
+        // Subject name
+        BIO *subject_bio = BIO_new(BIO_s_mem());
+        X509_NAME_print_ex(subject_bio, subject_name, 0, XN_FLAG_RFC2253);
+        length = BIO_get_mem_data(subject_bio, &data);
+        if (length > 0 && data) {
+          subject_s = std::string(data, length);
+        }
+        data = nullptr;
+        BIO_free(subject_bio);
+
+        // Subject Alternative Name
+        GENERAL_NAMES *names = static_cast<GENERAL_NAMES *>(X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
+        if (names) {
+          unsigned count = sk_GENERAL_NAME_num(names);
+          for (unsigned i = 0; i < count; ++i) {
+            GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
+            data               = nullptr;
+            length             = 0;
+            switch (name->type) {
+            case (GEN_EMAIL): {
+              data   = asn1_string_extract(name->d.rfc822Name);
+              length = ASN1_STRING_length(name->d.rfc822Name);
+              break;
+            }
+            case (GEN_DNS): {
+              data   = asn1_string_extract(name->d.dNSName);
+              length = ASN1_STRING_length(name->d.dNSName);
+              break;
+            }
+            case (GEN_URI): {
+              data   = asn1_string_extract(name->d.uniformResourceIdentifier);
+              length = ASN1_STRING_length(name->d.uniformResourceIdentifier);
+              break;
+            }
+            default:
+              break;
+            }
+            if (data) {
+              san_s.append(data, length);
+              san_s.push_back(',');
+            }
+          }
+          if (san_s.back() == ',') {
+            san_s.pop_back();
+          }
+        }
+
+        // Serial number
+        int64_t sn = 0;
+#if OPENSSL_VERSION_NUMBER >= 0x010100000
+        ASN1_INTEGER_get_int64(&sn, serial);
+#else
+        sn = ASN1_INTEGER_get(serial);
+#endif
+        if (sn != 0 && sn != -1) {
+          serial_s = std::to_string(sn);
+        }
+
+        // Expiration
+        BIO *time_bio = BIO_new(BIO_s_mem());
+        ASN1_TIME_print(time_bio, not_after);
+        length = BIO_get_mem_data(time_bio, &data);
+        time_s = std::string(data, length);
+        BIO_free(time_bio);
+        TSDebug(PLUGIN_NAME, "LookupName: %s:%s, Subject: %s. SAN: %s. Serial: %s. NotAfter: %s.", ca_path, ck_path,
+                subject_s.c_str(), san_s.c_str(), serial_s.c_str(), time_s.c_str());
+        TSTextLogObjectWrite(cert_reporting_log, "LookupName: %s:%s, Subject: %s. SAN: %s. Serial: %s. NotAfter: %s.", ca_path,
+                             ck_path, subject_s.c_str(), san_s.c_str(), serial_s.c_str(), time_s.c_str());
+      }
+    }
+    SSL_free(s);
+  }
+}
+
+// Plugin Message Continuation
+int
+CB_context_dump(TSCont, TSEvent, void *edata)
+{
+  TSPluginMsg *msg = static_cast<TSPluginMsg *>(edata);
+  static constexpr std::string_view PLUGIN_PREFIX("cert_reporting_tool."_sv);
+
+  std::string_view tag(msg->tag, strlen(msg->tag));
+
+  if (tag.substr(0, PLUGIN_PREFIX.size()) == PLUGIN_PREFIX) {
+    tag.remove_prefix(PLUGIN_PREFIX.size());
+    if (tag == "client") {
+      // Grab all keys by API and dump to log file according to arg passed in
+      int count = 0;
+      TSSslClientContextsNamesGet(0, nullptr, &count);
+      if (count > 0) {
+        char const **results = static_cast<char const **>(malloc(sizeof(const char *) * count));
+        TSSslClientContextsNamesGet(count, results, nullptr);
+        for (int i = 0; i < count; i += 2) {
+          dump_context(results[i], results[i + 1]);
+        }
+      }
+    }
+  }
+  TSTextLogObjectFlush(cert_reporting_log);
+  return TS_SUCCESS;
+}
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+  TSPluginRegistrationInfo info;
+
+  info.plugin_name   = PLUGIN_NAME;
+  info.vendor_name   = "Apache Software Foundation";
+  info.support_email = "dev@trafficserver.apache.org";
+
+  if (TSPluginRegister(&info) != TS_SUCCESS) {
+    TSError("[%s] Plugin registration failed", PLUGIN_NAME);
+    return;
+  }
+  if (TSTextLogObjectCreate(PLUGIN_NAME, TS_LOG_MODE_ADD_TIMESTAMP, &cert_reporting_log) != TS_SUCCESS || !cert_reporting_log) {
+    TSError("[%s] Failed to create log file", PLUGIN_NAME);
+    return;
+  }
+  TSDebug(PLUGIN_NAME, "Initialized.");
+  TSLifecycleHookAdd(TS_LIFECYCLE_MSG_HOOK, TSContCreate(CB_context_dump, nullptr));
+}