53
53
import org .jruby .Ruby ;
54
54
import org .jruby .RubyArray ;
55
55
import org .jruby .RubyClass ;
56
+ import org .jruby .RubyFixnum ;
56
57
import org .jruby .RubyHash ;
57
58
import org .jruby .RubyInteger ;
58
59
import org .jruby .RubyModule ;
@@ -96,31 +97,44 @@ public class SSLContext extends RubyObject {
96
97
97
98
// Mapping table for OpenSSL's SSL_METHOD -> JSSE's SSLContext algorithm.
98
99
private static final HashMap <String , String > SSL_VERSION_OSSL2JSSE ;
100
+ // Inverse mapping (incomplete, does not map 1-1 with SSL_VERSION_OSSL2JSSE)
101
+ private static final HashMap <String , String > SSL_VERSION_OSSL2JSSE_INV ;
99
102
// Mapping table for JSEE's enabled protocols for the algorithm.
100
103
private static final Map <String , String []> ENABLED_PROTOCOLS ;
104
+ // Mapping table from CRuby parse_proto_version(VALUE str)
105
+ private static final Map <String , Integer > PROTO_VERSION_MAP ;
106
+ // same as METHODS_MAP from ssl.rb
107
+ private static final Map <String , Integer > METHODS_MAP ;
108
+ private static final Map <Integer , String > METHODS_MAP_INV ;
101
109
102
110
static {
103
111
SSL_VERSION_OSSL2JSSE = new LinkedHashMap <String , String >(20 , 1 );
104
112
ENABLED_PROTOCOLS = new HashMap <String , String []>(8 , 1 );
105
113
114
+ SSL_VERSION_OSSL2JSSE_INV = new HashMap <String , String >();
115
+
106
116
SSL_VERSION_OSSL2JSSE .put ("TLSv1" , "TLSv1" );
107
117
SSL_VERSION_OSSL2JSSE .put ("TLSv1_server" , "TLSv1" );
108
118
SSL_VERSION_OSSL2JSSE .put ("TLSv1_client" , "TLSv1" );
109
119
ENABLED_PROTOCOLS .put ("TLSv1" , new String [] { "TLSv1" });
120
+ SSL_VERSION_OSSL2JSSE_INV .put ("TLSv1" , "TLSv1_1" );
110
121
111
122
SSL_VERSION_OSSL2JSSE .put ("SSLv2" , "SSLv2" );
112
123
SSL_VERSION_OSSL2JSSE .put ("SSLv2_server" , "SSLv2" );
113
124
SSL_VERSION_OSSL2JSSE .put ("SSLv2_client" , "SSLv2" );
114
125
ENABLED_PROTOCOLS .put ("SSLv2" , new String [] { "SSLv2" });
126
+ SSL_VERSION_OSSL2JSSE_INV .put ("SSLv2" , "SSLv2" );
115
127
116
128
SSL_VERSION_OSSL2JSSE .put ("SSLv3" , "SSLv3" );
117
129
SSL_VERSION_OSSL2JSSE .put ("SSLv3_server" , "SSLv3" );
118
130
SSL_VERSION_OSSL2JSSE .put ("SSLv3_client" , "SSLv3" );
119
131
ENABLED_PROTOCOLS .put ("SSLv3" , new String [] { "SSLv3" });
132
+ SSL_VERSION_OSSL2JSSE_INV .put ("SSLv3" , "SSLv3" );
120
133
121
134
SSL_VERSION_OSSL2JSSE .put ("SSLv23" , "SSL" );
122
135
SSL_VERSION_OSSL2JSSE .put ("SSLv23_server" , "SSL" );
123
136
SSL_VERSION_OSSL2JSSE .put ("SSLv23_client" , "SSL" );
137
+ SSL_VERSION_OSSL2JSSE_INV .put ("SSL" , "SSLv23" );
124
138
125
139
ENABLED_PROTOCOLS .put ("SSL" , new String [] { "SSLv2" , "SSLv3" , "TLSv1" , "TLSv1.1" , "TLSv1.2" });
126
140
@@ -138,10 +152,34 @@ public class SSLContext extends RubyObject {
138
152
SSL_VERSION_OSSL2JSSE .put ("TLSv1_1" , "TLSv1.1" ); // supported on MRI 2.x
139
153
SSL_VERSION_OSSL2JSSE .put ("TLSv1_2" , "TLSv1.2" ); // supported on MRI 2.x
140
154
ENABLED_PROTOCOLS .put ("TLSv1.2" , new String [] { "TLSv1.2" });
155
+ SSL_VERSION_OSSL2JSSE_INV .put ("TLSv1.1" , "TLSv1_1" );
156
+ SSL_VERSION_OSSL2JSSE_INV .put ("TLSv1.2" , "TLSv1_2" );
141
157
142
158
SSL_VERSION_OSSL2JSSE .put ("TLSv1.2" , "TLSv1.2" ); // just for completeness
143
159
SSL_VERSION_OSSL2JSSE .put ("TLSv1_2_server" , "TLSv1.2" );
144
160
SSL_VERSION_OSSL2JSSE .put ("TLSv1_2_client" , "TLSv1.2" );
161
+
162
+ PROTO_VERSION_MAP = new HashMap <String , Integer >();
163
+ PROTO_VERSION_MAP .put ("SSL2" , SSL .SSL2_VERSION );
164
+ PROTO_VERSION_MAP .put ("SSL3" , SSL .SSL3_VERSION );
165
+ PROTO_VERSION_MAP .put ("TLS1" , SSL .TLS1_VERSION );
166
+ PROTO_VERSION_MAP .put ("TLS1_1" , SSL .TLS1_1_VERSION );
167
+ PROTO_VERSION_MAP .put ("TLS1_2" , SSL .TLS1_2_VERSION );
168
+ PROTO_VERSION_MAP .put ("TLS1_3" , SSL .TLS1_3_VERSION );
169
+
170
+ METHODS_MAP = new HashMap <String , Integer >();
171
+ METHODS_MAP .put ("SSLv23" , 0 );
172
+ METHODS_MAP .put ("SSLv2" , SSL .SSL2_VERSION );
173
+ METHODS_MAP .put ("SSLv3" , SSL .SSL3_VERSION );
174
+ METHODS_MAP .put ("TLSv1" , SSL .TLS1_VERSION );
175
+ METHODS_MAP .put ("TLSv1_1" , SSL .TLS1_1_VERSION );
176
+ METHODS_MAP .put ("TLSv1_2" , SSL .TLS1_2_VERSION );
177
+ METHODS_MAP .put ("TLSv1_3" , SSL .TLS1_3_VERSION );
178
+
179
+ METHODS_MAP_INV = new HashMap <Integer , String >();
180
+ for (Map .Entry <String , Integer > e : METHODS_MAP .entrySet ()) {
181
+ METHODS_MAP_INV .put (e .getValue (), e .getKey ());
182
+ }
145
183
}
146
184
147
185
private static ObjectAllocator SSLCONTEXT_ALLOCATOR = new ObjectAllocator () {
@@ -267,9 +305,14 @@ public SSLContext(Ruby runtime, RubyClass type) {
267
305
}
268
306
269
307
private String ciphers = CipherStrings .SSL_DEFAULT_CIPHER_LIST ;
270
- private String protocol = "SSL" ; // SSLv23 in OpenSSL by default
308
+ private static final String DEFAULT_PROTOCOL = "SSL" ; // SSLv23 in OpenSSL by default
309
+ private static final int DEFAULT_PROTOCOL_VERSION = 0 ;
310
+ private String protocol = null ;
311
+ private Integer protocolVersion = null ;
271
312
private boolean protocolForServer = true ;
272
313
private boolean protocolForClient = true ;
314
+ private int minProtocolVersion = 0 ;
315
+ private int maxProtocolVersion = 0 ;
273
316
private PKey t_key ;
274
317
private X509Cert t_cert ;
275
318
@@ -446,6 +489,8 @@ public IRubyObject setup(final ThreadContext context) {
446
489
}
447
490
*/
448
491
492
+ setupProtocolVersion (context );
493
+
449
494
try {
450
495
internalContext = createInternalContext (context , cert , key , store , clientCert , extraChainCert , verifyMode , timeout );
451
496
}
@@ -456,6 +501,37 @@ public IRubyObject setup(final ThreadContext context) {
456
501
return runtime .getTrue ();
457
502
}
458
503
504
+ private void setupProtocolVersion (final ThreadContext context ) {
505
+ if (protocolVersion == null ) {
506
+ String ssl_version = null ;
507
+ if (maxProtocolVersion != 0 ) {
508
+ ssl_version = METHODS_MAP_INV .get (maxProtocolVersion );
509
+ set_ssl_version (ssl_version );
510
+ } else {
511
+ ssl_version = getMaxSupportedProtocolVersion ();
512
+ set_ssl_version (ssl_version );
513
+ }
514
+ }
515
+
516
+ if (minProtocolVersion !=0 || maxProtocolVersion !=0 ) {
517
+ if (minProtocolVersion != 0 && protocolVersion < minProtocolVersion ) {
518
+ throw newSSLError (context .runtime , "no protocols available" );
519
+ }
520
+ if (maxProtocolVersion != 0 && protocolVersion > maxProtocolVersion ) {
521
+ throw newSSLError (context .runtime , "no protocols available" );
522
+ }
523
+ }
524
+ }
525
+
526
+ private String getMaxSupportedProtocolVersion () {
527
+ //TODO: probably needs to be computed from
528
+ //javax.net.ssl.SSLContext.getSupportedSSLParameters().getProtocols()
529
+ //some changes prob. needed in dummySSLEngine and SecurityHelper.getSSLContext
530
+ //Hardcoding now.
531
+ //Nonetheless, TLS1.3 needs more changes in jruby-openssl
532
+ return "TLSv1_2" ;
533
+ }
534
+
459
535
@ JRubyMethod
460
536
public RubyArray ciphers (final ThreadContext context ) {
461
537
return matchedCiphers (context );
@@ -464,7 +540,8 @@ public RubyArray ciphers(final ThreadContext context) {
464
540
private RubyArray matchedCiphers (final ThreadContext context ) {
465
541
final Ruby runtime = context .runtime ;
466
542
try {
467
- final String [] supported = getSupportedCipherSuites (this .protocol );
543
+ setupProtocolVersion (context );
544
+ final String [] supported = getSupportedCipherSuites (protocol );
468
545
final Collection <CipherStrings .Def > cipherDefs =
469
546
CipherStrings .matchingCiphers (this .ciphers , supported , false );
470
547
@@ -517,17 +594,47 @@ public IRubyObject set_ssl_version(IRubyObject version) {
517
594
} else {
518
595
versionStr = version .convertToString ().toString ();
519
596
}
597
+ set_ssl_version (versionStr );
598
+ return version ;
599
+ }
600
+
601
+ private void set_ssl_version (final String versionStr ) {
520
602
final String protocol = SSL_VERSION_OSSL2JSSE .get (versionStr );
521
603
if ( protocol == null ) {
522
604
throw getRuntime ().newArgumentError ("unknown SSL method `" + versionStr +"'" );
523
605
}
524
606
this .protocol = protocol ;
607
+ this .protocolVersion = METHODS_MAP .get (versionStr );
525
608
protocolForServer = ! versionStr .endsWith ("_client" );
526
609
protocolForClient = ! versionStr .endsWith ("_server" );
527
- return version ;
528
610
}
529
611
530
- final String getProtocol () { return this .protocol ; }
612
+ @ JRubyMethod (name = "set_minmax_proto_version" )
613
+ public IRubyObject set_minmax_proto_version (ThreadContext context , IRubyObject minVersion , IRubyObject maxVersion ) {
614
+ minProtocolVersion = parseProtoVersion (minVersion );
615
+ maxProtocolVersion = parseProtoVersion (maxVersion );
616
+
617
+ return context .nil ;
618
+ }
619
+
620
+ private int parseProtoVersion (IRubyObject version ) {
621
+ if (version .isNil ())
622
+ return 0 ;
623
+ if (version instanceof RubyFixnum ) {
624
+ return (int ) ((RubyFixnum ) version ).getLongValue ();
625
+ }
626
+
627
+ String string = version .asString ().asJavaString ();
628
+ Integer sslVersion = PROTO_VERSION_MAP .get (string );
629
+
630
+ if (sslVersion == null ) {
631
+ throw getRuntime ().newArgumentError ("unrecognized version \" " + string + "\" " );
632
+ }
633
+
634
+ return sslVersion ;
635
+ }
636
+
637
+ final String getProtocol () { return this .protocol ; }
531
638
532
639
@ JRubyMethod (name = "session_cache_mode" )
533
640
public IRubyObject session_cache_mode () {
0 commit comments