Project

General

Profile

Bug #9750 ยป ssl_server_issue_example.rb

sstelfox (Sam Stelfox), 04/16/2014 05:39 PM

 
#!/usr/bin/env ruby

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

require 'openssl'
require 'socket'

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

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

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

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

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

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

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

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

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

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

ssl_context = OpenSSL::SSL::SSLContext.new
ssl_context.key = SERVER_KEY
ssl_context.cert = SERVER_CERT

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

ssl_sock.listen(5)

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

puts "Server ready"

# Standard simplified server loop
begin
ready_socket = IO.select([ssl_sock], nil, nil, 1)
if ready_socket
ready_socket[0].each do |s|
client = s.accept
client.write("Client successfully connected!\n")
client.close
end
end
rescue => e
puts e.message
puts e.backtrace
end while true

    (1-1/1)