1# frozen_string_literal: false
2#
3# ssl.rb -- SSL/TLS enhancement for GenericServer
4#
5# Copyright (c) 2003 GOTOU Yuuzou All rights reserved.
6#
7# $Id: ssl.rb 66152 2018-12-03 04:51:08Z normal $
8
9require 'webrick'
10require 'openssl'
11
12module WEBrick
13  module Config
14    svrsoft = General[:ServerSoftware]
15    osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
16
17    ##
18    # Default SSL server configuration.
19    #
20    # WEBrick can automatically create a self-signed certificate if
21    # <code>:SSLCertName</code> is set.  For more information on the various
22    # SSL options see OpenSSL::SSL::SSLContext.
23    #
24    # :ServerSoftware       ::
25    #   The server software name used in the Server: header.
26    # :SSLEnable            :: false,
27    #   Enable SSL for this server.  Defaults to false.
28    # :SSLCertificate       ::
29    #   The SSL certificate for the server.
30    # :SSLPrivateKey        ::
31    #   The SSL private key for the server certificate.
32    # :SSLClientCA          :: nil,
33    #   Array of certificates that will be sent to the client.
34    # :SSLExtraChainCert    :: nil,
35    #   Array of certificates that will be added to the certificate chain
36    # :SSLCACertificateFile :: nil,
37    #   Path to a CA certificate file
38    # :SSLCACertificatePath :: nil,
39    #   Path to a directory containing CA certificates
40    # :SSLCertificateStore  :: nil,
41    #   OpenSSL::X509::Store used for certificate validation of the client
42    # :SSLTmpDhCallback     :: nil,
43    #   Callback invoked when DH parameters are required.
44    # :SSLVerifyClient      ::
45    #   Sets whether the client is verified.  This defaults to VERIFY_NONE
46    #   which is typical for an HTTPS server.
47    # :SSLVerifyDepth       ::
48    #   Number of CA certificates to walk when verifying a certificate chain
49    # :SSLVerifyCallback    ::
50    #   Custom certificate verification callback
51    # :SSLServerNameCallback::
52    #   Custom servername indication callback
53    # :SSLTimeout           ::
54    #   Maximum session lifetime
55    # :SSLOptions           ::
56    #   Various SSL options
57    # :SSLCiphers           ::
58    #   Ciphers to be used
59    # :SSLStartImmediately  ::
60    #   Immediately start SSL upon connection?  Defaults to true
61    # :SSLCertName          ::
62    #   SSL certificate name.  Must be set to enable automatic certificate
63    #   creation.
64    # :SSLCertComment       ::
65    #   Comment used during automatic certificate creation.
66
67    SSL = {
68      :ServerSoftware       => "#{svrsoft} OpenSSL/#{osslv}",
69      :SSLEnable            => false,
70      :SSLCertificate       => nil,
71      :SSLPrivateKey        => nil,
72      :SSLClientCA          => nil,
73      :SSLExtraChainCert    => nil,
74      :SSLCACertificateFile => nil,
75      :SSLCACertificatePath => nil,
76      :SSLCertificateStore  => nil,
77      :SSLTmpDhCallback     => nil,
78      :SSLVerifyClient      => ::OpenSSL::SSL::VERIFY_NONE,
79      :SSLVerifyDepth       => nil,
80      :SSLVerifyCallback    => nil,   # custom verification
81      :SSLTimeout           => nil,
82      :SSLOptions           => nil,
83      :SSLCiphers           => nil,
84      :SSLStartImmediately  => true,
85      # Must specify if you use auto generated certificate.
86      :SSLCertName          => nil,
87      :SSLCertComment       => "Generated by Ruby/OpenSSL"
88    }
89    General.update(SSL)
90  end
91
92  module Utils
93    ##
94    # Creates a self-signed certificate with the given number of +bits+,
95    # the issuer +cn+ and a +comment+ to be stored in the certificate.
96
97    def create_self_signed_cert(bits, cn, comment)
98      rsa = OpenSSL::PKey::RSA.new(bits){|p, n|
99        case p
100        when 0; $stderr.putc "."  # BN_generate_prime
101        when 1; $stderr.putc "+"  # BN_generate_prime
102        when 2; $stderr.putc "*"  # searching good prime,
103                                  # n = #of try,
104                                  # but also data from BN_generate_prime
105        when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
106                                  # but also data from BN_generate_prime
107        else;   $stderr.putc "*"  # BN_generate_prime
108        end
109      }
110      cert = OpenSSL::X509::Certificate.new
111      cert.version = 2
112      cert.serial = 1
113      name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn)
114                                  : OpenSSL::X509::Name.new(cn)
115      cert.subject = name
116      cert.issuer = name
117      cert.not_before = Time.now
118      cert.not_after = Time.now + (365*24*60*60)
119      cert.public_key = rsa.public_key
120
121      ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
122      ef.issuer_certificate = cert
123      cert.extensions = [
124        ef.create_extension("basicConstraints","CA:FALSE"),
125        ef.create_extension("keyUsage", "keyEncipherment"),
126        ef.create_extension("subjectKeyIdentifier", "hash"),
127        ef.create_extension("extendedKeyUsage", "serverAuth"),
128        ef.create_extension("nsComment", comment),
129      ]
130      aki = ef.create_extension("authorityKeyIdentifier",
131                                "keyid:always,issuer:always")
132      cert.add_extension(aki)
133      cert.sign(rsa, OpenSSL::Digest::SHA256.new)
134
135      return [ cert, rsa ]
136    end
137    module_function :create_self_signed_cert
138  end
139
140  ##
141  #--
142  # Updates WEBrick::GenericServer with SSL functionality
143
144  class GenericServer
145
146    ##
147    # SSL context for the server when run in SSL mode
148
149    def ssl_context # :nodoc:
150      @ssl_context ||= begin
151        if @config[:SSLEnable]
152          ssl_context = setup_ssl_context(@config)
153          @logger.info("\n" + @config[:SSLCertificate].to_text)
154          ssl_context
155        end
156      end
157    end
158
159    undef listen
160
161    ##
162    # Updates +listen+ to enable SSL when the SSL configuration is active.
163
164    def listen(address, port) # :nodoc:
165      listeners = Utils::create_listeners(address, port)
166      if @config[:SSLEnable]
167        listeners.collect!{|svr|
168          ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
169          ssvr.start_immediately = @config[:SSLStartImmediately]
170          ssvr
171        }
172      end
173      @listeners += listeners
174      setup_shutdown_pipe
175    end
176
177    ##
178    # Sets up an SSL context for +config+
179
180    def setup_ssl_context(config) # :nodoc:
181      unless config[:SSLCertificate]
182        cn = config[:SSLCertName]
183        comment = config[:SSLCertComment]
184        cert, key = Utils::create_self_signed_cert(2048, cn, comment)
185        config[:SSLCertificate] = cert
186        config[:SSLPrivateKey] = key
187      end
188      ctx = OpenSSL::SSL::SSLContext.new
189      ctx.key = config[:SSLPrivateKey]
190      ctx.cert = config[:SSLCertificate]
191      ctx.client_ca = config[:SSLClientCA]
192      ctx.extra_chain_cert = config[:SSLExtraChainCert]
193      ctx.ca_file = config[:SSLCACertificateFile]
194      ctx.ca_path = config[:SSLCACertificatePath]
195      ctx.cert_store = config[:SSLCertificateStore]
196      ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
197      ctx.verify_mode = config[:SSLVerifyClient]
198      ctx.verify_depth = config[:SSLVerifyDepth]
199      ctx.verify_callback = config[:SSLVerifyCallback]
200      ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
201      ctx.timeout = config[:SSLTimeout]
202      ctx.options = config[:SSLOptions]
203      ctx.ciphers = config[:SSLCiphers]
204      ctx
205    end
206
207    ##
208    # ServerNameIndication callback
209
210    def ssl_servername_callback(sslsocket, hostname = nil)
211      # default
212    end
213
214  end
215end
216