summaryrefslogtreecommitdiffstats
path: root/lib/webrick
diff options
context:
space:
mode:
authorgotoyuzo <gotoyuzo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2003-08-19 06:00:36 +0000
committergotoyuzo <gotoyuzo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2003-08-19 06:00:36 +0000
commit4307f96f86f3b77b23f477488b779ee2b74a9d61 (patch)
treeeedc2d207ffde1876c9d7271f8492741ee065584 /lib/webrick
parent4a49c6555a441ff2bfd0ac63608db2153a9fab7e (diff)
downloadruby-4307f96f86f3b77b23f477488b779ee2b74a9d61.tar.gz
ruby-4307f96f86f3b77b23f477488b779ee2b74a9d61.tar.xz
ruby-4307f96f86f3b77b23f477488b779ee2b74a9d61.zip
* lib/webrick/ssl.rb: new file; SSL/TLS enhancement for GenericServer.
* lib/webrick/https.rb: SSLSocket handling is moved to webrick/ssl.rb. * lib/webrick/compat.rb (File::fnmatch): remove old migration code. * lib/webrick/httpserver.rb (HTTPServer#run): ditto. * lib/webrick/server.rb (GenericServer#listen): the body of this method is pull out as Utils::create_lisnteners. * lib/webrick/utils.rb (Utils::create_lisnteners): new method. * lib/webrick/server.rb (GenericServer#start): should not through unknown errors. and refine comments. * ext/openssl/lib/openssl/ssl.rb (SSLServer#accept): should close socket if SSLSocket raises error. git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@4409 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/webrick')
-rw-r--r--lib/webrick/compat.rb15
-rw-r--r--lib/webrick/https.rb127
-rw-r--r--lib/webrick/httpserver.rb2
-rw-r--r--lib/webrick/server.rb38
-rw-r--r--lib/webrick/ssl.rb124
-rw-r--r--lib/webrick/utils.rb24
6 files changed, 170 insertions, 160 deletions
diff --git a/lib/webrick/compat.rb b/lib/webrick/compat.rb
index a972204ff..ad7760b64 100644
--- a/lib/webrick/compat.rb
+++ b/lib/webrick/compat.rb
@@ -13,18 +13,3 @@ module Errno
class ECONNRESET < SystemCallError; end
class ECONNABORTED < SystemCallError; end
end
-
-unless File.respond_to?(:fnmatch)
- def File.fnmatch(pat, str)
- case pat[0]
- when nil
- not str[0]
- when ?*
- fnmatch(pat[1..-1], str) || str[0] && fnmatch(pat, str[1..-1])
- when ??
- str[0] && fnmatch(pat[1..-1], str[1..-1])
- else
- pat[0] == str[0] && fnmatch(pat[1..-1], str[1..-1])
- end
- end
-end
diff --git a/lib/webrick/https.rb b/lib/webrick/https.rb
index fa8c667d2..4e44cfab3 100644
--- a/lib/webrick/https.rb
+++ b/lib/webrick/https.rb
@@ -8,31 +8,11 @@
#
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
-require 'webrick'
-require 'openssl'
+require 'webrick/ssl'
module WEBrick
module Config
- HTTP.update(
- :SSLEnable => true,
- :SSLCertificate => nil,
- :SSLPrivateKey => nil,
- :SSLClientCA => nil,
- :SSLCACertificateFile => nil,
- :SSLCACertificatePath => nil,
- :SSLCertificateStore => nil,
- :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
- :SSLVerifyDepth => nil,
- :SSLVerifyCallback => nil, # custom verification
- :SSLTimeout => nil,
- :SSLOptions => nil,
- # Must specify if you use auto generated certificate.
- :SSLCertName => nil,
- :SSLCertComment => "Generated by Ruby/OpenSSL"
- )
-
- osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
- HTTP[:ServerSoftware] << " OpenSSL/#{osslv}"
+ HTTP.update(SSL)
end
class HTTPRequest
@@ -41,16 +21,18 @@ module WEBrick
alias orig_parse parse
def parse(socket=nil)
+ if socket && socket.is_a?(OpenSSL::SSL::SSLSocket)
+ @server_cert = @config[:SSLCertificate]
+ @client_cert = socket.peer_cert
+ @cipher = socket.cipher
+ end
orig_parse(socket)
- @cipher = socket.respond_to?(:cipher) ? socket.cipher : nil
- @client_cert = socket.respond_to?(:peer_cert) ? socket.peer_cert : nil
- @server_cert = @config[:SSLCertificate]
end
alias orig_parse_uri parse_uri
def parse_uri(str, scheme="https")
- if @config[:SSLEnable]
+ if @server_cert
return orig_parse_uri(str, scheme)
end
return orig_parse_uri(str)
@@ -60,100 +42,13 @@ module WEBrick
def meta_vars
meta = orig_meta_vars
- if @config[:SSLEnable]
+ if @server_cert
meta["HTTPS"] = "on"
- meta["SSL_CIPHER"] = @cipher ? @cipher[0] : ""
+ meta["SSL_SERVER_CERT"] = @server_cert.to_pem
meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
- meta["SSL_SERVER_CERT"] = @server_cert ? @server_cert.to_pem : ""
+ meta["SSL_CIPHER"] = @cipher[0]
end
meta
end
end
-
- class HTTPServer
- alias orig_init initialize
-
- def initialize(*args)
- orig_init(*args)
-
- if @config[:SSLEnable]
- unless @config[:SSLCertificate]
- rsa = OpenSSL::PKey::RSA.new(512){|p, n|
- case p
- when 0; $stderr.putc "." # BN_generate_prime
- when 1; $stderr.putc "+" # BN_generate_prime
- when 2; $stderr.putc "*" # searching good prime,
- # n = #of try,
- # but also data from BN_generate_prime
- when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
- # but also data from BN_generate_prime
- else; $stderr.putc "*" # BN_generate_prime
- end
- }
- cert = OpenSSL::X509::Certificate.new
- cert.version = 3
- cert.serial = 0
- name = OpenSSL::X509::Name.new(@config[:SSLCertName])
- cert.subject = name
- cert.issuer = name
- cert.not_before = Time.now
- cert.not_after = Time.now + (365*24*60*60)
- cert.public_key = rsa.public_key
-
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
- cert.extensions = [
- ef.create_extension("basicConstraints","CA:FALSE"),
- ef.create_extension("subjectKeyIdentifier", "hash"),
- ef.create_extension("extendedKeyUsage", "serverAuth")
- ]
- ef.issuer_certificate = cert
- ext = ef.create_extension("authorityKeyIdentifier",
- "keyid:always,issuer:always")
- cert.add_extension(ext)
- if comment = @config[:SSLCertComment]
- cert.add_extension(ef.create_extension("nsComment", comment))
- end
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
-
- @config[:SSLPrivateKey] = rsa
- @config[:SSLCertificate] = cert
- @logger.info cert.to_s
- end
- @ctx = OpenSSL::SSL::SSLContext.new
- set_ssl_context(@ctx, @config)
- end
- end
-
- alias orig_run run
-
- def run(sock)
- if @config[:SSLEnable]
- ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
- ssl.sync = true
- ssl.accept
- Thread.current[:WEBrickSocket] = ssl
- orig_run(ssl)
- Thread.current[:WEBrickSocket] = sock
- ssl.close
- else
- orig_run(sock)
- end
- end
-
- private
-
- def set_ssl_context(ctx, config)
- ctx.key = config[:SSLPrivateKey]
- ctx.cert = config[:SSLCertificate]
- ctx.client_ca = config[:SSLClientCA]
- ctx.ca_file = config[:SSLCACertificateFile]
- ctx.ca_path = config[:SSLCACertificatePath]
- ctx.cert_store = config[:SSLCertificateStore]
- ctx.verify_mode = config[:SSLVerifyClient]
- ctx.verify_depth = config[:SSLVerifyDepth]
- ctx.verify_callback = config[:SSLVerifyCallback]
- ctx.timeout = config[:SSLTimeout]
- ctx.options = config[:SSLOptions]
- end
- end
end
diff --git a/lib/webrick/httpserver.rb b/lib/webrick/httpserver.rb
index df06e19e2..a3da99db1 100644
--- a/lib/webrick/httpserver.rb
+++ b/lib/webrick/httpserver.rb
@@ -58,7 +58,7 @@ module WEBrick
res.set_error(ex)
rescue HTTPStatus::Status => ex
res.status = ex.code
- rescue StandardError, NameError => ex # for Ruby 1.6
+ rescue StandardError => ex
@logger.error(ex)
res.set_error(ex, true)
ensure
diff --git a/lib/webrick/server.rb b/lib/webrick/server.rb
index 911f78b66..c71b11864 100644
--- a/lib/webrick/server.rb
+++ b/lib/webrick/server.rb
@@ -55,10 +55,9 @@ module WEBrick
@logger.info("WEBrick #{webrickv}")
@logger.info("ruby #{rubyv}")
- if @config[:DoNotListen]
- @listeners = []
- else
- @listeners = listen(@config[:BindAddress], @config[:Port])
+ @listeners = []
+ unless @config[:DoNotListen]
+ listen(@config[:BindAddress], @config[:Port])
@config[:Listen].each{|addr, port|
listen(addr, port).each{|sock| @listeners << sock }
}
@@ -70,26 +69,7 @@ module WEBrick
end
def listen(address, port)
- res = Socket::getaddrinfo(address, port,
- Socket::AF_UNSPEC, # address family
- Socket::SOCK_STREAM, # socket type
- 0, # protocol
- Socket::AI_PASSIVE) # flag
- last_error = nil
- sockets = []
- res.each{|ai|
- begin
- @logger.debug("TCPServer.new(#{ai[3]}, #{ai[1]})")
- sock = TCPServer.new(ai[3], ai[1])
- Utils::set_close_on_exec(sock)
- sockets << sock
- rescue => ex
- @logger.warn("TCPServer Error: #{ex}")
- last_error = ex
- end
- }
- raise last_error if sockets.empty?
- return sockets
+ @listeners += Utils::create_listeners(address, port, @logger)
end
def start(&block)
@@ -117,12 +97,14 @@ module WEBrick
}
end
rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO => ex
+ # TCP connection was established but RST segment was sent
+ # from peer before calling TCPServer#accept.
+ rescue Errno::EBADF => ex
+ # if the listening socket was closed in GenericServer#shutdown,
+ # IO::select raise it.
+ rescue => ex
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
@logger.error msg
- rescue Errno::EBADF => ex # IO::select causes by shutdown
- rescue => ex
- @logger.error ex
- break
end
end
diff --git a/lib/webrick/ssl.rb b/lib/webrick/ssl.rb
new file mode 100644
index 000000000..402fd5d99
--- /dev/null
+++ b/lib/webrick/ssl.rb
@@ -0,0 +1,124 @@
+#
+# ssl.rb -- SSL/TLS enhancement for GenericServer
+#
+# Copyright (c) 2003 GOTOU Yuuzou All rights reserved.
+#
+# $Id$
+
+require 'webrick'
+require 'openssl'
+
+module WEBrick
+ module Config
+ svrsoft = General[:ServerSoftware]
+ osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
+ SSL = {
+ :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}",
+ :SSLEnable => true,
+ :SSLCertificate => nil,
+ :SSLPrivateKey => nil,
+ :SSLClientCA => nil,
+ :SSLCACertificateFile => nil,
+ :SSLCACertificatePath => nil,
+ :SSLCertificateStore => nil,
+ :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
+ :SSLVerifyDepth => nil,
+ :SSLVerifyCallback => nil, # custom verification
+ :SSLTimeout => nil,
+ :SSLOptions => nil,
+ :SSLStartImmediately => true,
+ # Must specify if you use auto generated certificate.
+ :SSLCertName => nil,
+ :SSLCertComment => "Generated by Ruby/OpenSSL"
+ }
+ General.update(SSL)
+ end
+
+ module Utils
+ def create_self_signed_cert(bits, cn, comment)
+ rsa = OpenSSL::PKey::RSA.new(bits){|p, n|
+ case p
+ when 0; $stderr.putc "." # BN_generate_prime
+ when 1; $stderr.putc "+" # BN_generate_prime
+ when 2; $stderr.putc "*" # searching good prime,
+ # n = #of try,
+ # but also data from BN_generate_prime
+ when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
+ # but also data from BN_generate_prime
+ else; $stderr.putc "*" # BN_generate_prime
+ end
+ }
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 3
+ cert.serial = 0
+ name = OpenSSL::X509::Name.new(cn)
+ cert.subject = name
+ cert.issuer = name
+ cert.not_before = Time.now
+ cert.not_after = Time.now + (365*24*60*60)
+ cert.public_key = rsa.public_key
+
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+ ef.issuer_certificate = cert
+ cert.extensions = [
+ ef.create_extension("basicConstraints","CA:FALSE"),
+ ef.create_extension("keyUsage", "keyEncipherment"),
+ ef.create_extension("subjectKeyIdentifier", "hash"),
+ ef.create_extension("extendedKeyUsage", "serverAuth"),
+ ef.create_extension("nsComment", comment),
+ ]
+ aki = ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ cert.add_extension(aki)
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+
+ return [ cert, rsa ]
+ end
+ module_function :create_self_signed_cert
+ end
+
+ class GenericServer
+ def ssl_context
+ @ssl_context ||= nil
+ end
+
+ def listen(address, port)
+ listeners = Utils::create_listeners(address, port, @logger)
+ if @config[:SSLEnable]
+ unless ssl_context
+ @ssl_context = setup_ssl_context(@config)
+ @logger.info("\n" + @config[:SSLCertificate].to_text)
+ end
+ listeners.collect!{|svr|
+ ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
+ ssvr.start_immediately = @config[:SSLStartImmediately]
+ ssvr
+ }
+ end
+ @listeners += listeners
+ end
+
+ def setup_ssl_context(config)
+ unless config[:SSLCertificate]
+ cn = config[:SSLCertName]
+ comment = config[:SSLCertComment]
+ cert, key = Utils::create_self_signed_cert(1024, cn, comment)
+ config[:SSLCertificate] = cert
+ config[:SSLPrivateKey] = key
+ end
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.key = config[:SSLPrivateKey]
+ ctx.cert = config[:SSLCertificate]
+ ctx.client_ca = config[:SSLClientCA]
+ ctx.ca_file = config[:SSLCACertificateFile]
+ ctx.ca_path = config[:SSLCACertificatePath]
+ ctx.cert_store = config[:SSLCertificateStore]
+ ctx.verify_mode = config[:SSLVerifyClient]
+ ctx.verify_depth = config[:SSLVerifyDepth]
+ ctx.verify_callback = config[:SSLVerifyCallback]
+ ctx.timeout = config[:SSLTimeout]
+ ctx.options = config[:SSLOptions]
+ ctx
+ end
+ end
+end
diff --git a/lib/webrick/utils.rb b/lib/webrick/utils.rb
index 646880d65..d371f4989 100644
--- a/lib/webrick/utils.rb
+++ b/lib/webrick/utils.rb
@@ -48,6 +48,30 @@ module WEBrick
end
module_function :getservername
+ def create_listeners(address, port, logger=nil)
+ res = Socket::getaddrinfo(address, port,
+ Socket::AF_UNSPEC, # address family
+ Socket::SOCK_STREAM, # socket type
+ 0, # protocol
+ Socket::AI_PASSIVE) # flag
+ last_error = nil
+ sockets = []
+ res.each{|ai|
+ begin
+ logger.debug("TCPServer.new(#{ai[3]}, #{ai[1]})") if logger
+ sock = TCPServer.new(ai[3], ai[1])
+ Utils::set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ logger.warn("TCPServer Error: #{ex}") if logger
+ last_error = ex
+ end
+ }
+ raise last_error if sockets.empty?
+ return sockets
+ end
+ module_function :create_listeners
+
RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789" +
"abcdefghijklmnopqrstuvwxyz"