Module: Arachni::RPC::EM::SSL

Includes:
ConnectionUtilities
Included in:
Protocol
Defined in:
lib/arachni/rpc/em/ssl.rb

Overview

Adds support for SSL and peer verification.

To be included by EventMachine::Connection classes.

@author: Tasos “Zapotek” Laskos <[email protected]>

Instance Method Summary collapse

Methods included from ConnectionUtilities

#peer_ip_addr

Instance Method Details

#are_we_a_client?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/arachni/rpc/em/ssl.rb', line 157

def are_we_a_client?
    @opts[:role] == :client
end

#ca_storeOpenSSL::X509::Store

Returns certificate store.

Returns:

  • (OpenSSL::X509::Store)

    certificate store



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/arachni/rpc/em/ssl.rb', line 97

def ca_store
    if !@ca_store
        if file = @opts[:ssl_ca]
            @ca_store = OpenSSL::X509::Store.new
            @ca_store.add_file( file )
        else
            fail "No CA certificate has been provided."
        end
    end

    @ca_store
end

#end_sslObject

Cleans up any SSL related resources.



76
77
# File 'lib/arachni/rpc/em/ssl.rb', line 76

def end_ssl
end

#log(severity, progname, msg) ⇒ Object

To be implemented by the parent.

By default, it will ‘warn’ if the severity is :error and will ‘raise’ if the severity if :fatal.

Parameters:

  • severity (Symbol)

    :fatal, :error, :warn, :info, :debug

  • progname (String)

    name of the component that performed the action

  • msg (String)

    message to log



89
90
91
92
# File 'lib/arachni/rpc/em/ssl.rb', line 89

def log( severity, progname, msg )
    warn "#{progname}: #{msg}" if severity == :error
    fail "#{progname}: #{msg}" if severity == :fatal
end

#ssl_handshake_completedObject

Checks for an appropriate server cert hostname if run from the client-side.

Does nothing when on the server-side.



146
147
148
149
150
151
152
153
154
155
# File 'lib/arachni/rpc/em/ssl.rb', line 146

def ssl_handshake_completed
    return if !are_we_a_client? || !ssl_opts? ||
        OpenSSL::SSL.verify_certificate_identity( @last_seen_cert, @opts[:host] )

    log( :error, 'SSL',
         "The hostname '#{@opts[:host]}' does not match the server certificate."
    )

    close_connection
end

#ssl_opts?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/arachni/rpc/em/ssl.rb', line 161

def ssl_opts?
    @opts[:ssl_ca] && @opts[:ssl_pkey] && @opts[:ssl_cert]
end

#ssl_verify_peer(cert_string) ⇒ Object

Verifies the peer cert based on the #ca_store.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/arachni/rpc/em/ssl.rb', line 115

def ssl_verify_peer( cert_string )
    cert = OpenSSL::X509::Certificate.new( cert_string )

    # Some servers send the same certificate multiple times. I'm not even
    # joking... (gmail.com)
    return true if cert == @last_seen_cert

    if ca_store.verify( cert )
        @last_seen_cert = cert

        # A server may send the root certificate, which we already have and thus
        # should not be added to the store again.
        ca_store.add_cert( @last_seen_cert ) if !@last_seen_cert.root?

        @verified_peer = true
        true
    else
        log( :error, 'SSL',
            "#{ca_store.error_string.capitalize} ['#{peer_ip_addr}']."
        )
        false
    end
end

#start_sslObject

Starts SSL with the supplied keys, certs etc.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/arachni/rpc/em/ssl.rb', line 51

def start_ssl
    @verified_peer = false
    @ssl_requested = true

    ssl_opts = {}
    if ssl_opts?
        ssl_opts = {
            private_key_file: @opts[:ssl_pkey],
            cert_chain_file:  @opts[:ssl_cert],
            verify_peer:      true
        }
        @last_seen_cert = nil
    end

    # ap ssl_opts
    start_tls( ssl_opts )
end

#verified_peer?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/arachni/rpc/em/ssl.rb', line 69

def verified_peer?
    @verified_peer
end