Skip to content

Commit

Permalink
add more SSL_ meta vars from the mod_ssl family
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
HoneyryderChuck committed Feb 3, 2023
1 parent 158a7ef commit a767613
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/webrick/https.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
85 changes: 85 additions & 0 deletions test/webrick/test_https.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit a767613

Please sign in to comment.