/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2006 Ola Bini <ola@ologix.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.openssl; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyModule; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyModule; import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.runtime.Block; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.SafePropertyAccessor; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class SSL { public static final int VERIFY_NONE = 0x00; public static final int VERIFY_PEER = 0x01; public static final int VERIFY_FAIL_IF_NO_PEER_CERT = 0x02; public static final int VERIFY_CLIENT_ONCE = 0x04; public static final long OP_ALL = 0x00000FFFL; public static final long OP_NO_TICKET = 0x00004000L; public static final long OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L; public static final long OP_SINGLE_ECDH_USE = 0x00080000L; public static final long OP_SINGLE_DH_USE = 0x00100000L; public static final long OP_EPHEMERAL_RSA = 0x00200000L; public static final long OP_CIPHER_SERVER_PREFERENCE = 0x00400000L; public static final long OP_TLS_ROLLBACK_BUG = 0x00800000L; public static final long OP_NO_SSLv2 = 0x01000000L; // supported public static final long OP_NO_SSLv3 = 0x02000000L; // supported public static final long OP_NO_TLSv1 = 0x04000000L; // supported public static final long OP_PKCS1_CHECK_1 = 0x08000000L; public static final long OP_PKCS1_CHECK_2 = 0x10000000L; public static final long OP_NETSCAPE_CA_DN_BUG = 0x20000000L; public static final long OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000L; private static final String JSSE_TLS_ephemeralDHKeySize = "jdk.tls.ephemeralDHKeySize" ; private static final String JSSE_TLS_ephemeralDHKeySize_default = "matched" ; private static final String JSSE_TLS_disabledAlgorithms = "jdk.tls.disabledAlgorithms" ; private static final String JSSE_TLS_disabledAlgorithms_default = "SSLv3, DHE" ; static { configureJSSE(); } private static void configureJSSE() { if ( OpenSSL.javaVersion8(true) ) { // >= 1.8 try { if ( System.getProperty(JSSE_TLS_ephemeralDHKeySize) == null ) { // The key size is the same as the authentication certificate, // but must be between 1024 bits and 2048 bits, inclusively. // However, the SunJCE provider only supports 2048-bit DH keys larger // than 1024 bits. Consequently, you may use the values 1024 or 2048 only. System.setProperty(JSSE_TLS_ephemeralDHKeySize, JSSE_TLS_ephemeralDHKeySize_default); } } catch (SecurityException ex) { OpenSSL.debug("setting " + JSSE_TLS_ephemeralDHKeySize + " failed: " + ex); } } else { // on JDK 7 DHE is weak - disable completely (unless user-set) try { if ( System.getProperty(JSSE_TLS_disabledAlgorithms) == null ) { System.setProperty(JSSE_TLS_disabledAlgorithms, JSSE_TLS_disabledAlgorithms_default); } } catch (SecurityException se) { OpenSSL.debug("setting " + JSSE_TLS_disabledAlgorithms + " failed: " + se); } } } static RaiseException handleCouldNotGenerateDHKeyPairError(final Ruby runtime, final RuntimeException ex) { String message = ex.getMessage(); if ( OpenSSL.javaHotSpot() || OpenSSL.javaOpenJDK() ) { if ( OpenSSL.javaVersion8(false) ) { // == 1.8 message += " (try disabling DHE using -D"+ JSSE_TLS_disabledAlgorithms +" as only keys of size 1024/2048 are supported in Java 8)"; } else if ( ! OpenSSL.javaVersion8(true) ) { // < 1.8 message += " (try disabling DHE using -D"+ JSSE_TLS_disabledAlgorithms +" as prior to Java 8 only keys of size < 1024 are supported)"; } } return newSSLError(runtime, message, ex); } public static void createSSL(final Ruby runtime, final RubyModule OpenSSL) { final RubyModule SSL = OpenSSL.defineModuleUnder("SSL"); final RubyClass OpenSSLError = OpenSSL.getClass("OpenSSLError"); final RubyClass SSLError = SSL.defineClassUnder("SSLError", OpenSSLError, OpenSSLError.getAllocator()); final IRubyObject WaitReadable = runtime.getIO().getConstantAt("WaitReadable"); if ( WaitReadable != null ) { // since 2.0 (do not exist in 1.8 / 1.9) SSL.defineClassUnder("SSLErrorWaitReadable", SSLError, OpenSSLError.getAllocator()). include(new IRubyObject[]{ WaitReadable }); } final IRubyObject WaitWritable = runtime.getIO().getConstantAt("WaitWritable"); if ( WaitWritable != null ) { // since 2.0 (do not exist in 1.8 / 1.9) SSL.defineClassUnder("SSLErrorWaitWritable", SSLError, OpenSSLError.getAllocator()). include(new IRubyObject[]{ WaitWritable }); } SSL.setConstant("VERIFY_NONE", runtime.newFixnum(VERIFY_NONE)); SSL.setConstant("VERIFY_PEER", runtime.newFixnum(VERIFY_PEER)); SSL.setConstant("VERIFY_FAIL_IF_NO_PEER_CERT", runtime.newFixnum(VERIFY_FAIL_IF_NO_PEER_CERT)); SSL.setConstant("VERIFY_CLIENT_ONCE", runtime.newFixnum(VERIFY_CLIENT_ONCE)); SSL.setConstant("OP_ALL", runtime.newFixnum(OP_ALL)); SSL.setConstant("OP_NO_TICKET", runtime.newFixnum(OP_NO_TICKET)); SSL.setConstant("OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", runtime.newFixnum(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)); SSL.setConstant("OP_SINGLE_ECDH_USE", runtime.newFixnum(OP_SINGLE_ECDH_USE)); SSL.setConstant("OP_SINGLE_DH_USE", runtime.newFixnum(OP_SINGLE_DH_USE)); SSL.setConstant("OP_EPHEMERAL_RSA", runtime.newFixnum(OP_EPHEMERAL_RSA)); SSL.setConstant("OP_CIPHER_SERVER_PREFERENCE", runtime.newFixnum(OP_CIPHER_SERVER_PREFERENCE)); SSL.setConstant("OP_TLS_ROLLBACK_BUG", runtime.newFixnum(OP_TLS_ROLLBACK_BUG)); SSL.setConstant("OP_NO_SSLv2", runtime.newFixnum(OP_NO_SSLv2)); SSL.setConstant("OP_NO_SSLv3", runtime.newFixnum(OP_NO_SSLv3)); SSL.setConstant("OP_NO_TLSv1", runtime.newFixnum(OP_NO_TLSv1)); SSL.setConstant("OP_PKCS1_CHECK_1", runtime.newFixnum(OP_PKCS1_CHECK_1)); SSL.setConstant("OP_PKCS1_CHECK_2", runtime.newFixnum(OP_PKCS1_CHECK_2)); SSL.setConstant("OP_NETSCAPE_CA_DN_BUG", runtime.newFixnum(OP_NETSCAPE_CA_DN_BUG)); SSL.setConstant("OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", runtime.newFixnum(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG)); SSLContext.createSSLContext(runtime, SSL); SSLSocket.createSSLSocket(runtime, SSL); SSLSession.createSession(runtime, SSL); createSocketForwarder(SSL); createNonblock(SSL); } public static RaiseException newSSLError(Ruby runtime, Exception ex) { return Utils.newError(runtime, _SSL(runtime).getClass("SSLError"), ex); } public static RaiseException newSSLError(Ruby runtime, String message) { return Utils.newError(runtime, _SSL(runtime).getClass("SSLError"), message, false); } private static RaiseException newSSLError(Ruby runtime, String message, Exception ex) { return Utils.newError(runtime, _SSL(runtime).getClass("SSLError"), message, ex); } public static RaiseException newSSLErrorWaitReadable(Ruby runtime, String message) { return newWaitSSLError(runtime, "SSLErrorWaitReadable", message); } public static RaiseException newSSLErrorWaitWritable(Ruby runtime, String message) { return newWaitSSLError(runtime, "SSLErrorWaitWritable", message); } // -Djruby.openssl.ssl.error_wait_nonblock.backtrace=false disables backtrace for WaitReadable/Writable private static final boolean waitErrorBacktrace; static { String backtrace = SafePropertyAccessor.getProperty("jruby.openssl.ssl.error_wait_nonblock.backtrace"); if (backtrace == null) { // default to JRuby's Option<Boolean> ERRNO_BACKTRACE = // ... "Generate backtraces for heavily-used Errno exceptions (EAGAIN)." backtrace = SafePropertyAccessor.getProperty("jruby.errno.backtrace", "false"); } waitErrorBacktrace = Boolean.parseBoolean(backtrace); } private static RaiseException newWaitSSLError(final Ruby runtime, final String name, final String message) { RubyClass errorClass = _SSL(runtime).getClass(name); if ( errorClass == null ) { // < Ruby 2.0 errorClass = _SSL(runtime).getClass("SSLError"); // fallback } if ( waitErrorBacktrace ) { return Utils.newError(runtime, errorClass, message, false); } return Utils.newErrorWithoutTrace(runtime, errorClass, message, false); } static RubyModule _SSL(final Ruby runtime) { return (RubyModule) runtime.getModule("OpenSSL").getConstant("SSL"); } private static RubyModule createSocketForwarder(final RubyModule SSL) { // OpenSSL::SSL final RubyModule SocketForwarder = SSL.defineModuleUnder("SocketForwarder"); SocketForwarder.defineAnnotatedMethods(SocketForwarder.class); return SocketForwarder; } @JRubyModule(name = "OpenSSL::SSL::SocketForwarder") public static class SocketForwarder { @JRubyMethod public static IRubyObject addr(ThreadContext context, IRubyObject self) { return to_io(context, self).callMethod(context, "addr"); } @JRubyMethod public static IRubyObject peeraddr(ThreadContext context, IRubyObject self) { return to_io(context, self).callMethod(context, "peeraddr"); } @JRubyMethod(name = "closed?") public static IRubyObject closed_p(ThreadContext context, IRubyObject self) { return to_io(context, self).callMethod(context, "closed?"); } @JRubyMethod //@JRubyMethod(required = 2) // def getsockopt(level, optname) //public static IRubyObject getsockopt(ThreadContext context, IRubyObject self, IRubyObject[] args) { public static IRubyObject getsockopt(ThreadContext context, IRubyObject self, IRubyObject level, IRubyObject optname) { //return to_io(context, self).callMethod(context, "getsockopt", args); return to_io(context, self).callMethod(context, "getsockopt", new IRubyObject[] { level, optname }); } @JRubyMethod //@JRubyMethod(required = 3) // def setsockopt(level, optname, optval) //public static IRubyObject setsockopt(ThreadContext context, IRubyObject self, IRubyObject[] args) { public static IRubyObject setsockopt(ThreadContext context, IRubyObject self, IRubyObject level, IRubyObject optname, IRubyObject optval) { //return to_io(context, self).callMethod(context, "setsockopt", args); return to_io(context, self).callMethod(context, "setsockopt", new IRubyObject[] { level, optname, optval }); } @JRubyMethod(name = "do_not_reverse_lookup=") // def do_not_reverse_lookup=(flag) public static IRubyObject do_not_reverse_lookup_eq(ThreadContext context, IRubyObject self, IRubyObject flag) { return to_io(context, self).callMethod(context, "do_not_reverse_lookup=", flag); } @JRubyMethod(rest = true) // def fcntl(*args) public static IRubyObject fcntl(ThreadContext context, IRubyObject self, IRubyObject[] args) { return to_io(context, self).callMethod(context, "fcntl", args); } private static IRubyObject to_io(ThreadContext context, IRubyObject self) { return self.callMethod(context, "to_io"); } } // SocketForwarder private static RubyModule createNonblock(final RubyModule SSL) { // OpenSSL::SSL final RubyModule Nonblock = SSL.defineModuleUnder("Nonblock"); Nonblock.defineAnnotatedMethods(Nonblock.class); return Nonblock; } @JRubyModule(name = "OpenSSL::SSL::Nonblock") public static class Nonblock { @JRubyMethod(rest = true, frame = true) // framed due super public static IRubyObject initialize(ThreadContext context, IRubyObject self, IRubyObject[] args) { final Ruby runtime = context.runtime; IRubyObject flag = runtime.getFile().getConstant("NONBLOCK"); // File::NONBLOCK IRubyObject Fcntl = runtime.getObject().getConstantAt("Fcntl"); if ( /* Fcntl != null && */ Fcntl instanceof RubyModule ) { final IRubyObject io = self.getInstanceVariables().getInstanceVariable("@io"); final RubyClass ioClass = self.getMetaClass(); final DynamicMethod fcntl = ioClass.searchMethod("fcntl"); IRubyObject F_GETFL = ((RubyModule) Fcntl).getConstantAt("F_GETFL"); if ( F_GETFL != null ) { // if defined?(Fcntl::F_GETFL) // flag |= @io.fcntl(Fcntl::F_GETFL) : flag = or(context, flag, fcntl.call(context, io, ioClass, "fcntl", F_GETFL)); } IRubyObject F_SETFL = ((RubyModule) Fcntl).getConstant("F_SETFL"); fcntl.call(context, io, ioClass, "fcntl", new IRubyObject[] { F_SETFL, flag }); // @io.fcntl(Fcntl::F_SETFL, flag) } return Utils.invokeSuper(context, self, args, Block.NULL_BLOCK); // super } private static IRubyObject or(final ThreadContext context, final IRubyObject flag, final IRubyObject flags) { if ( flag instanceof RubyFixnum && flags instanceof RubyFixnum ) { final long f = ((RubyFixnum) flag).getLongValue(); final long fs = ((RubyFixnum) flags).getLongValue(); return RubyFixnum.newFixnum(context.runtime, f | fs); } return flag.callMethod(context, "|", flags); } } // Nonblock }// SSL