2014-05-17 19:26:01 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2014 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.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.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package io.netty.handler.ssl;
|
|
|
|
|
|
|
|
import io.netty.util.internal.NativeLibraryLoader;
|
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
|
|
import org.apache.tomcat.jni.Library;
|
2014-12-30 10:59:30 +01:00
|
|
|
import org.apache.tomcat.jni.Pool;
|
2014-05-17 19:26:01 +02:00
|
|
|
import org.apache.tomcat.jni.SSL;
|
2014-12-30 10:59:30 +01:00
|
|
|
import org.apache.tomcat.jni.SSLContext;
|
|
|
|
|
|
|
|
import java.util.Collections;
|
2014-12-30 11:20:43 +01:00
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.Set;
|
2014-05-17 19:26:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
|
|
|
|
* are available.
|
|
|
|
*/
|
|
|
|
public final class OpenSsl {
|
|
|
|
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class);
|
|
|
|
private static final Throwable UNAVAILABILITY_CAUSE;
|
|
|
|
|
2014-12-30 11:20:43 +01:00
|
|
|
private static final Set<String> AVAILABLE_CIPHER_SUITES;
|
2014-12-30 10:59:30 +01:00
|
|
|
|
2014-05-17 19:26:01 +02:00
|
|
|
static {
|
|
|
|
Throwable cause = null;
|
2015-01-08 04:23:14 +01:00
|
|
|
|
|
|
|
// Test if netty-tcnative is in the classpath first.
|
2014-05-17 19:26:01 +02:00
|
|
|
try {
|
2015-01-08 04:23:14 +01:00
|
|
|
Class.forName("org.apache.tomcat.jni.SSL", false, OpenSsl.class.getClassLoader());
|
|
|
|
} catch (ClassNotFoundException t) {
|
2014-05-17 19:26:01 +02:00
|
|
|
cause = t;
|
|
|
|
logger.debug(
|
2015-01-08 04:23:14 +01:00
|
|
|
"netty-tcnative not in the classpath; " +
|
|
|
|
OpenSslEngine.class.getSimpleName() + " will be unavailable.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// If in the classpath, try to load the native library and initialize netty-tcnative.
|
|
|
|
if (cause == null) {
|
|
|
|
try {
|
|
|
|
NativeLibraryLoader.load("netty-tcnative", SSL.class.getClassLoader());
|
|
|
|
Library.initialize("provided");
|
|
|
|
SSL.initialize(null);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
cause = t;
|
|
|
|
logger.debug(
|
|
|
|
"Failed to load netty-tcnative; " +
|
2015-01-08 04:43:03 +01:00
|
|
|
OpenSslEngine.class.getSimpleName() + " will be unavailable. " +
|
|
|
|
"See http://netty.io/wiki/forked-tomcat-native.html for more information.", t);
|
2015-01-08 04:23:14 +01:00
|
|
|
}
|
2014-05-17 19:26:01 +02:00
|
|
|
}
|
2015-01-08 04:23:14 +01:00
|
|
|
|
2014-05-17 19:26:01 +02:00
|
|
|
UNAVAILABILITY_CAUSE = cause;
|
2014-12-30 10:59:30 +01:00
|
|
|
|
|
|
|
if (cause == null) {
|
2014-12-30 11:20:43 +01:00
|
|
|
final Set<String> availableCipherSuites = new LinkedHashSet<String>(128);
|
2014-12-30 10:59:30 +01:00
|
|
|
final long aprPool = Pool.create(0);
|
|
|
|
try {
|
|
|
|
final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
|
|
|
try {
|
|
|
|
SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
|
|
|
|
SSLContext.setCipherSuite(sslCtx, "ALL");
|
|
|
|
final long ssl = SSL.newSSL(sslCtx, true);
|
|
|
|
try {
|
|
|
|
for (String c: SSL.getCiphers(ssl)) {
|
|
|
|
// Filter out bad input.
|
|
|
|
if (c == null || c.length() == 0 || availableCipherSuites.contains(c)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
availableCipherSuites.add(c);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
SSL.freeSSL(ssl);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
SSLContext.free(sslCtx);
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
logger.warn("Failed to get the list of available OpenSSL cipher suites.", e);
|
|
|
|
} finally {
|
|
|
|
Pool.destroy(aprPool);
|
|
|
|
}
|
|
|
|
|
2014-12-30 11:20:43 +01:00
|
|
|
AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites);
|
2014-12-30 10:59:30 +01:00
|
|
|
} else {
|
2014-12-30 11:20:43 +01:00
|
|
|
AVAILABLE_CIPHER_SUITES = Collections.emptySet();
|
2014-12-30 10:59:30 +01:00
|
|
|
}
|
2014-05-17 19:26:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if and only if
|
|
|
|
* <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
|
|
|
|
* are available.
|
|
|
|
*/
|
|
|
|
public static boolean isAvailable() {
|
|
|
|
return UNAVAILABILITY_CAUSE == null;
|
|
|
|
}
|
|
|
|
|
2015-03-05 14:19:13 +01:00
|
|
|
/**
|
|
|
|
* Returns {@code true} if the used version of openssl supports
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc7301">ALPN</a>.
|
|
|
|
*/
|
|
|
|
public static boolean isAlpnSupported() {
|
|
|
|
return isAvailable() && SSL.version() >= 0x10002000L;
|
|
|
|
}
|
|
|
|
|
2014-05-17 19:26:01 +02:00
|
|
|
/**
|
|
|
|
* Ensure that <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and
|
|
|
|
* its OpenSSL support are available.
|
|
|
|
*
|
|
|
|
* @throws UnsatisfiedLinkError if unavailable
|
|
|
|
*/
|
|
|
|
public static void ensureAvailability() {
|
|
|
|
if (UNAVAILABILITY_CAUSE != null) {
|
|
|
|
throw (Error) new UnsatisfiedLinkError(
|
|
|
|
"failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the cause of unavailability of
|
|
|
|
* <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support.
|
|
|
|
*
|
|
|
|
* @return the cause if unavailable. {@code null} if available.
|
|
|
|
*/
|
|
|
|
public static Throwable unavailabilityCause() {
|
|
|
|
return UNAVAILABILITY_CAUSE;
|
|
|
|
}
|
|
|
|
|
2014-12-30 10:59:30 +01:00
|
|
|
/**
|
|
|
|
* Returns all the available OpenSSL cipher suites.
|
|
|
|
* Please note that the returned array may include the cipher suites that are insecure or non-functional.
|
|
|
|
*/
|
2014-12-30 11:20:43 +01:00
|
|
|
public static Set<String> availableCipherSuites() {
|
|
|
|
return AVAILABLE_CIPHER_SUITES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if and only if the specified cipher suite is available in OpenSSL.
|
|
|
|
* Both Java-style cipher suite and OpenSSL-style cipher suite are accepted.
|
|
|
|
*/
|
|
|
|
public static boolean isCipherSuiteAvailable(String cipherSuite) {
|
|
|
|
String converted = CipherSuiteConverter.toOpenSsl(cipherSuite);
|
|
|
|
if (converted != null) {
|
|
|
|
cipherSuite = converted;
|
|
|
|
}
|
|
|
|
return AVAILABLE_CIPHER_SUITES.contains(cipherSuite);
|
2014-12-30 10:59:30 +01:00
|
|
|
}
|
|
|
|
|
2014-10-17 16:11:40 +02:00
|
|
|
static boolean isError(long errorCode) {
|
|
|
|
return errorCode != SSL.SSL_ERROR_NONE;
|
2014-10-13 13:21:29 +02:00
|
|
|
}
|
|
|
|
|
2014-05-17 19:26:01 +02:00
|
|
|
private OpenSsl() { }
|
|
|
|
}
|