1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl;
18
19 import io.netty.buffer.ByteBuf;
20 import io.netty.handler.ssl.util.SelfSignedCertificate;
21 import io.netty.internal.tcnative.Buffer;
22 import io.netty.internal.tcnative.Library;
23 import io.netty.internal.tcnative.SSL;
24 import io.netty.internal.tcnative.SSLContext;
25 import io.netty.util.ReferenceCountUtil;
26 import io.netty.util.ReferenceCounted;
27 import io.netty.util.internal.NativeLibraryLoader;
28 import io.netty.util.internal.PlatformDependent;
29 import io.netty.util.internal.SystemPropertyUtil;
30 import io.netty.util.internal.logging.InternalLogger;
31 import io.netty.util.internal.logging.InternalLoggerFactory;
32
33 import java.security.AccessController;
34 import java.security.PrivilegedAction;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Set;
41
42 import static io.netty.handler.ssl.SslUtils.DEFAULT_CIPHER_SUITES;
43 import static io.netty.handler.ssl.SslUtils.addIfSupported;
44 import static io.netty.handler.ssl.SslUtils.useFallbackCiphersIfDefaultIsEmpty;
45 import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2;
46 import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO;
47 import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3;
48 import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1;
49 import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1;
50 import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
51
52
53
54
55
56 public final class OpenSsl {
57
58 private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class);
59 private static final Throwable UNAVAILABILITY_CAUSE;
60
61 static final List<String> DEFAULT_CIPHERS;
62 static final Set<String> AVAILABLE_CIPHER_SUITES;
63 private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES;
64 private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES;
65 private static final boolean SUPPORTS_KEYMANAGER_FACTORY;
66 private static final boolean SUPPORTS_HOSTNAME_VALIDATION;
67 private static final boolean USE_KEYMANAGER_FACTORY;
68 private static final boolean SUPPORTS_OCSP;
69
70 static final Set<String> SUPPORTED_PROTOCOLS_SET;
71
72 static {
73 Throwable cause = null;
74
75
76 try {
77 Class.forName("io.netty.internal.tcnative.SSL", false, OpenSsl.class.getClassLoader());
78 } catch (ClassNotFoundException t) {
79 cause = t;
80 logger.debug(
81 "netty-tcnative not in the classpath; " +
82 OpenSslEngine.class.getSimpleName() + " will be unavailable.");
83 }
84
85
86 if (cause == null) {
87 try {
88
89 loadTcNative();
90 } catch (Throwable t) {
91 cause = t;
92 logger.debug(
93 "Failed to load netty-tcnative; " +
94 OpenSslEngine.class.getSimpleName() + " will be unavailable, unless the " +
95 "application has already loaded the symbols by some other means. " +
96 "See http://netty.io/wiki/forked-tomcat-native.html for more information.", t);
97 }
98
99 try {
100 initializeTcNative();
101
102
103
104
105 cause = null;
106 } catch (Throwable t) {
107 if (cause == null) {
108 cause = t;
109 }
110 logger.debug(
111 "Failed to initialize netty-tcnative; " +
112 OpenSslEngine.class.getSimpleName() + " will be unavailable. " +
113 "See http://netty.io/wiki/forked-tomcat-native.html for more information.", t);
114 }
115 }
116
117 UNAVAILABILITY_CAUSE = cause;
118
119 if (cause == null) {
120 logger.debug("netty-tcnative using native library: {}", SSL.versionString());
121
122 final List<String> defaultCiphers = new ArrayList<String>();
123 final Set<String> availableOpenSslCipherSuites = new LinkedHashSet<String>(128);
124 boolean supportsKeyManagerFactory = false;
125 boolean useKeyManagerFactory = false;
126 boolean supportsHostNameValidation = false;
127 try {
128 final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
129 long certBio = 0;
130 SelfSignedCertificate cert = null;
131 try {
132 SSLContext.setCipherSuite(sslCtx, "ALL");
133 final long ssl = SSL.newSSL(sslCtx, true);
134 try {
135 for (String c: SSL.getCiphers(ssl)) {
136
137 if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c)) {
138 continue;
139 }
140 availableOpenSslCipherSuites.add(c);
141 }
142
143 try {
144 SSL.setHostNameValidation(ssl, 0, "netty.io");
145 supportsHostNameValidation = true;
146 } catch (Throwable ignore) {
147 logger.debug("Hostname Verification not supported.");
148 }
149 try {
150 cert = new SelfSignedCertificate();
151 certBio = ReferenceCountedOpenSslContext.toBIO(cert.cert());
152 SSL.setCertificateChainBio(ssl, certBio, false);
153 supportsKeyManagerFactory = true;
154 try {
155 useKeyManagerFactory = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
156 @Override
157 public Boolean run() {
158 return SystemPropertyUtil.getBoolean(
159 "io.netty.handler.ssl.openssl.useKeyManagerFactory", true);
160 }
161 });
162 } catch (Throwable ignore) {
163 logger.debug("Failed to get useKeyManagerFactory system property.");
164 }
165 } catch (Throwable ignore) {
166 logger.debug("KeyManagerFactory not supported.");
167 }
168 } finally {
169 SSL.freeSSL(ssl);
170 if (certBio != 0) {
171 SSL.freeBIO(certBio);
172 }
173 if (cert != null) {
174 cert.delete();
175 }
176 }
177 } finally {
178 SSLContext.free(sslCtx);
179 }
180 } catch (Exception e) {
181 logger.warn("Failed to get the list of available OpenSSL cipher suites.", e);
182 }
183 AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.unmodifiableSet(availableOpenSslCipherSuites);
184 final Set<String> availableJavaCipherSuites = new LinkedHashSet<String>(
185 AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2);
186 for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) {
187
188 availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS"));
189 availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL"));
190 }
191
192 addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES);
193 useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites);
194 DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers);
195
196 AVAILABLE_JAVA_CIPHER_SUITES = Collections.unmodifiableSet(availableJavaCipherSuites);
197
198 final Set<String> availableCipherSuites = new LinkedHashSet<String>(
199 AVAILABLE_OPENSSL_CIPHER_SUITES.size() + AVAILABLE_JAVA_CIPHER_SUITES.size());
200 availableCipherSuites.addAll(AVAILABLE_OPENSSL_CIPHER_SUITES);
201 availableCipherSuites.addAll(AVAILABLE_JAVA_CIPHER_SUITES);
202
203 AVAILABLE_CIPHER_SUITES = availableCipherSuites;
204 SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory;
205 SUPPORTS_HOSTNAME_VALIDATION = supportsHostNameValidation;
206 USE_KEYMANAGER_FACTORY = useKeyManagerFactory;
207
208 Set<String> protocols = new LinkedHashSet<String>(6);
209
210 protocols.add(PROTOCOL_SSL_V2_HELLO);
211 if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2)) {
212 protocols.add(PROTOCOL_SSL_V2);
213 }
214 if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3)) {
215 protocols.add(PROTOCOL_SSL_V3);
216 }
217 if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1)) {
218 protocols.add(PROTOCOL_TLS_V1);
219 }
220 if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1)) {
221 protocols.add(PROTOCOL_TLS_V1_1);
222 }
223 if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2)) {
224 protocols.add(PROTOCOL_TLS_V1_2);
225 }
226
227 SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols);
228 SUPPORTS_OCSP = doesSupportOcsp();
229
230 if (logger.isDebugEnabled()) {
231 logger.debug("Supported protocols (OpenSSL): {} ", Arrays.asList(SUPPORTED_PROTOCOLS_SET));
232 logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS);
233 }
234 } else {
235 DEFAULT_CIPHERS = Collections.emptyList();
236 AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
237 AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
238 AVAILABLE_CIPHER_SUITES = Collections.emptySet();
239 SUPPORTS_KEYMANAGER_FACTORY = false;
240 SUPPORTS_HOSTNAME_VALIDATION = false;
241 USE_KEYMANAGER_FACTORY = false;
242 SUPPORTED_PROTOCOLS_SET = Collections.emptySet();
243 SUPPORTS_OCSP = false;
244 }
245 }
246
247 private static boolean doesSupportOcsp() {
248 boolean supportsOcsp = false;
249 if (version() >= 0x10002000L) {
250 long sslCtx = -1;
251 try {
252 sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_MODE_SERVER);
253 SSLContext.enableOcsp(sslCtx, false);
254 supportsOcsp = true;
255 } catch (Exception ignore) {
256
257 } finally {
258 if (sslCtx != -1) {
259 SSLContext.free(sslCtx);
260 }
261 }
262 }
263 return supportsOcsp;
264 }
265 private static boolean doesSupportProtocol(int protocol) {
266 long sslCtx = -1;
267 try {
268 sslCtx = SSLContext.make(protocol, SSL.SSL_MODE_COMBINED);
269 return true;
270 } catch (Exception ignore) {
271 return false;
272 } finally {
273 if (sslCtx != -1) {
274 SSLContext.free(sslCtx);
275 }
276 }
277 }
278
279
280
281
282
283
284 public static boolean isAvailable() {
285 return UNAVAILABILITY_CAUSE == null;
286 }
287
288
289
290
291
292 public static boolean isAlpnSupported() {
293 return version() >= 0x10002000L;
294 }
295
296
297
298
299 public static boolean isOcspSupported() {
300 return SUPPORTS_OCSP;
301 }
302
303
304
305
306
307 public static int version() {
308 return isAvailable() ? SSL.version() : -1;
309 }
310
311
312
313
314
315 public static String versionString() {
316 return isAvailable() ? SSL.versionString() : null;
317 }
318
319
320
321
322
323
324
325 public static void ensureAvailability() {
326 if (UNAVAILABILITY_CAUSE != null) {
327 throw (Error) new UnsatisfiedLinkError(
328 "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
329 }
330 }
331
332
333
334
335
336
337
338 public static Throwable unavailabilityCause() {
339 return UNAVAILABILITY_CAUSE;
340 }
341
342
343
344
345 @Deprecated
346 public static Set<String> availableCipherSuites() {
347 return availableOpenSslCipherSuites();
348 }
349
350
351
352
353
354 public static Set<String> availableOpenSslCipherSuites() {
355 return AVAILABLE_OPENSSL_CIPHER_SUITES;
356 }
357
358
359
360
361
362 public static Set<String> availableJavaCipherSuites() {
363 return AVAILABLE_JAVA_CIPHER_SUITES;
364 }
365
366
367
368
369
370 public static boolean isCipherSuiteAvailable(String cipherSuite) {
371 String converted = CipherSuiteConverter.toOpenSsl(cipherSuite);
372 if (converted != null) {
373 cipherSuite = converted;
374 }
375 return AVAILABLE_OPENSSL_CIPHER_SUITES.contains(cipherSuite);
376 }
377
378
379
380
381 public static boolean supportsKeyManagerFactory() {
382 return SUPPORTS_KEYMANAGER_FACTORY;
383 }
384
385
386
387
388
389 public static boolean supportsHostnameValidation() {
390 return SUPPORTS_HOSTNAME_VALIDATION;
391 }
392
393 static boolean useKeyManagerFactory() {
394 return USE_KEYMANAGER_FACTORY;
395 }
396
397 static long memoryAddress(ByteBuf buf) {
398 assert buf.isDirect();
399 return buf.hasMemoryAddress() ? buf.memoryAddress() : Buffer.address(buf.nioBuffer());
400 }
401
402 private OpenSsl() { }
403
404 private static void loadTcNative() throws Exception {
405 String os = PlatformDependent.normalizedOs();
406 String arch = PlatformDependent.normalizedArch();
407
408 Set<String> libNames = new LinkedHashSet<String>(4);
409 String staticLibName = "netty_tcnative";
410
411
412
413 libNames.add(staticLibName + "_" + os + '_' + arch);
414 if ("linux".equalsIgnoreCase(os)) {
415
416 libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora");
417 }
418 libNames.add(staticLibName + "_" + arch);
419 libNames.add(staticLibName);
420
421 NativeLibraryLoader.loadFirstAvailable(SSL.class.getClassLoader(),
422 libNames.toArray(new String[libNames.size()]));
423 }
424
425 private static boolean initializeTcNative() throws Exception {
426 return Library.initialize();
427 }
428
429 static void releaseIfNeeded(ReferenceCounted counted) {
430 if (counted.refCnt() > 0) {
431 ReferenceCountUtil.safeRelease(counted);
432 }
433 }
434 }