~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/drb/ssl.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'socket'
 
2
require 'openssl'
 
3
require 'drb/drb'
 
4
require 'singleton'
 
5
 
 
6
module DRb
 
7
 
 
8
  class DRbSSLSocket < DRbTCPSocket
 
9
 
 
10
    class SSLConfig
 
11
 
 
12
      DEFAULT = {
 
13
        :SSLCertificate       => nil,
 
14
        :SSLPrivateKey        => nil,
 
15
        :SSLClientCA          => nil,
 
16
        :SSLCACertificatePath => nil,
 
17
        :SSLCACertificateFile => nil,
 
18
        :SSLVerifyMode        => ::OpenSSL::SSL::VERIFY_NONE, 
 
19
        :SSLVerifyDepth       => nil,
 
20
        :SSLVerifyCallback    => nil,   # custom verification
 
21
        :SSLCertificateStore  => nil,
 
22
        # Must specify if you use auto generated certificate.
 
23
        :SSLCertName          => nil,   # e.g. [["CN","fqdn.example.com"]]
 
24
        :SSLCertComment       => "Generated by Ruby/OpenSSL"
 
25
      }
 
26
 
 
27
      def initialize(config)
 
28
        @config  = config
 
29
        @cert    = config[:SSLCertificate]
 
30
        @pkey    = config[:SSLPrivateKey]
 
31
        @ssl_ctx = nil
 
32
      end
 
33
 
 
34
      def [](key); 
 
35
        @config[key] || DEFAULT[key]
 
36
      end
 
37
 
 
38
      def connect(tcp)
 
39
        ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
 
40
        ssl.sync = true
 
41
        ssl.connect
 
42
        ssl
 
43
      end
 
44
      
 
45
      def accept(tcp)
 
46
        ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
 
47
        ssl.sync = true
 
48
        ssl.accept
 
49
        ssl
 
50
      end
 
51
      
 
52
      def setup_certificate
 
53
        if @cert && @pkey
 
54
          return
 
55
        end
 
56
 
 
57
        rsa = OpenSSL::PKey::RSA.new(512){|p, n|
 
58
          next unless self[:verbose]
 
59
          case p
 
60
          when 0; $stderr.putc "."  # BN_generate_prime
 
61
          when 1; $stderr.putc "+"  # BN_generate_prime
 
62
          when 2; $stderr.putc "*"  # searching good prime,
 
63
                                    # n = #of try,
 
64
                                    # but also data from BN_generate_prime
 
65
          when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
 
66
                                    # but also data from BN_generate_prime
 
67
          else;   $stderr.putc "*"  # BN_generate_prime
 
68
          end
 
69
        }
 
70
 
 
71
        cert = OpenSSL::X509::Certificate.new
 
72
        cert.version = 3
 
73
        cert.serial = 0
 
74
        name = OpenSSL::X509::Name.new(self[:SSLCertName])
 
75
        cert.subject = name
 
76
        cert.issuer = name
 
77
        cert.not_before = Time.now
 
78
        cert.not_after = Time.now + (365*24*60*60)
 
79
        cert.public_key = rsa.public_key
 
80
        
 
81
        ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
 
82
        cert.extensions = [
 
83
          ef.create_extension("basicConstraints","CA:FALSE"),
 
84
          ef.create_extension("subjectKeyIdentifier", "hash") ]
 
85
        ef.issuer_certificate = cert
 
86
        cert.add_extension(ef.create_extension("authorityKeyIdentifier",
 
87
                                               "keyid:always,issuer:always"))
 
88
        if comment = self[:SSLCertComment]
 
89
          cert.add_extension(ef.create_extension("nsComment", comment))
 
90
        end
 
91
        cert.sign(rsa, OpenSSL::Digest::SHA1.new)
 
92
        
 
93
        @cert = cert
 
94
        @pkey = rsa
 
95
      end
 
96
 
 
97
      def setup_ssl_context
 
98
        ctx = ::OpenSSL::SSL::SSLContext.new
 
99
        ctx.cert            = @cert
 
100
        ctx.key             = @pkey
 
101
        ctx.client_ca       = self[:SSLClientCA]
 
102
        ctx.ca_path         = self[:SSLCACertificatePath]
 
103
        ctx.ca_file         = self[:SSLCACertificateFile]
 
104
        ctx.verify_mode     = self[:SSLVerifyMode]
 
105
        ctx.verify_depth    = self[:SSLVerifyDepth]
 
106
        ctx.verify_callback = self[:SSLVerifyCallback]
 
107
        ctx.cert_store      = self[:SSLCertificateStore]
 
108
        @ssl_ctx = ctx
 
109
      end
 
110
    end
 
111
 
 
112
    def self.parse_uri(uri)
 
113
      if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
 
114
        host = $1
 
115
        port = $2.to_i
 
116
        option = $4
 
117
        [host, port, option]
 
118
      else
 
119
        raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
 
120
        raise(DRbBadURI, 'can\'t parse uri:' + uri)
 
121
      end
 
122
    end
 
123
 
 
124
    def self.open(uri, config)
 
125
      host, port, option = parse_uri(uri)
 
126
      host.untaint
 
127
      port.untaint
 
128
      soc = TCPSocket.open(host, port)
 
129
      ssl_conf = SSLConfig::new(config)
 
130
      ssl_conf.setup_ssl_context
 
131
      ssl = ssl_conf.connect(soc)
 
132
      self.new(uri, ssl, ssl_conf, true)
 
133
    end
 
134
 
 
135
    def self.open_server(uri, config)
 
136
      uri = 'drbssl://:0' unless uri
 
137
      host, port, opt = parse_uri(uri)
 
138
      if host.size == 0
 
139
        host = getservername
 
140
        soc = open_server_inaddr_any(host, port)
 
141
      else
 
142
        soc = TCPServer.open(host, port)
 
143
      end
 
144
      port = soc.addr[1] if port == 0
 
145
      @uri = "drbssl://#{host}:#{port}"
 
146
      
 
147
      ssl_conf = SSLConfig.new(config)
 
148
      ssl_conf.setup_certificate
 
149
      ssl_conf.setup_ssl_context
 
150
      self.new(@uri, soc, ssl_conf, false)
 
151
    end
 
152
 
 
153
    def self.uri_option(uri, config)
 
154
      host, port, option = parse_uri(uri)
 
155
      return "drbssl://#{host}:#{port}", option
 
156
    end
 
157
 
 
158
    def initialize(uri, soc, config, is_established)
 
159
      @ssl = is_established ? soc : nil
 
160
      super(uri, soc.to_io, config)
 
161
    end
 
162
    
 
163
    def stream; @ssl; end
 
164
 
 
165
    def close
 
166
      if @ssl
 
167
        @ssl.close
 
168
        @ssl = nil
 
169
      end
 
170
      super
 
171
    end
 
172
      
 
173
    def accept
 
174
      begin
 
175
      while true
 
176
        soc = @socket.accept
 
177
        break if (@acl ? @acl.allow_socket?(soc) : true) 
 
178
        soc.close
 
179
      end
 
180
      ssl = @config.accept(soc)
 
181
      self.class.new(uri, ssl, @config, true)
 
182
      rescue OpenSSL::SSL::SSLError
 
183
        warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose]
 
184
        retry
 
185
      end
 
186
    end
 
187
  end
 
188
  
 
189
  DRbProtocol.add_protocol(DRbSSLSocket)
 
190
end