@@ -722,6 +722,21 @@ class << self
722
722
# The port this client connected to
723
723
attr_reader :port
724
724
725
+ # Returns the
726
+ # {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
727
+ # used by the SSLSocket when TLS is attempted, even when the TLS handshake
728
+ # is unsuccessful. The context object will be frozen.
729
+ #
730
+ # Returns +nil+ for a plaintext connection.
731
+ attr_reader :ssl_ctx
732
+
733
+ # Returns the parameters that were sent to #ssl_ctx
734
+ # {set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params]
735
+ # when the connection tries to use TLS (even when unsuccessful).
736
+ #
737
+ # Returns +false+ for a plaintext connection.
738
+ attr_reader :ssl_ctx_params
739
+
725
740
# Creates a new Net::IMAP object and connects it to the specified
726
741
# +host+.
727
742
#
@@ -804,7 +819,7 @@ def initialize(host, options = {}, *deprecated)
804
819
@port = options [ :port ] || ( options [ :ssl ] ? SSL_PORT : PORT )
805
820
@open_timeout = options [ :open_timeout ] || 30
806
821
@idle_response_timeout = options [ :idle_response_timeout ] || 5
807
- ssl_ctx_params = options [ :ssl ]
822
+ @ ssl_ctx_params, @ssl_ctx = build_ssl_ctx ( options [ :ssl ] )
808
823
809
824
# Basic Client State
810
825
@utf8_strings = false
@@ -835,7 +850,8 @@ def initialize(host, options = {}, *deprecated)
835
850
# Connection
836
851
@tls_verified = false
837
852
@sock = tcp_socket ( @host , @port )
838
- start_imap_connection ( ssl_ctx_params )
853
+ start_tls_session if ssl_ctx
854
+ start_imap_connection
839
855
840
856
# DEPRECATED: to remove in next version
841
857
@client_thread = Thread . current
@@ -1083,17 +1099,18 @@ def logout
1083
1099
# Cached #capabilities will be cleared when this method completes.
1084
1100
#
1085
1101
def starttls ( options = { } , verify = true )
1102
+ begin
1103
+ # for backward compatibility
1104
+ certs = options . to_str
1105
+ options = create_ssl_params ( certs , verify )
1106
+ rescue NoMethodError
1107
+ end
1108
+ @ssl_ctx_params , @ssl_ctx = build_ssl_ctx ( options || { } )
1086
1109
send_command ( "STARTTLS" ) do |resp |
1087
1110
if resp . kind_of? ( TaggedResponse ) && resp . name == "OK"
1088
- begin
1089
- # for backward compatibility
1090
- certs = options . to_str
1091
- options = create_ssl_params ( certs , verify )
1092
- rescue NoMethodError
1093
- end
1094
1111
clear_cached_capabilities
1095
1112
clear_responses
1096
- start_tls_session ( options )
1113
+ start_tls_session
1097
1114
end
1098
1115
end
1099
1116
end
@@ -2351,8 +2368,7 @@ def convert_deprecated_options(
2351
2368
options
2352
2369
end
2353
2370
2354
- def start_imap_connection ( ssl_ctx_params )
2355
- start_tls_session ( ssl_ctx_params ) if ssl_ctx_params
2371
+ def start_imap_connection
2356
2372
@greeting = get_server_greeting
2357
2373
@capabilities = capabilities_from_resp_code @greeting
2358
2374
@receiver_thread = start_receiver_thread
@@ -2664,6 +2680,21 @@ def normalize_searching_criteria(keys)
2664
2680
end
2665
2681
end
2666
2682
2683
+ def build_ssl_ctx ( ssl )
2684
+ if ssl
2685
+ params = ( Hash . try_convert ( ssl ) || { } ) . freeze
2686
+ context = SSLContext . new
2687
+ context . set_params ( params )
2688
+ if defined? ( VerifyCallbackProc )
2689
+ context . verify_callback = VerifyCallbackProc
2690
+ end
2691
+ context . freeze
2692
+ [ params , context ]
2693
+ else
2694
+ false
2695
+ end
2696
+ end
2697
+
2667
2698
def create_ssl_params ( certs = nil , verify = true )
2668
2699
params = { }
2669
2700
if certs
@@ -2681,28 +2712,15 @@ def create_ssl_params(certs = nil, verify = true)
2681
2712
return params
2682
2713
end
2683
2714
2684
- def start_tls_session ( params = { } )
2685
- unless defined? ( OpenSSL ::SSL )
2686
- raise "SSL extension not installed"
2687
- end
2688
- if @sock . kind_of? ( OpenSSL ::SSL ::SSLSocket )
2689
- raise RuntimeError , "already using SSL"
2690
- end
2691
- begin
2692
- params = params . to_hash
2693
- rescue NoMethodError
2694
- params = { }
2695
- end
2696
- context = SSLContext . new
2697
- context . set_params ( params )
2698
- if defined? ( VerifyCallbackProc )
2699
- context . verify_callback = VerifyCallbackProc
2700
- end
2701
- @sock = SSLSocket . new ( @sock , context )
2715
+ def start_tls_session
2716
+ raise "SSL extension not installed" unless defined? ( OpenSSL ::SSL )
2717
+ raise "already using SSL" if @sock . kind_of? ( OpenSSL ::SSL ::SSLSocket )
2718
+ raise "cannot start TLS without SSLContext" unless ssl_ctx
2719
+ @sock = SSLSocket . new ( @sock , ssl_ctx )
2702
2720
@sock . sync_close = true
2703
2721
@sock . hostname = @host if @sock . respond_to? :hostname=
2704
2722
ssl_socket_connect ( @sock , @open_timeout )
2705
- if context . verify_mode != VERIFY_NONE
2723
+ if ssl_ctx . verify_mode != VERIFY_NONE
2706
2724
@sock . post_connection_check ( @host )
2707
2725
@tls_verified = true
2708
2726
end
0 commit comments