From a767613c3d1b1c1b37e77c7eb83932ca47f4e14e Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Wed, 1 Feb 2023 22:50:09 +0000 Subject: [PATCH] add more SSL_ meta vars from the mod_ssl family adding a few more variables when webrick is run in SSL, and mutual tls is in place; in such a case, it's important to send information to the backend whether the certificate has been verified, among others. --- lib/webrick/https.rb | 28 ++++++++++++- test/webrick/test_https.rb | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/lib/webrick/https.rb b/lib/webrick/https.rb index 7f00b30..c301006 100644 --- a/lib/webrick/https.rb +++ b/lib/webrick/https.rb @@ -64,17 +64,43 @@ def parse_uri(str, scheme="https") alias orig_meta_vars meta_vars + # This method provides the metavariables defined by + # the Apache mod_ssl module, which add SSL/TLS support to CGI. + # To browse the current documentation, see below: + # http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25 + def meta_vars meta = orig_meta_vars if server_cert meta["HTTPS"] = "on" meta["SSL_SERVER_CERT"] = @server_cert.to_pem - meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : "" + + if @client_cert + meta["SSL_CLIENT_M_VERSION"] = @client_cert.version + meta["SSL_CLIENT_M_SERIAL"] = @client_cert.serial + meta["SSL_CLIENT_S_DN"] = @client_cert.subject.to_s + meta["SSL_CLIENT_I_DN"] = @client_cert.issuer.to_s + meta["SSL_CLIENT_V_START"] = @client_cert.not_before.httpdate + meta["SSL_CLIENT_V_END"] = @client_cert.not_after.httpdate + meta["SSL_CLIENT_V_REMAIN"] = (@client_cert.not_after - @client_cert.not_before) / 60 / 60 / 24 + meta["SSL_CLIENT_A_SIG"] = @client_cert.signature_algorithm + meta["SSL_CLIENT_A_KEY"] = @client_cert.public_key.oid + meta["SSL_CLIENT_CERT"] = @client_cert.to_pem + meta["SSL_CLIENT_VERIFY"] = if @socket.context.verify_mode == OpenSSL::SSL::VERIFY_NONE + "NONE" + elsif @socket.verify_result == OpenSSL::X509::V_OK + "SUCCESS" + else + "FAILED" + end + end + if @client_cert_chain @client_cert_chain.each_with_index{|cert, i| meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem } end + meta["SSL_CIPHER"] = @cipher[0] meta["SSL_PROTOCOL"] = @cipher[1] meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s diff --git a/test/webrick/test_https.rb b/test/webrick/test_https.rb index ec0aac3..e702528 100644 --- a/test/webrick/test_https.rb +++ b/test/webrick/test_https.rb @@ -109,4 +109,89 @@ def test_check_ssl_virtual end } end + + def test_ssl_meta_vars + # CA cert + ca_cert, ca_key = WEBrick::Utils.create_self_signed_cert(2048, "/CN=ca", "is CA") + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = ca_cert + ef.issuer_certificate = ca_cert + ca_cert.extensions = [ + ef.create_extension("basicConstraints", "CA:TRUE", true), + ef.create_extension("keyUsage", "keyCertSign, cRLSign", true), + ef.create_extension("subjectKeyIdentifier", "hash", false) + ] + ca_cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") + ca_cert.sign(ca_key, "SHA256") + + # Client cert + client_cert, client_key = WEBrick::Utils.create_self_signed_cert(2048, "/CN=client", "is client") + client_cert.issuer = ca_cert.issuer + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = client_cert + ef.issuer_certificate = ca_cert + client_cert.extensions = [ + ef.create_extension("basicConstraints", "CA:FALSE", true), + ef.create_extension("keyUsage", "digitalSignature", true), + ef.create_extension("subjectKeyIdentifier", "hash", false), + ef.create_extension("subjectAltName", "DNS:localhost,IP:127.0.0.1", false) + ] + client_cert.sign(ca_key, "SHA256") + + + # Server cert + server_cert, server_key = WEBrick::Utils.create_self_signed_cert(2048, "/CN=server", "is server") + server_cert.issuer = ca_cert.issuer + ef = OpenSSL::X509::ExtensionFactory.new + ef.subject_certificate = server_cert + ef.issuer_certificate = ca_cert + server_cert.extensions = [ + ef.create_extension("basicConstraints", "CA:FALSE", true), + ef.create_extension("keyUsage", "digitalSignature", true), + ef.create_extension("subjectKeyIdentifier", "hash", false), + ef.create_extension("subjectAltName", "DNS:localhost,IP:127.0.0.1", false) + ] + server_cert.sign(ca_key, "SHA256") + + # Client CA Store + ca_client_store = OpenSSL::X509::Store.new + ca_client_store.add_cert(ca_cert) + ca_client_store.add_cert(client_cert) + + # Server CA Store + server_ca_store = OpenSSL::X509::Store.new + server_ca_store.add_cert(ca_cert) + server_ca_store.add_cert(server_cert) + + config = { + SSLEnable: true, + :SSLCertName => "/CN=localhost", + SSLCertificate: server_cert, + SSLPrivateKey: server_key, + SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER, + SSLCertificateStore: ca_client_store + } + TestWEBrick.start_httpserver(config){|server, addr, port, log| + env = nil + server.mount_proc("/") {|req, res| + env = req.meta_vars + res.body = "OK" + } + + subject = nil + http = Net::HTTP.new(addr, port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_CLIENT_ONCE + http.cert = client_cert + http.key = client_key + http.extra_chain_cert = [ca_cert] + http.cert_store = server_ca_store + req = Net::HTTP::Get.new("/") + body = http.request(req).body + assert_not_nil(env) + assert_equal("SUCCESS", env["SSL_CLIENT_VERIFY"]) + assert_equal("/CN=client", env["SSL_CLIENT_S_DN"]) + assert_equal(client_cert.to_pem, env["SSL_CLIENT_CERT"]) + } + end end