mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	add acl.rb, ssl.rb
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4676 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									e6b60ce2fe
								
							
						
					
					
						commit
						8d2a8dbfb0
					
				
					 2 changed files with 329 additions and 0 deletions
				
			
		
							
								
								
									
										144
									
								
								lib/drb/acl.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								lib/drb/acl.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
# acl-2.0 - simple Access Control List
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2000,2002,2003 Masatoshi SEKI
 | 
			
		||||
#
 | 
			
		||||
# acl.rb is copyrighted free software by Masatoshi SEKI.
 | 
			
		||||
# You can redistribute it and/or modify it under the same terms as Ruby.
 | 
			
		||||
 | 
			
		||||
require 'ipaddr'
 | 
			
		||||
 | 
			
		||||
class ACL
 | 
			
		||||
  VERSION=["2.0.0"]
 | 
			
		||||
  class ACLEntry
 | 
			
		||||
    def initialize(str)
 | 
			
		||||
      if str == '*' or str == 'all'
 | 
			
		||||
	@pat = [:all]
 | 
			
		||||
      else
 | 
			
		||||
	begin
 | 
			
		||||
	  @pat = [:ip, IPAddr.new(str)]
 | 
			
		||||
	rescue ArgumentError
 | 
			
		||||
	  @pat = [:name, dot_pat(str)]
 | 
			
		||||
	end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
    def dot_pat_str(str)
 | 
			
		||||
      list = str.split('.').collect { |s|
 | 
			
		||||
	(s == '*') ? '.+' : s
 | 
			
		||||
      }
 | 
			
		||||
      list.join("\\.")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
    def dot_pat(str)
 | 
			
		||||
      exp = "^" + dot_pat_str(str) + "$"
 | 
			
		||||
      Regexp.new(exp)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    public
 | 
			
		||||
    def match(addr)
 | 
			
		||||
      case @pat[0]
 | 
			
		||||
      when :all
 | 
			
		||||
	true
 | 
			
		||||
      when :ip
 | 
			
		||||
	begin
 | 
			
		||||
	  ipaddr = IPAddr.new(addr[3])
 | 
			
		||||
	  ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
 | 
			
		||||
	rescue ArgumentError
 | 
			
		||||
	  return false
 | 
			
		||||
	end
 | 
			
		||||
	(@pat[1].include?(ipaddr)) ? true : false
 | 
			
		||||
      when :name
 | 
			
		||||
	(@pat[1] =~ addr[2]) ? true : false
 | 
			
		||||
      else
 | 
			
		||||
	false
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class ACLList
 | 
			
		||||
    def initialize
 | 
			
		||||
      @list = []
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    public
 | 
			
		||||
    def match(addr)
 | 
			
		||||
      @list.each do |e|
 | 
			
		||||
	return true if e.match(addr)
 | 
			
		||||
      end
 | 
			
		||||
      false
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    public
 | 
			
		||||
    def add(str)
 | 
			
		||||
      @list.push(ACLEntry.new(str))
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  DENY_ALLOW = 0
 | 
			
		||||
  ALLOW_DENY = 1
 | 
			
		||||
 | 
			
		||||
  def initialize(list=nil, order = DENY_ALLOW)
 | 
			
		||||
    @order = order
 | 
			
		||||
    @deny = ACLList.new
 | 
			
		||||
    @allow = ACLList.new
 | 
			
		||||
    install_list(list) if list
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  public
 | 
			
		||||
  def allow_socket?(soc)
 | 
			
		||||
    allow_addr?(soc.peeraddr)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  public
 | 
			
		||||
  def allow_addr?(addr)
 | 
			
		||||
    case @order
 | 
			
		||||
    when DENY_ALLOW
 | 
			
		||||
      return true if @allow.match(addr)
 | 
			
		||||
      return false if @deny.match(addr)
 | 
			
		||||
      return true
 | 
			
		||||
    when ALLOW_DENY
 | 
			
		||||
      return false if @deny.match(addr)
 | 
			
		||||
      return true if @allow.match(addr)
 | 
			
		||||
      return false
 | 
			
		||||
    else
 | 
			
		||||
      false
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  public
 | 
			
		||||
  def install_list(list)
 | 
			
		||||
    i = 0
 | 
			
		||||
    while i < list.size
 | 
			
		||||
      permission, domain = list.slice(i,2)
 | 
			
		||||
      case permission.downcase
 | 
			
		||||
      when 'allow'
 | 
			
		||||
	@allow.add(domain)
 | 
			
		||||
      when 'deny'
 | 
			
		||||
	@deny.add(domain)
 | 
			
		||||
      else
 | 
			
		||||
	raise "Invalid ACL entry #{list.to_s}"
 | 
			
		||||
      end
 | 
			
		||||
      i += 2
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
if __FILE__ == $0
 | 
			
		||||
  # example
 | 
			
		||||
  list = %w(deny all
 | 
			
		||||
	    allow 192.168.1.1
 | 
			
		||||
            allow ::ffff:192.168.1.2
 | 
			
		||||
            allow 192.168.1.3
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
  addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
 | 
			
		||||
 | 
			
		||||
  acl = ACL.new
 | 
			
		||||
  p acl.allow_addr?(addr)
 | 
			
		||||
 | 
			
		||||
  acl = ACL.new(list, ACL::DENY_ALLOW)
 | 
			
		||||
  p acl.allow_addr?(addr)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										185
									
								
								lib/drb/ssl.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								lib/drb/ssl.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,185 @@
 | 
			
		|||
require 'socket'
 | 
			
		||||
require 'openssl'
 | 
			
		||||
require 'drb/drb'
 | 
			
		||||
require 'singleton'
 | 
			
		||||
 | 
			
		||||
module DRb
 | 
			
		||||
 | 
			
		||||
  class DRbSSLSocket < DRbTCPSocket
 | 
			
		||||
 | 
			
		||||
    class SSLConfig
 | 
			
		||||
 | 
			
		||||
      DEFAULT = {
 | 
			
		||||
	:SSLCertificate       => nil,
 | 
			
		||||
	:SSLPrivateKey        => nil,
 | 
			
		||||
	:SSLClientCA          => nil,
 | 
			
		||||
	:SSLCACertificatePath => nil,
 | 
			
		||||
	:SSLCACertificateFile => nil,
 | 
			
		||||
	:SSLVerifyMode        => ::OpenSSL::SSL::VERIFY_NONE, 
 | 
			
		||||
	:SSLVerifyDepth       => nil,
 | 
			
		||||
	:SSLVerifyCallback    => nil,   # custom verification
 | 
			
		||||
        :SSLCertificateStore  => nil,
 | 
			
		||||
	# Must specify if you use auto generated certificate.
 | 
			
		||||
	:SSLCertName          => nil,   # e.g. [["CN","fqdn.example.com"]]
 | 
			
		||||
	:SSLCertComment       => "Generated by Ruby/OpenSSL"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      def initialize(config)
 | 
			
		||||
	@config  = config
 | 
			
		||||
        @cert    = config[:SSLCertificate]
 | 
			
		||||
        @pkey    = config[:SSLPrivateKey]
 | 
			
		||||
        @ssl_ctx = nil
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def [](key); 
 | 
			
		||||
	@config[key] || DEFAULT[key]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def connect(tcp)
 | 
			
		||||
	ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
 | 
			
		||||
	ssl.sync = true
 | 
			
		||||
	ssl.connect
 | 
			
		||||
	ssl
 | 
			
		||||
      end
 | 
			
		||||
      
 | 
			
		||||
      def accept(tcp)
 | 
			
		||||
	ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
 | 
			
		||||
	ssl.sync = true
 | 
			
		||||
	ssl.accept
 | 
			
		||||
	ssl
 | 
			
		||||
      end
 | 
			
		||||
      
 | 
			
		||||
      def setup_certificate
 | 
			
		||||
        if @cert && @pkey
 | 
			
		||||
          return
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
	rsa = OpenSSL::PKey::RSA.new(512){|p, n|
 | 
			
		||||
	  next unless self[:verbose]
 | 
			
		||||
	  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(self[: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.issuer_certificate = cert
 | 
			
		||||
	cert.add_extension(ef.create_extension("authorityKeyIdentifier",
 | 
			
		||||
					       "keyid:always,issuer:always"))
 | 
			
		||||
	if comment = self[:SSLCertComment]
 | 
			
		||||
	  cert.add_extension(ef.create_extension("nsComment", comment))
 | 
			
		||||
	end
 | 
			
		||||
	cert.sign(rsa, OpenSSL::Digest::SHA1.new)
 | 
			
		||||
	
 | 
			
		||||
	@cert = cert
 | 
			
		||||
        @pkey = rsa
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def setup_ssl_context
 | 
			
		||||
        ctx = ::OpenSSL::SSL::SSLContext.new
 | 
			
		||||
        ctx.cert            = @cert
 | 
			
		||||
        ctx.key             = @pkey
 | 
			
		||||
	ctx.client_ca       = self[:SSLClientCA]
 | 
			
		||||
	ctx.ca_path         = self[:SSLCACertificatePath]
 | 
			
		||||
	ctx.ca_file         = self[:SSLCACertificateFile]
 | 
			
		||||
	ctx.verify_mode     = self[:SSLVerifyMode]
 | 
			
		||||
	ctx.verify_depth    = self[:SSLVerifyDepth]
 | 
			
		||||
	ctx.verify_callback = self[:SSLVerifyCallback]
 | 
			
		||||
        ctx.cert_store      = self[:SSLCertificateStore]
 | 
			
		||||
        @ssl_ctx = ctx
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.parse_uri(uri)
 | 
			
		||||
      if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
 | 
			
		||||
	host = $1
 | 
			
		||||
	port = $2.to_i
 | 
			
		||||
	option = $4
 | 
			
		||||
	[host, port, option]
 | 
			
		||||
      else
 | 
			
		||||
	raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
 | 
			
		||||
	raise(DRbBadURI, 'can\'t parse uri:' + uri)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.open(uri, config)
 | 
			
		||||
      host, port, option = parse_uri(uri)
 | 
			
		||||
      host.untaint
 | 
			
		||||
      port.untaint
 | 
			
		||||
      soc = TCPSocket.open(host, port)
 | 
			
		||||
      ssl_conf = SSLConfig::new(config)
 | 
			
		||||
      ssl_conf.setup_ssl_context
 | 
			
		||||
      ssl = ssl_conf.connect(soc)
 | 
			
		||||
      self.new(uri, ssl, ssl_conf, true)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.open_server(uri, config)
 | 
			
		||||
      uri = 'drbssl://:0' unless uri
 | 
			
		||||
      host, port, opt = parse_uri(uri)
 | 
			
		||||
      if host.size == 0
 | 
			
		||||
	soc = TCPServer.open(port)
 | 
			
		||||
	host = Socket.gethostname
 | 
			
		||||
      else
 | 
			
		||||
	soc = TCPServer.open(host, port)
 | 
			
		||||
      end
 | 
			
		||||
      port = soc.addr[1] if port == 0
 | 
			
		||||
      @uri = "drbssl://#{host}:#{port}"
 | 
			
		||||
      
 | 
			
		||||
      ssl_conf = SSLConfig.new(config)
 | 
			
		||||
      ssl_conf.setup_certificate
 | 
			
		||||
      ssl_conf.setup_ssl_context
 | 
			
		||||
      self.new(@uri, soc, ssl_conf, false)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.uri_option(uri, config)
 | 
			
		||||
      host, port, option = parse_uri(uri)
 | 
			
		||||
      return "drbssl://#{host}:#{port}", option
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def initialize(uri, soc, config, is_established)
 | 
			
		||||
      @ssl = is_established ? soc : nil
 | 
			
		||||
      super(uri, soc.to_io, config)
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    def stream; @ssl; end
 | 
			
		||||
 | 
			
		||||
    def close
 | 
			
		||||
      if @ssl
 | 
			
		||||
	@ssl.close
 | 
			
		||||
	@ssl = nil
 | 
			
		||||
      end
 | 
			
		||||
      super
 | 
			
		||||
    end
 | 
			
		||||
      
 | 
			
		||||
    def accept
 | 
			
		||||
      while true
 | 
			
		||||
	soc = @socket.accept
 | 
			
		||||
	break if (@acl ? @acl.allow_socket?(soc) : true) 
 | 
			
		||||
	soc.close
 | 
			
		||||
      end
 | 
			
		||||
      ssl = @config.accept(soc)
 | 
			
		||||
      self.class.new(uri, ssl, @config, true)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  DRbProtocol.add_protocol(DRbSSLSocket)
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue