ssl_server_issue_example.rb

Sam Stelfox, 04/16/2014 05:39 PM

Download (3.38 KB)

 
1
#!/usr/bin/env ruby
2

    
3
# This code was written by Sam Stelfox (http://www.stelfox.net/) and is
4
# licensed under GPLv2
5

    
6
require 'openssl'
7
require 'socket'
8

    
9
# This bug has been tested on:
10
#   ruby 2.2.0dev (2014-04-16 trunk 45603) [x86_64-linux]
11
#   ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]
12

    
13
# Too connect to this server once it is running you can use the following
14
# command (only tested on Linux):
15
#
16
#     openssl s_client -connect 127.0.0.1:4443
17

    
18
# This class is a work around for OpenSSL::SSL::SSLServer when a raw socket
19
# needs to be passed too it. The Ruby sample code I've been able to find for
20
# interacting with OpenSSL::SSL::SSLServer shows passing a TCPServer object
21
# which is not always ideal.
22
#
23
# TCPServer#accept only returns the socket file descriptor, while Socket#accept
24
# returns both the socket file descriptor and an Addrinfo object with
25
# information about the client connected.
26
class TweakedSSLServer < OpenSSL::SSL::SSLServer
27
  # This method was pulled in it's entirety out of trunk with one line adjusted
28
  # so it can handle both Socket and TCPServer.
29
  def accept
30
    sock, addrinfo = @svr.accept # This line was adjusted
31
    begin
32
      ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
33
      ssl.sync_close = true
34
      ssl.accept if @start_immediately
35
      ssl
36
    rescue SSLError => ex
37
      sock.close
38
      raise ex
39
    end
40
  end
41
end
42

    
43
# Helper method for generating a self-signed certificate so we're not dependent
44
# on outside files
45
def generate_cert(name, key)
46
  cert = OpenSSL::X509::Certificate.new
47
  cert.version = 2
48
  cert.serial = 1
49

    
50
  cert.not_before = (Time.now - 3600)
51
  cert.not_after = (Time.now + 3600)
52

    
53
  cert.subject = OpenSSL::X509::Name.parse("CN=#{name}")
54
  cert.issuer = cert.subject
55
  cert.public_key = key.public_key
56

    
57
  cert.sign(key, OpenSSL::Digest::SHA384.new)
58
end
59

    
60
# Sample non-production key & certificate. These are not relevant to the issue,
61
# but are required to setup the SSL context.
62
SERVER_KEY = OpenSSL::PKey::RSA.new(1024)
63
SERVER_CERT = generate_cert('example', SERVER_KEY)
64

    
65
sock = Socket.new(Socket::AF_INET6, Socket::SOCK_STREAM, 0)
66

    
67
sock.bind(Socket.pack_sockaddr_in(4443, '::'))
68

    
69
ssl_context = OpenSSL::SSL::SSLContext.new
70
ssl_context.key = SERVER_KEY
71
ssl_context.cert = SERVER_CERT
72

    
73
# Switch the comments on these lines to see the issue with the SSLServer code.
74
# When the regular SSLServer is used, this will break with the following error
75
# after a client attempts to connect:
76
#
77
#    wrong argument type Array (expected File)
78
#    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `initialize'
79
#    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `new'
80
#    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `accept'
81
#    test_ssl_server.rb:84:in `block in <main>'
82
#    test_ssl_server.rb:83:in `each'
83
#    test_ssl_server.rb:83:in `<main>'
84
#
85
ssl_sock = OpenSSL::SSL::SSLServer.new(sock, ssl_context)
86
#ssl_sock = TweakedSSLServer.new(sock, ssl_context)
87

    
88
ssl_sock.listen(5)
89

    
90
# Properly close the socket on a Ctrl-C before closing.
91
trap(:INT) { ssl_sock.close; exit }
92

    
93
puts "Server ready"
94

    
95
# Standard simplified server loop
96
begin
97
  ready_socket = IO.select([ssl_sock], nil, nil, 1)
98
  if ready_socket
99
    ready_socket[0].each do |s|
100
      client = s.accept
101
      client.write("Client successfully connected!\n")
102
      client.close
103
    end
104
  end
105
rescue => e
106
  puts e.message
107
  puts e.backtrace
108
end while true
109