This is an automated email from the ASF dual-hosted git repository. bcall pushed a commit to branch 8.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit ea340992f25a6b4ec905a8ce192604006a216211 Author: Walter Karas <[email protected]> AuthorDate: Mon Dec 10 11:36:59 2018 -0600 Make sslheaders plugin better conform to documentation. (cherry picked from commit 7f63d1630a11cd324b8ebdd606d896a5b475c279) Conflicts: plugins/experimental/sslheaders/expand.cc --- plugins/experimental/sslheaders/expand.cc | 2 +- plugins/experimental/sslheaders/sslheaders.cc | 66 +++++++++++++--- tests/gold_tests/pluginTest/sslheaders/observer.py | 31 ++++++++ .../pluginTest/sslheaders/ssl/server.key | 28 +++++++ .../pluginTest/sslheaders/ssl/server.pem | 21 +++++ .../pluginTest/sslheaders/sslheaders.gold | 1 + .../pluginTest/sslheaders/sslheaders.test.py | 91 ++++++++++++++++++++++ 7 files changed, 228 insertions(+), 12 deletions(-) diff --git a/plugins/experimental/sslheaders/expand.cc b/plugins/experimental/sslheaders/expand.cc index 15b20d6..d271837 100644 --- a/plugins/experimental/sslheaders/expand.cc +++ b/plugins/experimental/sslheaders/expand.cc @@ -114,7 +114,7 @@ static const x509_expansion expansions[SSL_HEADERS_FIELD_MAX] = { x509_expand_serial, // SSL_HEADERS_FIELD_SERIAL x509_expand_signature, // SSL_HEADERS_FIELD_SIGNATURE x509_expand_notbefore, // SSL_HEADERS_FIELD_NOTBEFORE - x509_expand_notafter, // SSL_HEADERS_FIELD_NOTBEFORE + x509_expand_notafter, // SSL_HEADERS_FIELD_NOTAFTER }; bool diff --git a/plugins/experimental/sslheaders/sslheaders.cc b/plugins/experimental/sslheaders/sslheaders.cc index 6c6838d..657a9cd 100644 --- a/plugins/experimental/sslheaders/sslheaders.cc +++ b/plugins/experimental/sslheaders/sslheaders.cc @@ -116,6 +116,49 @@ SslHdrSetHeader(TSMBuffer mbuf, TSMLoc mhdr, const std::string &name, BIO *value } } +namespace +{ +template <bool IsClient> class WrapX509 +{ +public: + WrapX509(SSL *ssl) : _ssl(ssl), _x509(_nonNullInvalidValue()) {} + + X509 * + get() + { + if (_x509 == _nonNullInvalidValue()) { + _set(); + } + + return _x509; + } + + ~WrapX509() + { + if (IsClient && (_x509 != _nonNullInvalidValue()) && (_x509 != nullptr)) { + X509_free(_x509); + } + } + +private: + SSL *_ssl; + X509 *_x509; + + // The address of this object can not be a valid X509 structure address. + X509 * + _nonNullInvalidValue() const + { + return reinterpret_cast<X509 *>(const_cast<WrapX509 *>(this)); + } + + void + _set() + { + _x509 = (IsClient ? SSL_get_peer_certificate : SSL_get_certificate)(_ssl); + } +}; +} // end anonymous namespace + // Process SSL header expansions. If this is not an SSL connection, then we need to delete the SSL headers // so that malicious clients cannot inject bogus information. Otherwise, we populate the header with the // expanded value. If the value expands to something empty, we nuke the header. @@ -127,22 +170,28 @@ SslHdrExpand(SSL *ssl, const SslHdrInstance::expansion_list &expansions, TSMBuff SslHdrRemoveHeader(mbuf, mhdr, expansion.name); } } else { + WrapX509<true> clientX509(ssl); + WrapX509<false> serverX509(ssl); X509 *x509; + BIO *exp = BIO_new(BIO_s_mem()); for (const auto &expansion : expansions) { switch (expansion.scope) { case SSL_HEADERS_SCOPE_CLIENT: - x509 = SSL_get_peer_certificate(ssl); + x509 = clientX509.get(); + if (x509 == nullptr) { + SslHdrRemoveHeader(mbuf, mhdr, expansion.name); + continue; + } break; case SSL_HEADERS_SCOPE_SERVER: - x509 = SSL_get_certificate(ssl); + x509 = serverX509.get(); + if (x509 == nullptr) { + continue; + } break; default: - x509 = nullptr; - } - - if (x509 == nullptr) { continue; } @@ -152,11 +201,6 @@ SslHdrExpand(SSL *ssl, const SslHdrInstance::expansion_list &expansions, TSMBuff } else { SslHdrRemoveHeader(mbuf, mhdr, expansion.name); } - - // Getting the peer certificate takes a reference count, but the server certificate doesn't. - if (x509 && expansion.scope == SSL_HEADERS_SCOPE_CLIENT) { - X509_free(x509); - } } BIO_free(exp); diff --git a/tests/gold_tests/pluginTest/sslheaders/observer.py b/tests/gold_tests/pluginTest/sslheaders/observer.py new file mode 100644 index 0000000..673a56e --- /dev/null +++ b/tests/gold_tests/pluginTest/sslheaders/observer.py @@ -0,0 +1,31 @@ +''' +Extract ssl-* headers and store in a log file for later verification. +''' +# 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. + +log = open('sslheaders.log', 'w') + +def observe(headers): + + for h in headers.items(): + if h[0].lower().startswith('ssl-'): + + log.write(h[0] + ": " + h[1] + "\n"); + log.write("-\n") + log.flush() + +Hooks.register(Hooks.ReadRequestHook, observe) diff --git a/tests/gold_tests/pluginTest/sslheaders/ssl/server.key b/tests/gold_tests/pluginTest/sslheaders/ssl/server.key new file mode 100644 index 0000000..9cdfc36 --- /dev/null +++ b/tests/gold_tests/pluginTest/sslheaders/ssl/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCZkEXSlZ+ZFKFg +CPpcDG39e73BuK6E5uE38q2PHh4DV0xcsJnIUx51viqLPwYughxfP0crHyBdXoHV +dW/3WX4gpiGrdiM/dvCouheo0DPaqUUJ2nZKVYh2M57oyeiuJidlKb7BGkfw3HWP +9TV7dVyGWok/cowjopqaLHJWxg/kh2KqvUBD0CHt9Kd1XvgXVmHwE7vCv0j5owv2 +MaExTsFb16uWmVLhl1gNHI2RqCX2yLaebH1DvtbLrit1XErjtaSYeJE9clVRaqT6 +vsvLOhyB5tA9WfZqfBYr/MHDeXQfrbIf+4Cp3aTpq5grc5InIMMH0eOk6/f/4tW+ +nq1lfszZAgMBAAECggEAYvYAqRbXRRVwca0Xel5gO2yU+tSDUw5esWlow8RK3yhR +A6KjV9+Iz6P/UsEIwMwEcLUcrgNfHgybau5Fe4dmqq+lHxQA3xNNP869FIMoB4/x +98mbVYgNau8VRztnAWOBG8ZtMZA4MFZCRMVm8+rL96E8tXCiMwzEyPo/rP/ymfhN +3GRunX+GhfIA79AYNbd7HMVL+cvWWUGUF5Bc5i1wXcLy4I7b9NYtv920BeCLzSFK +BypFB7ku/vKgTcBxe4yxThxPeXPwm4WFzGYKk/Afl1j8tVXCE2U4Y3yykfC0Qk6S +ECZbCKLO2Rxi9fclIDZBHWuKejZhdjHfjeNvZ2vLoQKBgQDJzLmkVLvWAxgl1yvF +U7gwqj/TzYqtVowbjEvTNEnPU1j/hIVI343SVV/EvJmif/iRUop6sRYfLsUjpMsH +CmPysNKL3UtgSYOxLs+0xLhG4OOQRpPSf/uvl9YyWY9G3AqiC7ScthkQjEhZa4c1 +eycYy0jr42kX0OL9MuIH9q0ENQKBgQDCzvGKMs8r5E/Qd3YB9VYB60dx+6G83AHZ +YqIelykObhCdxL9n4K+p4VKKLvgTcCOLYYIkBSWRJWR+ue3s3ey9+XWd2/q4Xvfh +TCjAuO2ibMV+y5ClNlW0fQ/doIVWSDbjO2tZW1jh7YWZ4CtuVrsEisv1sk3KltMB +MguhpTUylQKBgG6TfrncMFzxrx61C+gBmvEXqQffHfkjbnx94OKnSTaQ3jiNHhez +X9v8KhD8o1bWtpay2uyl8pA9qYqBdzqxZ9kJKSW4qd/mCIJjOy87iBpWint5IPD8 +biZmldlbF9ZlJnJq5ZnlclCN/er5r8oPZHoCkj+nieOh8294nUBt25ptAoGAMnPA +EIeaKgbmONpHgLhWPwb9KOL/f1cHT5KA5CVH58nPmdyTqcaCGCAX7Vu+ueIIApgN +SWDf2thxT3S9zuOm5YiO0oRfSZKm5f2AbHE4ciFzgKQd4PvSdH0TN9XT0oW/WVhR +NAI5YcHPIQvyk4/4vXNo4Uf9Z6NqIFwisQmFXoUCgYBK/ZI/HsFsvnR5MV0tFdGM +AdNe6bsQRSZkowoaPxuWbklE4Hn6QvwEmQg3ST2O+vCQV1f1yI6YiWYoptOYscJc +MSs/HxhhaaO5ZsiuPUO6WEPzpNb2CxuIGDDtl83VtUQyjxCmOb6pqqjwzFmZ2bsw +JNMaBCzokrJTxknvauCuSQ== +-----END PRIVATE KEY----- diff --git a/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem b/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem new file mode 100644 index 0000000..2b56cc8 --- /dev/null +++ b/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZDCCAkygAwIBAgIJANod1+h9CtCaMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJJTDEPMA0GA1UECgwGQXBhY2hlMRowGAYDVQQDDBFy +YW5kb20uc2VydmVyLmNvbTAeFw0xODExMTkxNzEwMTlaFw0yODExMTYxNzEwMTla +MEcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJJTDEPMA0GA1UECgwGQXBhY2hlMRow +GAYDVQQDDBFyYW5kb20uc2VydmVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJmQRdKVn5kUoWAI+lwMbf17vcG4roTm4TfyrY8eHgNXTFywmchT +HnW+Kos/Bi6CHF8/RysfIF1egdV1b/dZfiCmIat2Iz928Ki6F6jQM9qpRQnadkpV +iHYznujJ6K4mJ2UpvsEaR/DcdY/1NXt1XIZaiT9yjCOimposclbGD+SHYqq9QEPQ +Ie30p3Ve+BdWYfATu8K/SPmjC/YxoTFOwVvXq5aZUuGXWA0cjZGoJfbItp5sfUO+ +1suuK3VcSuO1pJh4kT1yVVFqpPq+y8s6HIHm0D1Z9mp8Fiv8wcN5dB+tsh/7gKnd +pOmrmCtzkicgwwfR46Tr9//i1b6erWV+zNkCAwEAAaNTMFEwHQYDVR0OBBYEFI2y +qm0+UAChDAnLrAINeFOuyUlhMB8GA1UdIwQYMBaAFI2yqm0+UAChDAnLrAINeFOu +yUlhMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAA3ZNFbqxcOX +szS5A4EXCepyBJBFejEYy0CsvwQX/ai/pMrw5jqVeF0GAOTpBCVLddyY+ZV1arD2 +Pqi7Qwot9OxEZOzbCBiuMJGotruKgnWFQDHzJ9HA7KDQs270uNESAOG/xW9os9zN +MXApzqfBSR5EIQU5L3RtaiPzoKdQenGQUOj86s0Kon7snDSUzaA2VcfstMWgGvXP +JHtaVusULm0gry32cEap5G5UK+gII6DfLWgFwFGhHHmTz3mKjyGiJQ+09XBtu4lb +ENE+HGRBBA49dUKSr3kwErO4HyHnS0YrsTDnbYURCsGUDma12oijX2sCos6Q4zn8 +3svaouRrucw= +-----END CERTIFICATE----- diff --git a/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold b/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold new file mode 100644 index 0000000..39cdd0d --- /dev/null +++ b/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold @@ -0,0 +1 @@ +- diff --git a/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py b/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py new file mode 100644 index 0000000..26c3a43 --- /dev/null +++ b/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py @@ -0,0 +1,91 @@ +''' +Test the sslheaders 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. + +Test.Summary = ''' +Test sslheaders plugin. +''' + +Test.SkipUnless( + Condition.HasATSFeature('TS_USE_TLS_ALPN'), + Condition.HasCurlFeature('http2'), +) + +Test.Disk.File('sslheaders.log').Content = 'sslheaders.gold' + +server = Test.MakeOriginServer("server", options={'--load': Test.TestDirectory + '/observer.py'}) + +request_header = { + "headers": "GET / HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +ts = Test.MakeATSProcess("ts", select_ports=False) + +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") +# ts.addSSLfile("ssl/signer.pem") + +ts.Variables.ssl_port = 4443 + +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 0, + 'proxy.config.diags.debug.tags': 'http', + 'proxy.config.http.cache.http': 0, # Make sure each request is forwarded to the origin server. + 'proxy.config.proxy_name': 'Poxy_Proxy', # This will be the server name. + '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.http.server_ports': ( + 'ipv4:{0} ipv4:{1}:proto=http2;http:ssl ipv6:{0} ipv6:{1}:proto=http2;http:ssl' + .format(ts.Variables.port, ts.Variables.ssl_port)), +# 'proxy.config.ssl.client.verify.server': 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.url_remap.pristine_host_hdr' : 1, +# 'proxy.config.ssl.client.certification_level': 2, +# 'proxy.config.ssl.CA.cert.filename': '{0}/signer.pem'.format(ts.Variables.SSLDir), +# 'proxy.config.ssl.TLSv1_3': 0 +}) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +ts.Disk.remap_config.AddLine( + 'map http://bar.com http://127.0.0.1:{0}'.format(server.Variables.Port) +) +ts.Disk.remap_config.AddLine( + 'map https://bar.com http://127.0.0.1:{0}'.format(server.Variables.Port) +) + +ts.Disk.ssl_server_name_yaml.AddLines([ + '- fqdn: "*bar.com"', + ' verify_client: STRICT', +]) + +ts.Disk.plugin_config.AddLine( + 'sslheaders.so SSL-Client-ID=client.subject' +) + +tr = Test.AddTestRun() +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.Processes.Default.Command = ( + 'curl -H "SSL-Client-ID: My Fake Client ID" --verbose --ipv4 --insecure --header "Host: bar.com"' + + ' https://localhost:{}'.format(ts.Variables.ssl_port) +) +tr.Processes.Default.ReturnCode = 0
