|
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;
|
@@ -98,6 +99,10 @@ public class SSLContext extends RubyObject {
|
98 | 99 | private static final HashMap<String, String> SSL_VERSION_OSSL2JSSE;
|
99 | 100 | // Mapping table for JSEE's enabled protocols for the algorithm.
|
100 | 101 | private static final Map<String, String[]> ENABLED_PROTOCOLS;
|
| 102 | + // Mapping table from CRuby parse_proto_version(VALUE str) |
| 103 | + private static final Map<String, Integer> PROTO_VERSION_MAP; |
| 104 | + |
| 105 | + private static final Map<String, Integer> JSSE_TO_VERSION; |
101 | 106 |
|
102 | 107 | static {
|
103 | 108 | SSL_VERSION_OSSL2JSSE = new LinkedHashMap<String, String>(20, 1);
|
@@ -142,6 +147,22 @@ public class SSLContext extends RubyObject {
|
142 | 147 | SSL_VERSION_OSSL2JSSE.put("TLSv1.2", "TLSv1.2"); // just for completeness
|
143 | 148 | SSL_VERSION_OSSL2JSSE.put("TLSv1_2_server", "TLSv1.2");
|
144 | 149 | SSL_VERSION_OSSL2JSSE.put("TLSv1_2_client", "TLSv1.2");
|
| 150 | + |
| 151 | + PROTO_VERSION_MAP = new HashMap<String, Integer>(); |
| 152 | + PROTO_VERSION_MAP.put("SSL2", SSL.SSL2_VERSION); |
| 153 | + PROTO_VERSION_MAP.put("SSL3", SSL.SSL3_VERSION); |
| 154 | + PROTO_VERSION_MAP.put("TLS1", SSL.TLS1_VERSION); |
| 155 | + PROTO_VERSION_MAP.put("TLS1_1", SSL.TLS1_1_VERSION); |
| 156 | + PROTO_VERSION_MAP.put("TLS1_2", SSL.TLS1_2_VERSION); |
| 157 | + PROTO_VERSION_MAP.put("TLS1_3", SSL.TLS1_3_VERSION); |
| 158 | + |
| 159 | + JSSE_TO_VERSION = new HashMap<String, Integer>(); |
| 160 | + JSSE_TO_VERSION.put("SSLv2", SSL.SSL2_VERSION); |
| 161 | + JSSE_TO_VERSION.put("SSLv3", SSL.SSL3_VERSION); |
| 162 | + JSSE_TO_VERSION.put("TLSv1", SSL.TLS1_VERSION); |
| 163 | + JSSE_TO_VERSION.put("TLSv1.1", SSL.TLS1_1_VERSION); |
| 164 | + JSSE_TO_VERSION.put("TLSv1.2", SSL.TLS1_2_VERSION); |
| 165 | + JSSE_TO_VERSION.put("TLSv1.3", SSL.TLS1_3_VERSION); |
145 | 166 | }
|
146 | 167 |
|
147 | 168 | private static ObjectAllocator SSLCONTEXT_ALLOCATOR = new ObjectAllocator() {
|
@@ -270,6 +291,8 @@ public SSLContext(Ruby runtime, RubyClass type) {
|
270 | 291 | private String protocol = "SSL"; // SSLv23 in OpenSSL by default
|
271 | 292 | private boolean protocolForServer = true;
|
272 | 293 | private boolean protocolForClient = true;
|
| 294 | + private int minProtocolVersion = 0; |
| 295 | + private int maxProtocolVersion = 0; |
273 | 296 | private PKey t_key;
|
274 | 297 | private X509Cert t_cert;
|
275 | 298 |
|
@@ -464,7 +487,7 @@ public RubyArray ciphers(final ThreadContext context) {
|
464 | 487 | private RubyArray matchedCiphers(final ThreadContext context) {
|
465 | 488 | final Ruby runtime = context.runtime;
|
466 | 489 | try {
|
467 |
| - final String[] supported = getSupportedCipherSuites(this.protocol); |
| 490 | + final String[] supported = getSupportedCipherSuites(protocol); |
468 | 491 | final Collection<CipherStrings.Def> cipherDefs =
|
469 | 492 | CipherStrings.matchingCiphers(this.ciphers, supported, false);
|
470 | 493 |
|
@@ -527,6 +550,31 @@ public IRubyObject set_ssl_version(IRubyObject version) {
|
527 | 550 | return version;
|
528 | 551 | }
|
529 | 552 |
|
| 553 | + @JRubyMethod(name = "set_minmax_proto_version") |
| 554 | + public IRubyObject set_minmax_proto_version(ThreadContext context, IRubyObject minVersion, IRubyObject maxVersion) { |
| 555 | + minProtocolVersion = parseProtoVersion(minVersion); |
| 556 | + maxProtocolVersion = parseProtoVersion(maxVersion); |
| 557 | + |
| 558 | + return context.nil; |
| 559 | + } |
| 560 | + |
| 561 | + private int parseProtoVersion(IRubyObject version) { |
| 562 | + if (version.isNil()) |
| 563 | + return 0; |
| 564 | + if (version instanceof RubyFixnum) { |
| 565 | + return RubyFixnum.fix2int(version); |
| 566 | + } |
| 567 | + |
| 568 | + String string = version.asString().asJavaString(); |
| 569 | + Integer sslVersion = PROTO_VERSION_MAP.get(string); |
| 570 | + |
| 571 | + if (sslVersion == null) { |
| 572 | + throw getRuntime().newArgumentError("unrecognized version \"" + string + "\""); |
| 573 | + } |
| 574 | + |
| 575 | + return sslVersion; |
| 576 | + } |
| 577 | + |
530 | 578 | final String getProtocol() { return this.protocol; }
|
531 | 579 |
|
532 | 580 | @JRubyMethod(name = "session_cache_mode")
|
@@ -651,6 +699,10 @@ private String[] getEnabledProtocols(final SSLEngine engine) {
|
651 | 699 | final String[] engineProtocols = engine.getEnabledProtocols();
|
652 | 700 | final List<String> protocols = new ArrayList<String>(enabledProtocols.length);
|
653 | 701 | for ( final String enabled : enabledProtocols ) {
|
| 702 | + int protocolVersion = JSSE_TO_VERSION.get(enabled); |
| 703 | + if (minProtocolVersion != 0 && protocolVersion < minProtocolVersion) continue; |
| 704 | + if (maxProtocolVersion != 0 && protocolVersion > maxProtocolVersion) continue; |
| 705 | + |
654 | 706 | if (((options & OP_NO_SSLv2) != 0) && enabled.equals("SSLv2")) continue;
|
655 | 707 | if (((options & OP_NO_SSLv3) != 0) && enabled.equals("SSLv3")) continue;
|
656 | 708 | if (((options & OP_NO_TLSv1) != 0) && enabled.equals("TLSv1")) continue;
|
|
0 commit comments