1require_relative "utils"
2
3if defined?(OpenSSL)
4
5class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
6  def test_session_equals
7    session = OpenSSL::SSL::Session.new <<-SESSION
8-----BEGIN SSL SESSION PARAMETERS-----
9MIIDFgIBAQICAwEEAgA5BCCY3pW6iTkPoD5SENuztz/gZjhvey6XnHbsxd22k0Ol
10dgQw8uaN3hCRnlhoIKPWInCFzrp/tQsDRFs9jDjc9pwpy/oKHmJdQQMQA1g8FYnO
11gpdVoQYCBE52ikKiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
12AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
13eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA5MTkwMDE4MTBaFw0xMTA5MTkwMDQ4
14MTBaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
15LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
16gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
177JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
18GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
19DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQARC7GP7InX1t7VEXz2
20I8RI57S0/HSJL4fDIYP3zFpitHX1PZeo+7XuzMilvPjjBo/ky9Jzo8TYiY+N+JEz
21mY/A/zPA4ZsJ7KYj6/FEdIc/vRlS0CvsbClbNjw1jl/PoB2FLr2b3uuBcZEsyZeP
22yq154ijq37Ajf8K5Mi5FgshoP41BPtRPj+VVf61rv1IcEnNWdDCS6DR4XsaNC+zt
23G6AqCqkytIXWRuDw6n6vYLF3A/tn2sldLo7/scY0PMDNbo63O/LTxkDHmPhSkD68
248m9SsMeTR+RCiDEZWFPVcAH/8mDfi+5k8uN3qS+gOU/PPrmHGgl5ykiSFgqs4v61
25tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM=
26-----END SSL SESSION PARAMETERS-----
27    SESSION
28
29    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) { |_, port|
30      ctx = OpenSSL::SSL::SSLContext.new
31      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
32      ctx.session_id_context = self.object_id.to_s
33
34      sock = TCPSocket.new '127.0.0.1', port
35      ssl = OpenSSL::SSL::SSLSocket.new sock, ctx
36      ssl.session = session
37
38      assert_equal session, ssl.session
39      sock.close
40    }
41  end
42
43  def test_session
44    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
45      sock = TCPSocket.new("127.0.0.1", port)
46      ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
47      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
48      ssl.sync_close = true
49      ssl.connect
50      session = ssl.session
51      assert(session == OpenSSL::SSL::Session.new(session.to_pem))
52      assert(session == OpenSSL::SSL::Session.new(ssl))
53      assert_equal(300, session.timeout)
54      session.timeout = 5
55      assert_equal(5, session.timeout)
56      assert_not_nil(session.time)
57      # SSL_SESSION_time keeps long value so we can't keep nsec fragment.
58      session.time = t1 = Time.now.to_i
59      assert_equal(Time.at(t1), session.time)
60      if session.respond_to?(:id)
61        assert_not_nil(session.id)
62      end
63      pem = session.to_pem
64      assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem)
65      assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem)
66      pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
67      assert_equal(session.to_der, pem.unpack('m*')[0])
68      assert_not_nil(session.to_text)
69      ssl.close
70    end
71  end
72
73  DUMMY_SESSION = <<__EOS__
74-----BEGIN SSL SESSION PARAMETERS-----
75MIIDzQIBAQICAwEEAgA5BCAF219w9ZEV8dNA60cpEGOI34hJtIFbf3bkfzSgMyad
76MQQwyGLbkCxE4OiMLdKKem+pyh8V7ifoP7tCxhdmwoDlJxI1v6nVCjai+FGYuncy
77NNSWoQYCBE4DDWuiAwIBCqOCAo4wggKKMIIBcqADAgECAgECMA0GCSqGSIb3DQEB
78BQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
79LWxhbmcxCzAJBgNVBAMMAkNBMB4XDTExMDYyMzA5NTQ1MVoXDTExMDYyMzEwMjQ1
80MVowRDETMBEGCgmSJomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1Ynkt
81bGFuZzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
82iQKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7CxaKPERYHs
83k4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/Q3geLv8Z
84D9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQABoxIwEDAO
85BgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBACj5WhoZ/ODVeHpwgq1d
868fW/13ICRYHYpv6dzlWihyqclGxbKMlMnaVCPz+4JaVtMz3QB748KJQgL3Llg3R1
87ek+f+n1MBCMfFFsQXJ2gtLB84zD6UCz8aaCWN5/czJCd7xMz7fRLy3TOIW5boXAU
88zIa8EODk+477K1uznHm286ab0Clv+9d304hwmBZgkzLg6+31Of6d6s0E0rwLGiS2
89sOWYg34Y3r4j8BS9Ak4jzpoLY6cJ0QAKCOJCgmjGr4XHpyXMLbicp3ga1uSbwtVO
90gF/gTfpLhJC+y0EQ5x3Ftl88Cq7ZJuLBDMo/TLIfReJMQu/HlrTT7+LwtneSWGmr
91KkSkAgQApQMCAROqgcMEgcAuDkAVfj6QAJMz9yqTzW5wPFyty7CxUEcwKjUqj5UP
92/Yvky1EkRuM/eQfN7ucY+MUvMqv+R8ZSkHPsnjkBN5ChvZXjrUSZKFVjR4eFVz2V
93jismLEJvIFhQh6pqTroRrOjMfTaM5Lwoytr2FTGobN9rnjIRsXeFQW1HLFbXn7Dh
948uaQkMwIVVSGRB8T7t6z6WIdWruOjCZ6G5ASI5XoqAHwGezhLodZuvJEfsVyCF9y
95j+RBGfCFrrQbBdnkFI/ztgM=
96-----END SSL SESSION PARAMETERS-----
97__EOS__
98
99  DUMMY_SESSION_NO_EXT = <<-__EOS__
100-----BEGIN SSL SESSION PARAMETERS-----
101MIIDCAIBAQICAwAEAgA5BCDyAW7rcpzMjDSosH+Tv6sukymeqgq3xQVVMez628A+
102lAQw9TrKzrIqlHEh6ltuQaqv/Aq83AmaAlogYktZgXAjOGnhX7ifJDNLMuCfQq53
103hPAaoQYCBE4iDeeiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
104AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
105eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA3MTYyMjE3MTFaFw0xMTA3MTYyMjQ3
106MTFaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
107LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
108gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
1097JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
110GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
111DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQA3TRzABRG3kz8jEEYr
112tDQqXgsxwTsLhTT5d1yF0D8uFw+y15hJAJnh6GJHjqhWBrF4zNoTApFo+4iIL6g3
113q9C3mUsxIVAHx41DwZBh/FI7J4FqlAoGOguu7892CNVY3ZZjc3AXMTdKjcNoWPzz
114FCdj5fNT24JMMe+ZdGZK97ChahJsdn/6B3j6ze9NK9mfYEbiJhejGTPLOFVHJCGR
115KYYZ3ZcKhLDr9ql4d7cCo1gBtemrmFQGPui7GttNEqmXqUKvV8mYoa8farf5i7T4
116L6a/gp2cVZTaDIS1HjbJsA/Ag7AajZqiN6LfqShNUVsrMZ+5CoV8EkBDTZPJ9MSr
117a3EqpAIEAKUDAgET
118-----END SSL SESSION PARAMETERS-----
119__EOS__
120
121
122  def test_session_time
123    sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
124    sess.time = (now = Time.now)
125    assert_equal(now.to_i, sess.time.to_i)
126    sess.time = 1
127    assert_equal(1, sess.time.to_i)
128    sess.time = 1.2345
129    assert_equal(1, sess.time.to_i)
130    # Can OpenSSL handle t>2038y correctly? Version?
131    sess.time = 2**31 - 1
132    assert_equal(2**31 - 1, sess.time.to_i)
133  end
134
135  def test_session_timeout
136    sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
137    assert_raise(TypeError) do
138      sess.timeout = Time.now
139    end
140    sess.timeout = 1
141    assert_equal(1, sess.timeout.to_i)
142    sess.timeout = 1.2345
143    assert_equal(1, sess.timeout.to_i)
144    sess.timeout = 2**31 - 1
145    assert_equal(2**31 - 1, sess.timeout.to_i)
146  end
147
148  def test_session_exts_read
149    assert(OpenSSL::SSL::Session.new(DUMMY_SESSION))
150  end if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x009080bf
151
152  def test_client_session
153    last_session = nil
154    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
155      2.times do
156        sock = TCPSocket.new("127.0.0.1", port)
157        # Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
158        # when use default SSLContext. [ruby-dev:36167]
159        ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
160        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
161        ssl.sync_close = true
162        ssl.session = last_session if last_session
163        ssl.connect
164
165        session = ssl.session
166        if last_session
167          assert(ssl.session_reused?)
168
169          if session.respond_to?(:id)
170            assert_equal(session.id, last_session.id)
171          end
172          assert_equal(session.to_pem, last_session.to_pem)
173          assert_equal(session.to_der, last_session.to_der)
174          # Older version of OpenSSL may not be consistent.  Look up which versions later.
175          assert_equal(session.to_text, last_session.to_text)
176        else
177          assert(!ssl.session_reused?)
178        end
179        last_session = session
180
181        str = "x" * 100 + "\n"
182        ssl.puts(str)
183        assert_equal(str, ssl.gets)
184
185        ssl.close
186      end
187    end
188  end
189
190  def test_server_session
191    connections = 0
192    saved_session = nil
193
194    ctx_proc = Proc.new do |ctx, ssl|
195# add test for session callbacks here
196    end
197
198    server_proc = Proc.new do |ctx, ssl|
199      session = ssl.session
200      stats = ctx.session_cache_stats
201
202      case connections
203      when 0
204        assert_equal(stats[:cache_num], 1)
205        assert_equal(stats[:cache_hits], 0)
206        assert_equal(stats[:cache_misses], 0)
207        assert(!ssl.session_reused?)
208      when 1
209        assert_equal(stats[:cache_num], 1)
210        assert_equal(stats[:cache_hits], 1)
211        assert_equal(stats[:cache_misses], 0)
212        assert(ssl.session_reused?)
213        ctx.session_remove(session)
214        saved_session = session
215      when 2
216        assert_equal(stats[:cache_num], 1)
217        assert_equal(stats[:cache_hits], 1)
218        assert_equal(stats[:cache_misses], 1)
219        assert(!ssl.session_reused?)
220        ctx.session_add(saved_session)
221      when 3
222        assert_equal(stats[:cache_num], 2)
223        assert_equal(stats[:cache_hits], 2)
224        assert_equal(stats[:cache_misses], 1)
225        assert(ssl.session_reused?)
226        ctx.flush_sessions(Time.now + 5000)
227      when 4
228        assert_equal(stats[:cache_num], 1)
229        assert_equal(stats[:cache_hits], 2)
230        assert_equal(stats[:cache_misses], 2)
231        assert(!ssl.session_reused?)
232        ctx.session_add(saved_session)
233      end
234      connections += 1
235
236      readwrite_loop(ctx, ssl)
237    end
238
239    first_session = nil
240    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
241      10.times do |i|
242        sock = TCPSocket.new("127.0.0.1", port)
243        ctx = OpenSSL::SSL::SSLContext.new
244        if defined?(OpenSSL::SSL::OP_NO_TICKET)
245          # disable RFC4507 support
246          ctx.options = OpenSSL::SSL::OP_NO_TICKET
247        end
248        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
249        ssl.sync_close = true
250        ssl.session = first_session if first_session
251        ssl.connect
252
253        session = ssl.session
254        if first_session
255          case i
256          when 1; assert(ssl.session_reused?)
257          when 2; assert(!ssl.session_reused?)
258          when 3; assert(ssl.session_reused?)
259          when 4; assert(!ssl.session_reused?)
260          when 5..10; assert(ssl.session_reused?)
261          end
262        end
263        first_session ||= session
264
265        str = "x" * 100 + "\n"
266        ssl.puts(str)
267        assert_equal(str, ssl.gets)
268
269        ssl.close
270      end
271    end
272  end
273
274  def test_ctx_client_session_cb
275    called = {}
276    ctx = OpenSSL::SSL::SSLContext.new("SSLv3")
277    ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
278
279    ctx.session_new_cb = lambda { |ary|
280      sock, sess = ary
281      called[:new] = [sock, sess]
282    }
283
284    ctx.session_remove_cb = lambda { |ary|
285      ctx, sess = ary
286      called[:remove] = [ctx, sess]
287      # any resulting value is OK (ignored)
288    }
289
290    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
291      sock = TCPSocket.new("127.0.0.1", port)
292      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
293      ssl.sync_close = true
294      ssl.connect
295      assert_equal(1, ctx.session_cache_stats[:cache_num])
296      assert_equal(1, ctx.session_cache_stats[:connect_good])
297      assert_equal([ssl, ssl.session], called[:new])
298      assert(ctx.session_remove(ssl.session))
299      assert(!ctx.session_remove(ssl.session))
300      assert_equal([ctx, ssl.session], called[:remove])
301      ssl.close
302    end
303  end
304
305  def test_ctx_server_session_cb
306    called = {}
307
308    ctx_proc = Proc.new { |ctx, ssl|
309      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER
310      last_server_session = nil
311
312      # get_cb is called whenever a client proposed to resume a session but
313      # the session could not be found in the internal session cache.
314      ctx.session_get_cb = lambda { |ary|
315        sess, data = ary
316        if last_server_session
317          called[:get2] = [sess, data]
318          last_server_session
319        else
320          called[:get1] = [sess, data]
321          last_server_session = sess
322          nil
323        end
324      }
325
326      ctx.session_new_cb = lambda { |ary|
327        sock, sess = ary
328        called[:new] = [sock, sess]
329        # SSL server doesn't cache sessions so get_cb is called next time.
330        ctx.session_remove(sess)
331      }
332
333      ctx.session_remove_cb = lambda { |ary|
334        ctx, sess = ary
335        called[:remove] = [ctx, sess]
336      }
337    }
338
339    server_proc = Proc.new { |c, ssl|
340      ssl.session
341      c.session_cache_stats
342      readwrite_loop(c, ssl)
343    }
344    start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
345      last_client_session = nil
346      3.times do
347        sock = TCPSocket.new("127.0.0.1", port)
348        ssl = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new("SSLv3"))
349        ssl.sync_close = true
350        ssl.session = last_client_session if last_client_session
351        ssl.connect
352        last_client_session = ssl.session
353        ssl.close
354        timeout(5) do
355          Thread.pass until called.key?(:new)
356          assert(called.delete(:new))
357          Thread.pass until called.key?(:remove)
358          assert(called.delete(:remove))
359        end
360      end
361    end
362    assert(called[:get1])
363    assert(called[:get2])
364  end
365end
366
367end
368