/***** 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