8221882: Use fiber-friendly java.util.concurrent.locks in JSSE

Reviewed-by: alanb, dfuchs
This commit is contained in:
Xue-Lei Andrew Fan 2019-04-05 11:28:23 -07:00
parent 6d617481d4
commit 8263b618ba
22 changed files with 1672 additions and 1020 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,8 +26,9 @@
package javax.net.ssl; package javax.net.ssl;
import java.security.*; import java.security.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Objects; import java.util.Objects;
import sun.security.jca.GetInstance; import sun.security.jca.GetInstance;
/** /**
@ -58,6 +59,20 @@ public class SSLContext {
private final String protocol; private final String protocol;
private static volatile SSLContext defaultContext;
private static final VarHandle VH_DEFAULT_CONTEXT;
static {
try {
VH_DEFAULT_CONTEXT = MethodHandles.lookup()
.findStaticVarHandle(
SSLContext.class, "defaultContext", SSLContext.class);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/** /**
* Creates an SSLContext object. * Creates an SSLContext object.
* *
@ -72,8 +87,6 @@ public class SSLContext {
this.protocol = protocol; this.protocol = protocol;
} }
private static SSLContext defaultContext;
/** /**
* Returns the default SSL context. * Returns the default SSL context.
* *
@ -91,12 +104,16 @@ public class SSLContext {
* {@link SSLContext#getInstance SSLContext.getInstance()} call fails * {@link SSLContext#getInstance SSLContext.getInstance()} call fails
* @since 1.6 * @since 1.6
*/ */
public static synchronized SSLContext getDefault() public static SSLContext getDefault() throws NoSuchAlgorithmException {
throws NoSuchAlgorithmException { SSLContext temporaryContext = defaultContext;
if (defaultContext == null) { if (temporaryContext == null) {
defaultContext = SSLContext.getInstance("Default"); temporaryContext = SSLContext.getInstance("Default");
if (!VH_DEFAULT_CONTEXT.compareAndSet(null, temporaryContext)) {
temporaryContext = defaultContext;
}
} }
return defaultContext;
return temporaryContext;
} }
/** /**
@ -111,7 +128,7 @@ public class SSLContext {
* {@code SSLPermission("setDefaultSSLContext")} * {@code SSLPermission("setDefaultSSLContext")}
* @since 1.6 * @since 1.6
*/ */
public static synchronized void setDefault(SSLContext context) { public static void setDefault(SSLContext context) {
if (context == null) { if (context == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
@ -119,6 +136,7 @@ public class SSLContext {
if (sm != null) { if (sm != null) {
sm.checkPermission(new SSLPermission("setDefaultSSLContext")); sm.checkPermission(new SSLPermission("setDefaultSSLContext"));
} }
defaultContext = context; defaultContext = context;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -42,17 +42,7 @@ import java.security.*;
* @see SSLServerSocket * @see SSLServerSocket
* @author David Brownell * @author David Brownell
*/ */
public abstract class SSLServerSocketFactory extends ServerSocketFactory public abstract class SSLServerSocketFactory extends ServerSocketFactory {
{
private static SSLServerSocketFactory theFactory;
private static boolean propertyChecked;
private static void log(String msg) {
if (SSLSocketFactory.DEBUG) {
System.out.println(msg);
}
}
/** /**
* Constructor is used only by subclasses. * Constructor is used only by subclasses.
@ -75,39 +65,9 @@ public abstract class SSLServerSocketFactory extends ServerSocketFactory
* @return the default <code>ServerSocketFactory</code> * @return the default <code>ServerSocketFactory</code>
* @see SSLContext#getDefault * @see SSLContext#getDefault
*/ */
public static synchronized ServerSocketFactory getDefault() { public static ServerSocketFactory getDefault() {
if (theFactory != null) { if (DefaultFactoryHolder.defaultFactory != null) {
return theFactory; return DefaultFactoryHolder.defaultFactory;
}
if (propertyChecked == false) {
propertyChecked = true;
String clsName = SSLSocketFactory.getSecurityProperty
("ssl.ServerSocketFactory.provider");
if (clsName != null) {
log("setting up default SSLServerSocketFactory");
try {
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
log("class " + clsName + " is loaded");
@SuppressWarnings("deprecation")
SSLServerSocketFactory fac = (SSLServerSocketFactory)cls.newInstance();
log("instantiated an instance of class " + clsName);
theFactory = fac;
return fac;
} catch (Exception e) {
log("SSLServerSocketFactory instantiation failed: " + e);
theFactory = new DefaultSSLServerSocketFactory(e);
return theFactory;
}
}
} }
try { try {
@ -156,8 +116,50 @@ public abstract class SSLServerSocketFactory extends ServerSocketFactory
* @see #getDefaultCipherSuites() * @see #getDefaultCipherSuites()
*/ */
public abstract String [] getSupportedCipherSuites(); public abstract String [] getSupportedCipherSuites();
}
// lazy initialization holder class idiom for static default factory
//
// See Effective Java Second Edition: Item 71.
private static final class DefaultFactoryHolder {
private static final SSLServerSocketFactory defaultFactory;
static {
SSLServerSocketFactory mediator = null;
String clsName = SSLSocketFactory.getSecurityProperty(
"ssl.ServerSocketFactory.provider");
if (clsName != null) {
log("setting up default SSLServerSocketFactory");
try {
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
log("class " + clsName + " is loaded");
mediator = (SSLServerSocketFactory)cls
.getDeclaredConstructor().newInstance();
log("instantiated an instance of class " + clsName);
} catch (Exception e) {
log("SSLServerSocketFactory instantiation failed: " + e);
mediator = new DefaultSSLServerSocketFactory(e);
}
}
defaultFactory = mediator;
}
private static void log(String msg) {
if (SSLSocketFactory.DEBUG) {
System.out.println(msg);
}
}
}
}
// //
// The default factory does NOTHING. // The default factory does NOTHING.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -42,31 +42,20 @@ import sun.security.action.GetPropertyAction;
* @see SSLSocket * @see SSLSocket
* @author David Brownell * @author David Brownell
*/ */
public abstract class SSLSocketFactory extends SocketFactory public abstract class SSLSocketFactory extends SocketFactory {
{
private static SSLSocketFactory theFactory;
private static boolean propertyChecked;
static final boolean DEBUG; static final boolean DEBUG;
static { static {
String s = GetPropertyAction.privilegedGetProperty("javax.net.debug", "") String s = GetPropertyAction.privilegedGetProperty(
.toLowerCase(Locale.ENGLISH); "javax.net.debug", "").toLowerCase(Locale.ENGLISH);
DEBUG = s.contains("all") || s.contains("ssl"); DEBUG = s.contains("all") || s.contains("ssl");
} }
private static void log(String msg) {
if (DEBUG) {
System.out.println(msg);
}
}
/** /**
* Constructor is used only by subclasses. * Constructor is used only by subclasses.
*/ */
public SSLSocketFactory() { public SSLSocketFactory() {
// blank
} }
/** /**
@ -85,38 +74,9 @@ public abstract class SSLSocketFactory extends SocketFactory
* @return the default <code>SocketFactory</code> * @return the default <code>SocketFactory</code>
* @see SSLContext#getDefault * @see SSLContext#getDefault
*/ */
public static synchronized SocketFactory getDefault() { public static SocketFactory getDefault() {
if (theFactory != null) { if (DefaultFactoryHolder.defaultFactory != null) {
return theFactory; return DefaultFactoryHolder.defaultFactory;
}
if (propertyChecked == false) {
propertyChecked = true;
String clsName = getSecurityProperty("ssl.SocketFactory.provider");
if (clsName != null) {
log("setting up default SSLSocketFactory");
try {
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
log("class " + clsName + " is loaded");
@SuppressWarnings("deprecation")
SSLSocketFactory fac = (SSLSocketFactory)cls.newInstance();
log("instantiated an instance of class " + clsName);
theFactory = fac;
return fac;
} catch (Exception e) {
log("SSLSocketFactory instantiation failed: " + e.toString());
theFactory = new DefaultSSLSocketFactory(e);
return theFactory;
}
}
} }
try { try {
@ -246,6 +206,49 @@ public abstract class SSLSocketFactory extends SocketFactory
boolean autoClose) throws IOException { boolean autoClose) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
// lazy initialization holder class idiom for static default factory
//
// See Effective Java Second Edition: Item 71.
private static final class DefaultFactoryHolder {
private static final SSLSocketFactory defaultFactory;
static {
SSLSocketFactory mediator = null;
String clsName = getSecurityProperty("ssl.SocketFactory.provider");
if (clsName != null) {
log("setting up default SSLSocketFactory");
try {
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
log("class " + clsName + " is loaded");
mediator = (SSLSocketFactory)cls
.getDeclaredConstructor().newInstance();
log("instantiated an instance of class " + clsName);
} catch (Exception e) {
log("SSLSocketFactory instantiation failed: " + e);
mediator = new DefaultSSLSocketFactory(e);
}
}
defaultFactory = mediator;
}
private static void log(String msg) {
if (DEBUG) {
System.out.println(msg);
}
}
}
} }

View File

@ -57,8 +57,7 @@ import sun.net.www.http.HttpClient;
public class HttpsURLConnectionImpl public class HttpsURLConnectionImpl
extends javax.net.ssl.HttpsURLConnection { extends javax.net.ssl.HttpsURLConnection {
// NOTE: made protected for plugin so that subclass can set it. private final DelegateHttpsURLConnection delegate;
protected DelegateHttpsURLConnection delegate;
HttpsURLConnectionImpl(URL u, Handler handler) throws IOException { HttpsURLConnectionImpl(URL u, Handler handler) throws IOException {
this(u, null, handler); this(u, null, handler);
@ -78,13 +77,6 @@ public class HttpsURLConnectionImpl
delegate = new DelegateHttpsURLConnection(url, p, handler, this); delegate = new DelegateHttpsURLConnection(url, p, handler, this);
} }
// NOTE: introduced for plugin
// subclass needs to overwrite this to set delegate to
// the appropriate delegatee
protected HttpsURLConnectionImpl(URL u) throws IOException {
super(u);
}
/** /**
* Create a new HttpClient object, bypassing the cache of * Create a new HttpClient object, bypassing the cache of
* HTTP client objects/connections. * HTTP client objects/connections.
@ -219,11 +211,11 @@ public class HttpsURLConnectionImpl
* - get input, [read input,] get output, [write output] * - get input, [read input,] get output, [write output]
*/ */
public synchronized OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream(); return delegate.getOutputStream();
} }
public synchronized InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return delegate.getInputStream(); return delegate.getInputStream();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -632,7 +632,7 @@ abstract class BaseSSLSocketImpl extends SSLSocket {
} }
@Override @Override
public synchronized void setSoTimeout(int timeout) throws SocketException { public void setSoTimeout(int timeout) throws SocketException {
if (self == this) { if (self == this) {
super.setSoTimeout(timeout); super.setSoTimeout(timeout);
} else { } else {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -58,7 +58,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
} }
@Override @Override
public synchronized void close() throws IOException { public void close() throws IOException {
if (!isClosed) { if (!isClosed) {
super.close(); super.close();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -58,13 +58,18 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
} }
@Override @Override
public synchronized void close() throws IOException { public void close() throws IOException {
if (!isClosed) { recordLock.lock();
if (fragmenter != null && fragmenter.hasAlert()) { try {
isCloseWaiting = true; if (!isClosed) {
} else { if (fragmenter != null && fragmenter.hasAlert()) {
super.close(); isCloseWaiting = true;
} else {
super.close();
}
} }
} finally {
recordLock.unlock();
} }
} }

View File

@ -26,6 +26,7 @@
package sun.security.ssl; package sun.security.ssl;
import java.security.*; import java.security.*;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* The "KeyManager" for ephemeral RSA keys. Ephemeral DH and ECDH keys * The "KeyManager" for ephemeral RSA keys. Ephemeral DH and ECDH keys
@ -48,6 +49,8 @@ final class EphemeralKeyManager {
new EphemeralKeyPair(null), new EphemeralKeyPair(null),
}; };
private final ReentrantLock cachedKeysLock = new ReentrantLock();
EphemeralKeyManager() { EphemeralKeyManager() {
// empty // empty
} }
@ -65,20 +68,32 @@ final class EphemeralKeyManager {
index = INDEX_RSA1024; index = INDEX_RSA1024;
} }
synchronized (keys) { KeyPair kp = keys[index].getKeyPair();
KeyPair kp = keys[index].getKeyPair(); if (kp != null) {
if (kp == null) {
try {
KeyPairGenerator kgen = KeyPairGenerator.getInstance("RSA");
kgen.initialize(length, random);
keys[index] = new EphemeralKeyPair(kgen.genKeyPair());
kp = keys[index].getKeyPair();
} catch (Exception e) {
// ignore
}
}
return kp; return kp;
} }
cachedKeysLock.lock();
try {
// double check
kp = keys[index].getKeyPair();
if (kp != null) {
return kp;
}
try {
KeyPairGenerator kgen = KeyPairGenerator.getInstance("RSA");
kgen.initialize(length, random);
keys[index] = new EphemeralKeyPair(kgen.genKeyPair());
kp = keys[index].getKeyPair();
} catch (Exception e) {
// ignore
}
} finally {
cachedKeysLock.unlock();
}
return kp;
} }
/** /**

View File

@ -30,6 +30,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import static sun.security.ssl.ClientHello.ClientHelloMessage; import static sun.security.ssl.ClientHello.ClientHelloMessage;
/** /**
@ -45,6 +46,8 @@ abstract class HelloCookieManager {
private volatile D13HelloCookieManager d13HelloCookieManager; private volatile D13HelloCookieManager d13HelloCookieManager;
private volatile T13HelloCookieManager t13HelloCookieManager; private volatile T13HelloCookieManager t13HelloCookieManager;
private final ReentrantLock managerLock = new ReentrantLock();
Builder(SecureRandom secureRandom) { Builder(SecureRandom secureRandom) {
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
} }
@ -56,11 +59,14 @@ abstract class HelloCookieManager {
return d13HelloCookieManager; return d13HelloCookieManager;
} }
synchronized (this) { managerLock.lock();
try {
if (d13HelloCookieManager == null) { if (d13HelloCookieManager == null) {
d13HelloCookieManager = d13HelloCookieManager =
new D13HelloCookieManager(secureRandom); new D13HelloCookieManager(secureRandom);
} }
} finally {
managerLock.unlock();
} }
return d13HelloCookieManager; return d13HelloCookieManager;
@ -69,11 +75,14 @@ abstract class HelloCookieManager {
return d10HelloCookieManager; return d10HelloCookieManager;
} }
synchronized (this) { managerLock.lock();
try {
if (d10HelloCookieManager == null) { if (d10HelloCookieManager == null) {
d10HelloCookieManager = d10HelloCookieManager =
new D10HelloCookieManager(secureRandom); new D10HelloCookieManager(secureRandom);
} }
} finally {
managerLock.unlock();
} }
return d10HelloCookieManager; return d10HelloCookieManager;
@ -84,11 +93,14 @@ abstract class HelloCookieManager {
return t13HelloCookieManager; return t13HelloCookieManager;
} }
synchronized (this) { managerLock.lock();
try {
if (t13HelloCookieManager == null) { if (t13HelloCookieManager == null) {
t13HelloCookieManager = t13HelloCookieManager =
new T13HelloCookieManager(secureRandom); new T13HelloCookieManager(secureRandom);
} }
} finally {
managerLock.unlock();
} }
return t13HelloCookieManager; return t13HelloCookieManager;
@ -114,6 +126,8 @@ abstract class HelloCookieManager {
private byte[] cookieSecret; private byte[] cookieSecret;
private byte[] legacySecret; private byte[] legacySecret;
private final ReentrantLock d10ManagerLock = new ReentrantLock();
D10HelloCookieManager(SecureRandom secureRandom) { D10HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
@ -131,7 +145,8 @@ abstract class HelloCookieManager {
int version; int version;
byte[] secret; byte[] secret;
synchronized (this) { d10ManagerLock.lock();
try {
version = cookieVersion; version = cookieVersion;
secret = cookieSecret; secret = cookieSecret;
@ -142,6 +157,8 @@ abstract class HelloCookieManager {
} }
cookieVersion++; cookieVersion++;
} finally {
d10ManagerLock.unlock();
} }
MessageDigest md; MessageDigest md;
@ -168,12 +185,15 @@ abstract class HelloCookieManager {
} }
byte[] secret; byte[] secret;
synchronized (this) { d10ManagerLock.lock();
try {
if (((cookieVersion >> 24) & 0xFF) == cookie[0]) { if (((cookieVersion >> 24) & 0xFF) == cookie[0]) {
secret = cookieSecret; secret = cookieSecret;
} else { } else {
secret = legacySecret; // including out of window cookies secret = legacySecret; // including out of window cookies
} }
} finally {
d10ManagerLock.unlock();
} }
MessageDigest md; MessageDigest md;
@ -218,6 +238,8 @@ abstract class HelloCookieManager {
private final byte[] cookieSecret; private final byte[] cookieSecret;
private final byte[] legacySecret; private final byte[] legacySecret;
private final ReentrantLock t13ManagerLock = new ReentrantLock();
T13HelloCookieManager(SecureRandom secureRandom) { T13HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.cookieVersion = secureRandom.nextInt(); this.cookieVersion = secureRandom.nextInt();
@ -234,7 +256,8 @@ abstract class HelloCookieManager {
int version; int version;
byte[] secret; byte[] secret;
synchronized (this) { t13ManagerLock.lock();
try {
version = cookieVersion; version = cookieVersion;
secret = cookieSecret; secret = cookieSecret;
@ -245,6 +268,8 @@ abstract class HelloCookieManager {
} }
cookieVersion++; // allow wrapped version number cookieVersion++; // allow wrapped version number
} finally {
t13ManagerLock.unlock();
} }
MessageDigest md; MessageDigest md;
@ -313,12 +338,15 @@ abstract class HelloCookieManager {
Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length); Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);
byte[] secret; byte[] secret;
synchronized (this) { t13ManagerLock.lock();
try {
if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) { if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {
secret = cookieSecret; secret = cookieSecret;
} else { } else {
secret = legacySecret; // including out of window cookies secret = legacySecret; // including out of window cookies
} }
} finally {
t13ManagerLock.unlock();
} }
MessageDigest md; MessageDigest md;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -31,6 +31,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantLock;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import sun.security.ssl.SSLCipher.SSLReadCipher; import sun.security.ssl.SSLCipher.SSLReadCipher;
@ -43,10 +44,10 @@ import sun.security.ssl.SSLCipher.SSLReadCipher;
abstract class InputRecord implements Record, Closeable { abstract class InputRecord implements Record, Closeable {
SSLReadCipher readCipher; SSLReadCipher readCipher;
// Needed for KeyUpdate, used after Handshake.Finished // Needed for KeyUpdate, used after Handshake.Finished
TransportContext tc; TransportContext tc;
final HandshakeHash handshakeHash; final HandshakeHash handshakeHash;
boolean isClosed; volatile boolean isClosed;
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
// and the first message we read is a ClientHello in V2 format, we convert // and the first message we read is a ClientHello in V2 format, we convert
@ -56,6 +57,8 @@ abstract class InputRecord implements Record, Closeable {
// fragment size // fragment size
int fragmentSize; int fragmentSize;
final ReentrantLock recordLock = new ReentrantLock();
InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) { InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
this.readCipher = readCipher; this.readCipher = readCipher;
this.helloVersion = ProtocolVersion.TLS10; this.helloVersion = ProtocolVersion.TLS10;
@ -92,14 +95,19 @@ abstract class InputRecord implements Record, Closeable {
* and flag the record as holding no data. * and flag the record as holding no data.
*/ */
@Override @Override
public synchronized void close() throws IOException { public void close() throws IOException {
if (!isClosed) { recordLock.lock();
isClosed = true; try {
readCipher.dispose(); if (!isClosed) {
isClosed = true;
readCipher.dispose();
}
} finally {
recordLock.unlock();
} }
} }
synchronized boolean isClosed() { boolean isClosed() {
return isClosed; return isClosed;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,6 +30,7 @@ import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantLock;
import sun.security.ssl.SSLCipher.SSLWriteCipher; import sun.security.ssl.SSLCipher.SSLWriteCipher;
/** /**
@ -68,6 +69,8 @@ abstract class OutputRecord
// closed or not? // closed or not?
volatile boolean isClosed; volatile boolean isClosed;
final ReentrantLock recordLock = new ReentrantLock();
/* /*
* Mappings from V3 cipher suite encodings to their pure V2 equivalents. * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* This is taken from the SSL V3 specification, Appendix E. * This is taken from the SSL V3 specification, Appendix E.
@ -89,15 +92,25 @@ abstract class OutputRecord
// Please set packetSize and protocolVersion in the implementation. // Please set packetSize and protocolVersion in the implementation.
} }
synchronized void setVersion(ProtocolVersion protocolVersion) { void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion; recordLock.lock();
try {
this.protocolVersion = protocolVersion;
} finally {
recordLock.unlock();
}
} }
/* /*
* Updates helloVersion of this record. * Updates helloVersion of this record.
*/ */
synchronized void setHelloVersion(ProtocolVersion helloVersion) { void setHelloVersion(ProtocolVersion helloVersion) {
this.helloVersion = helloVersion; recordLock.lock();
try {
this.helloVersion = helloVersion;
} finally {
recordLock.unlock();
}
} }
/* /*
@ -108,9 +121,14 @@ abstract class OutputRecord
return false; return false;
} }
synchronized boolean seqNumIsHuge() { boolean seqNumIsHuge() {
return (writeCipher.authenticator != null) && recordLock.lock();
try {
return (writeCipher.authenticator != null) &&
writeCipher.authenticator.seqNumIsHuge(); writeCipher.authenticator.seqNumIsHuge();
} finally {
recordLock.unlock();
}
} }
// SSLEngine and SSLSocket // SSLEngine and SSLSocket
@ -148,68 +166,93 @@ abstract class OutputRecord
} }
// Change write ciphers, may use change_cipher_spec record. // Change write ciphers, may use change_cipher_spec record.
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher, void changeWriteCiphers(SSLWriteCipher writeCipher,
boolean useChangeCipherSpec) throws IOException { boolean useChangeCipherSpec) throws IOException {
if (isClosed()) { recordLock.lock();
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { try {
SSLLogger.warning("outbound has closed, ignore outbound " + if (isClosed()) {
"change_cipher_spec message"); if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"change_cipher_spec message");
}
return;
} }
return;
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
} finally {
recordLock.unlock();
} }
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
} }
// Change write ciphers using key_update handshake message. // Change write ciphers using key_update handshake message.
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher, void changeWriteCiphers(SSLWriteCipher writeCipher,
byte keyUpdateRequest) throws IOException { byte keyUpdateRequest) throws IOException {
if (isClosed()) { recordLock.lock();
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { try {
SSLLogger.warning("outbound has closed, ignore outbound " + if (isClosed()) {
"key_update handshake message"); if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"key_update handshake message");
}
return;
} }
return;
// encode the handshake message, KeyUpdate
byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
hm[hm.length - 1] = keyUpdateRequest;
encodeHandshake(hm, 0, hm.length);
flush();
// Dispose of any intermediate state in the underlying cipher.
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
} finally {
recordLock.unlock();
} }
// encode the handshake message, KeyUpdate
byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
hm[hm.length - 1] = keyUpdateRequest;
encodeHandshake(hm, 0, hm.length);
flush();
// Dispose of any intermediate state in the underlying cipher.
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
} }
synchronized void changePacketSize(int packetSize) { void changePacketSize(int packetSize) {
this.packetSize = packetSize; recordLock.lock();
try {
this.packetSize = packetSize;
} finally {
recordLock.unlock();
}
} }
synchronized void changeFragmentSize(int fragmentSize) { void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize; recordLock.lock();
try {
this.fragmentSize = fragmentSize;
} finally {
recordLock.unlock();
}
} }
synchronized int getMaxPacketSize() { int getMaxPacketSize() {
return packetSize; recordLock.lock();
try {
return packetSize;
} finally {
recordLock.unlock();
}
} }
// apply to DTLS SSLEngine // apply to DTLS SSLEngine
@ -228,13 +271,18 @@ abstract class OutputRecord
} }
@Override @Override
public synchronized void close() throws IOException { public void close() throws IOException {
if (isClosed) { recordLock.lock();
return; try {
} if (isClosed) {
return;
}
isClosed = true; isClosed = true;
writeCipher.dispose(); writeCipher.dispose();
} finally {
recordLock.unlock();
}
} }
boolean isClosed() { boolean isClosed() {

View File

@ -30,6 +30,7 @@ import java.net.Socket;
import java.security.*; import java.security.*;
import java.security.cert.*; import java.security.cert.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.*; import javax.net.ssl.*;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
import sun.security.provider.certpath.AlgorithmChecker; import sun.security.provider.certpath.AlgorithmChecker;
@ -69,6 +70,8 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private volatile StatusResponseManager statusResponseManager; private volatile StatusResponseManager statusResponseManager;
private final ReentrantLock contextLock = new ReentrantLock();
SSLContextImpl() { SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager(); ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl(); clientCache = new SSLSessionContextImpl();
@ -230,11 +233,14 @@ public abstract class SSLContextImpl extends SSLContextSpi {
// Used for DTLS in server mode only. // Used for DTLS in server mode only.
HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) { HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) {
if (helloCookieManagerBuilder == null) { if (helloCookieManagerBuilder == null) {
synchronized (this) { contextLock.lock();
try {
if (helloCookieManagerBuilder == null) { if (helloCookieManagerBuilder == null) {
helloCookieManagerBuilder = helloCookieManagerBuilder =
new HelloCookieManager.Builder(secureRandom); new HelloCookieManager.Builder(secureRandom);
} }
} finally {
contextLock.unlock();
} }
} }
@ -243,7 +249,8 @@ public abstract class SSLContextImpl extends SSLContextSpi {
StatusResponseManager getStatusResponseManager() { StatusResponseManager getStatusResponseManager() {
if (serverEnableStapling && statusResponseManager == null) { if (serverEnableStapling && statusResponseManager == null) {
synchronized (this) { contextLock.lock();
try {
if (statusResponseManager == null) { if (statusResponseManager == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
SSLLogger.finest( SSLLogger.finest(
@ -251,6 +258,8 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
statusResponseManager = new StatusResponseManager(); statusResponseManager = new StatusResponseManager();
} }
} finally {
contextLock.unlock();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -33,6 +33,7 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
@ -54,6 +55,7 @@ import javax.net.ssl.SSLSession;
final class SSLEngineImpl extends SSLEngine implements SSLTransport { final class SSLEngineImpl extends SSLEngine implements SSLTransport {
private final SSLContextImpl sslContext; private final SSLContextImpl sslContext;
final TransportContext conContext; final TransportContext conContext;
private final ReentrantLock engineLock = new ReentrantLock();
/** /**
* Constructor for an SSLEngine from SSLContext, without * Constructor for an SSLEngine from SSLContext, without
@ -93,57 +95,68 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
} }
@Override @Override
public synchronized void beginHandshake() throws SSLException { public void beginHandshake() throws SSLException {
if (conContext.isUnsureMode) { engineLock.lock();
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
try { try {
conContext.kickstart(); if (conContext.isUnsureMode) {
} catch (IOException ioe) { throw new IllegalStateException(
throw conContext.fatal(Alert.HANDSHAKE_FAILURE, "Client/Server mode has not yet been set.");
"Couldn't kickstart handshaking", ioe); }
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR, try {
"Fail to begin handshake", ex); conContext.kickstart();
} catch (IOException ioe) {
throw conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Couldn't kickstart handshaking", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to begin handshake", ex);
}
} finally {
engineLock.unlock();
} }
} }
@Override @Override
public synchronized SSLEngineResult wrap(ByteBuffer[] appData, public SSLEngineResult wrap(ByteBuffer[] appData,
int offset, int length, ByteBuffer netData) throws SSLException { int offset, int length, ByteBuffer netData) throws SSLException {
return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1); return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1);
} }
// @Override // @Override
public synchronized SSLEngineResult wrap( public SSLEngineResult wrap(
ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException { ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
if (conContext.isUnsureMode) { engineLock.lock();
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
// See if the handshaker needs to report back some SSLException.
checkTaskThrown();
// check parameters
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
try { try {
return writeRecord( if (conContext.isUnsureMode) {
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); throw new IllegalStateException(
} catch (SSLProtocolException spe) { "Client/Server mode has not yet been set.");
// may be an unexpected handshake message }
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe);
} catch (IOException ioe) { // See if the handshaker needs to report back some SSLException.
throw conContext.fatal(Alert.INTERNAL_ERROR, checkTaskThrown();
"problem wrapping app data", ioe);
} catch (Exception ex) { // including RuntimeException // check parameters
throw conContext.fatal(Alert.INTERNAL_ERROR, checkParams(srcs, srcsOffset, srcsLength,
"Fail to wrap application data", ex); dsts, dstsOffset, dstsLength);
try {
return writeRecord(
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
} catch (SSLProtocolException spe) {
// may be an unexpected handshake message
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe);
} catch (IOException ioe) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"problem wrapping app data", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to wrap application data", ex);
}
} finally {
engineLock.unlock();
} }
} }
@ -428,47 +441,53 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
} }
@Override @Override
public synchronized SSLEngineResult unwrap(ByteBuffer src, public SSLEngineResult unwrap(ByteBuffer src,
ByteBuffer[] dsts, int offset, int length) throws SSLException { ByteBuffer[] dsts, int offset, int length) throws SSLException {
return unwrap( return unwrap(
new ByteBuffer[]{src}, 0, 1, dsts, offset, length); new ByteBuffer[]{src}, 0, 1, dsts, offset, length);
} }
// @Override // @Override
public synchronized SSLEngineResult unwrap( public SSLEngineResult unwrap(
ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException { ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
if (conContext.isUnsureMode) { engineLock.lock();
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
// See if the handshaker needs to report back some SSLException.
checkTaskThrown();
// check parameters
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
try { try {
return readRecord( if (conContext.isUnsureMode) {
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); throw new IllegalStateException(
} catch (SSLProtocolException spe) { "Client/Server mode has not yet been set.");
// may be an unexpected handshake message }
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
spe.getMessage(), spe); // See if the handshaker needs to report back some SSLException.
} catch (IOException ioe) { checkTaskThrown();
/*
* Don't reset position so it looks like we didn't // check parameters
* consume anything. We did consume something, and it checkParams(srcs, srcsOffset, srcsLength,
* got us into this situation, so report that much back. dsts, dstsOffset, dstsLength);
* Our days of consuming are now over anyway.
*/ try {
throw conContext.fatal(Alert.INTERNAL_ERROR, return readRecord(
"problem unwrapping net record", ioe); srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
} catch (Exception ex) { // including RuntimeException } catch (SSLProtocolException spe) {
throw conContext.fatal(Alert.INTERNAL_ERROR, // may be an unexpected handshake message
"Fail to unwrap network record", ex); throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
spe.getMessage(), spe);
} catch (IOException ioe) {
/*
* Don't reset position so it looks like we didn't
* consume anything. We did consume something, and it
* got us into this situation, so report that much back.
* Our days of consuming are now over anyway.
*/
throw conContext.fatal(Alert.INTERNAL_ERROR,
"problem unwrapping net record", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to unwrap network record", ex);
}
} finally {
engineLock.unlock();
} }
} }
@ -703,61 +722,87 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
} }
@Override @Override
public synchronized Runnable getDelegatedTask() { public Runnable getDelegatedTask() {
if (conContext.handshakeContext != null && // PRE or POST handshake engineLock.lock();
!conContext.handshakeContext.taskDelegated && try {
!conContext.handshakeContext.delegatedActions.isEmpty()) { if (conContext.handshakeContext != null && // PRE or POST handshake
conContext.handshakeContext.taskDelegated = true; !conContext.handshakeContext.taskDelegated &&
return new DelegatedTask(this); !conContext.handshakeContext.delegatedActions.isEmpty()) {
conContext.handshakeContext.taskDelegated = true;
return new DelegatedTask(this);
}
} finally {
engineLock.unlock();
} }
return null; return null;
} }
@Override @Override
public synchronized void closeInbound() throws SSLException { public void closeInbound() throws SSLException {
if (isInboundDone()) { engineLock.lock();
return; try {
if (isInboundDone()) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("Closing inbound of SSLEngine");
}
// Is it ready to close inbound?
//
// No exception if the initial handshake is not started.
if (!conContext.isInputCloseNotified &&
(conContext.isNegotiated ||
conContext.handshakeContext != null)) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"closing inbound before receiving peer's close_notify");
}
conContext.closeInbound();
} finally {
engineLock.unlock();
} }
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("Closing inbound of SSLEngine");
}
// Is it ready to close inbound?
//
// No need to throw exception if the initial handshake is not started.
if (!conContext.isInputCloseNotified &&
(conContext.isNegotiated || conContext.handshakeContext != null)) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"closing inbound before receiving peer's close_notify");
}
conContext.closeInbound();
} }
@Override @Override
public synchronized boolean isInboundDone() { public boolean isInboundDone() {
return conContext.isInboundClosed(); engineLock.lock();
try {
return conContext.isInboundClosed();
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized void closeOutbound() { public void closeOutbound() {
if (conContext.isOutboundClosed()) { engineLock.lock();
return; try {
} if (conContext.isOutboundClosed()) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("Closing outbound of SSLEngine"); SSLLogger.finest("Closing outbound of SSLEngine");
} }
conContext.closeOutbound(); conContext.closeOutbound();
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized boolean isOutboundDone() { public boolean isOutboundDone() {
return conContext.isOutboundDone(); engineLock.lock();
try {
return conContext.isOutboundDone();
} finally {
engineLock.unlock();
}
} }
@Override @Override
@ -766,14 +811,24 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
} }
@Override @Override
public synchronized String[] getEnabledCipherSuites() { public String[] getEnabledCipherSuites() {
return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites); engineLock.lock();
try {
return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites);
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized void setEnabledCipherSuites(String[] suites) { public void setEnabledCipherSuites(String[] suites) {
conContext.sslConfig.enabledCipherSuites = engineLock.lock();
CipherSuite.validValuesOf(suites); try {
conContext.sslConfig.enabledCipherSuites =
CipherSuite.validValuesOf(suites);
} finally {
engineLock.unlock();
}
} }
@Override @Override
@ -783,119 +838,214 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
} }
@Override @Override
public synchronized String[] getEnabledProtocols() { public String[] getEnabledProtocols() {
return ProtocolVersion.toStringArray( engineLock.lock();
conContext.sslConfig.enabledProtocols); try {
} return ProtocolVersion.toStringArray(
conContext.sslConfig.enabledProtocols);
@Override } finally {
public synchronized void setEnabledProtocols(String[] protocols) { engineLock.unlock();
if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
}
conContext.sslConfig.enabledProtocols =
ProtocolVersion.namesOf(protocols);
}
@Override
public synchronized SSLSession getSession() {
return conContext.conSession;
}
@Override
public synchronized SSLSession getHandshakeSession() {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.handshakeSession;
}
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
return conContext.getHandshakeStatus();
}
@Override
public synchronized void setUseClientMode(boolean mode) {
conContext.setUseClientMode(mode);
}
@Override
public synchronized boolean getUseClientMode() {
return conContext.sslConfig.isClientMode;
}
@Override
public synchronized void setNeedClientAuth(boolean need) {
conContext.sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getNeedClientAuth() {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED);
}
@Override
public synchronized void setWantClientAuth(boolean want) {
conContext.sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getWantClientAuth() {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@Override
public synchronized void setEnableSessionCreation(boolean flag) {
conContext.sslConfig.enableSessionCreation = flag;
}
@Override
public synchronized boolean getEnableSessionCreation() {
return conContext.sslConfig.enableSessionCreation;
}
@Override
public synchronized SSLParameters getSSLParameters() {
return conContext.sslConfig.getSSLParameters();
}
@Override
public synchronized void setSSLParameters(SSLParameters params) {
conContext.sslConfig.setSSLParameters(params);
if (conContext.sslConfig.maximumPacketSize != 0) {
conContext.outputRecord.changePacketSize(
conContext.sslConfig.maximumPacketSize);
} }
} }
@Override @Override
public synchronized String getApplicationProtocol() { public void setEnabledProtocols(String[] protocols) {
return conContext.applicationProtocol; engineLock.lock();
try {
if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
}
conContext.sslConfig.enabledProtocols =
ProtocolVersion.namesOf(protocols);
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized String getHandshakeApplicationProtocol() { public SSLSession getSession() {
return conContext.handshakeContext == null ? engineLock.lock();
null : conContext.handshakeContext.applicationProtocol; try {
return conContext.conSession;
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized void setHandshakeApplicationProtocolSelector( public SSLSession getHandshakeSession() {
engineLock.lock();
try {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.handshakeSession;
} finally {
engineLock.unlock();
}
}
@Override
public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
engineLock.lock();
try {
return conContext.getHandshakeStatus();
} finally {
engineLock.unlock();
}
}
@Override
public void setUseClientMode(boolean mode) {
engineLock.lock();
try {
conContext.setUseClientMode(mode);
} finally {
engineLock.unlock();
}
}
@Override
public boolean getUseClientMode() {
engineLock.lock();
try {
return conContext.sslConfig.isClientMode;
} finally {
engineLock.unlock();
}
}
@Override
public void setNeedClientAuth(boolean need) {
engineLock.lock();
try {
conContext.sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
engineLock.unlock();
}
}
@Override
public boolean getNeedClientAuth() {
engineLock.lock();
try {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED);
} finally {
engineLock.unlock();
}
}
@Override
public void setWantClientAuth(boolean want) {
engineLock.lock();
try {
conContext.sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
engineLock.unlock();
}
}
@Override
public boolean getWantClientAuth() {
engineLock.lock();
try {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
} finally {
engineLock.unlock();
}
}
@Override
public void setEnableSessionCreation(boolean flag) {
engineLock.lock();
try {
conContext.sslConfig.enableSessionCreation = flag;
} finally {
engineLock.unlock();
}
}
@Override
public boolean getEnableSessionCreation() {
engineLock.lock();
try {
return conContext.sslConfig.enableSessionCreation;
} finally {
engineLock.unlock();
}
}
@Override
public SSLParameters getSSLParameters() {
engineLock.lock();
try {
return conContext.sslConfig.getSSLParameters();
} finally {
engineLock.unlock();
}
}
@Override
public void setSSLParameters(SSLParameters params) {
engineLock.lock();
try {
conContext.sslConfig.setSSLParameters(params);
if (conContext.sslConfig.maximumPacketSize != 0) {
conContext.outputRecord.changePacketSize(
conContext.sslConfig.maximumPacketSize);
}
} finally {
engineLock.unlock();
}
}
@Override
public String getApplicationProtocol() {
engineLock.lock();
try {
return conContext.applicationProtocol;
} finally {
engineLock.unlock();
}
}
@Override
public String getHandshakeApplicationProtocol() {
engineLock.lock();
try {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.applicationProtocol;
} finally {
engineLock.unlock();
}
}
@Override
public void setHandshakeApplicationProtocolSelector(
BiFunction<SSLEngine, List<String>, String> selector) { BiFunction<SSLEngine, List<String>, String> selector) {
conContext.sslConfig.engineAPSelector = selector; engineLock.lock();
try {
conContext.sslConfig.engineAPSelector = selector;
} finally {
engineLock.unlock();
}
} }
@Override @Override
public synchronized BiFunction<SSLEngine, List<String>, String> public BiFunction<SSLEngine, List<String>, String>
getHandshakeApplicationProtocolSelector() { getHandshakeApplicationProtocolSelector() {
return conContext.sslConfig.engineAPSelector; engineLock.lock();
try {
return conContext.sslConfig.engineAPSelector;
} finally {
engineLock.unlock();
}
} }
@Override @Override
@ -909,38 +1059,42 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
* null, report back the Exception that happened in the delegated * null, report back the Exception that happened in the delegated
* task(s). * task(s).
*/ */
private synchronized void checkTaskThrown() throws SSLException { private void checkTaskThrown() throws SSLException {
Exception exc = null; Exception exc = null;
engineLock.lock();
// First check the handshake context. try {
HandshakeContext hc = conContext.handshakeContext; // First check the handshake context.
if ((hc != null) && (hc.delegatedThrown != null)) { HandshakeContext hc = conContext.handshakeContext;
exc = hc.delegatedThrown; if ((hc != null) && (hc.delegatedThrown != null)) {
hc.delegatedThrown = null; exc = hc.delegatedThrown;
} hc.delegatedThrown = null;
/*
* hc.delegatedThrown and conContext.delegatedThrown are most likely
* the same, but it's possible we could have had a non-fatal
* exception and thus the new HandshakeContext is still valid
* (alert warning). If so, then we may have a secondary exception
* waiting to be reported from the TransportContext, so we will
* need to clear that on a successive call. Otherwise, clear it now.
*/
if (conContext.delegatedThrown != null) {
if (exc != null) {
// hc object comparison
if (conContext.delegatedThrown == exc) {
// clear if/only if both are the same
conContext.delegatedThrown = null;
} // otherwise report the hc delegatedThrown
} else {
// Nothing waiting in HandshakeContext, but one is in the
// TransportContext.
exc = conContext.delegatedThrown;
conContext.delegatedThrown = null;
} }
/*
* hc.delegatedThrown and conContext.delegatedThrown are most
* likely the same, but it's possible we could have had a non-fatal
* exception and thus the new HandshakeContext is still valid
* (alert warning). If so, then we may have a secondary exception
* waiting to be reported from the TransportContext, so we will
* need to clear that on a successive call. Otherwise, clear it now.
*/
if (conContext.delegatedThrown != null) {
if (exc != null) {
// hc object comparison
if (conContext.delegatedThrown == exc) {
// clear if/only if both are the same
conContext.delegatedThrown = null;
} // otherwise report the hc delegatedThrown
} else {
// Nothing waiting in HandshakeContext, but one is in the
// TransportContext.
exc = conContext.delegatedThrown;
conContext.delegatedThrown = null;
}
}
} finally {
engineLock.unlock();
} }
// Anything to report? // Anything to report?
@ -998,7 +1152,8 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
@Override @Override
public void run() { public void run() {
synchronized (engine) { engine.engineLock.lock();
try {
HandshakeContext hc = engine.conContext.handshakeContext; HandshakeContext hc = engine.conContext.handshakeContext;
if (hc == null || hc.delegatedActions.isEmpty()) { if (hc == null || hc.delegatedActions.isEmpty()) {
return; return;
@ -1055,6 +1210,8 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
if (hc != null) { if (hc != null) {
hc.taskDelegated = false; hc.taskDelegated = false;
} }
} finally {
engine.engineLock.unlock();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -51,13 +51,18 @@ final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
} }
@Override @Override
public synchronized void close() throws IOException { public void close() throws IOException {
if (!isClosed) { recordLock.lock();
if (fragmenter != null && fragmenter.hasAlert()) { try {
isCloseWaiting = true; if (!isClosed) {
} else { if (fragmenter != null && fragmenter.hasAlert()) {
super.close(); isCloseWaiting = true;
} else {
super.close();
}
} }
} finally {
recordLock.unlock();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,6 +28,7 @@ package sun.security.ssl;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocket;
@ -56,6 +57,7 @@ import javax.net.ssl.SSLServerSocket;
final class SSLServerSocketImpl extends SSLServerSocket { final class SSLServerSocketImpl extends SSLServerSocket {
private final SSLContextImpl sslContext; private final SSLContextImpl sslContext;
private final SSLConfiguration sslConfig; private final SSLConfiguration sslConfig;
private final ReentrantLock serverSocketLock = new ReentrantLock();
SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException { SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException {
@ -84,14 +86,24 @@ final class SSLServerSocketImpl extends SSLServerSocket {
} }
@Override @Override
public synchronized String[] getEnabledCipherSuites() { public String[] getEnabledCipherSuites() {
return CipherSuite.namesOf(sslConfig.enabledCipherSuites); serverSocketLock.lock();
try {
return CipherSuite.namesOf(sslConfig.enabledCipherSuites);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized void setEnabledCipherSuites(String[] suites) { public void setEnabledCipherSuites(String[] suites) {
sslConfig.enabledCipherSuites = serverSocketLock.lock();
CipherSuite.validValuesOf(suites); try {
sslConfig.enabledCipherSuites =
CipherSuite.validValuesOf(suites);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
@ -106,93 +118,153 @@ final class SSLServerSocketImpl extends SSLServerSocket {
} }
@Override @Override
public synchronized String[] getEnabledProtocols() { public String[] getEnabledProtocols() {
return ProtocolVersion.toStringArray(sslConfig.enabledProtocols); serverSocketLock.lock();
} try {
return ProtocolVersion.toStringArray(sslConfig.enabledProtocols);
@Override } finally {
public synchronized void setEnabledProtocols(String[] protocols) { serverSocketLock.unlock();
if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
} }
sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols);
} }
@Override @Override
public synchronized void setNeedClientAuth(boolean need) { public void setEnabledProtocols(String[] protocols) {
sslConfig.clientAuthType = serverSocketLock.lock();
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED : try {
ClientAuthType.CLIENT_AUTH_NONE); if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
}
sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getNeedClientAuth() { public void setNeedClientAuth(boolean need) {
return (sslConfig.clientAuthType == serverSocketLock.lock();
try {
sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
serverSocketLock.unlock();
}
}
@Override
public boolean getNeedClientAuth() {
serverSocketLock.lock();
try {
return (sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED); ClientAuthType.CLIENT_AUTH_REQUIRED);
} } finally {
serverSocketLock.unlock();
@Override
public synchronized void setWantClientAuth(boolean want) {
sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getWantClientAuth() {
return (sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@Override
public synchronized void setUseClientMode(boolean useClientMode) {
/*
* If we need to change the client mode and the enabled
* protocols and cipher suites haven't specifically been
* set by the user, change them to the corresponding
* default ones.
*/
if (sslConfig.isClientMode != useClientMode) {
if (sslContext.isDefaultProtocolVesions(
sslConfig.enabledProtocols)) {
sslConfig.enabledProtocols =
sslContext.getDefaultProtocolVersions(!useClientMode);
}
if (sslContext.isDefaultCipherSuiteList(
sslConfig.enabledCipherSuites)) {
sslConfig.enabledCipherSuites =
sslContext.getDefaultCipherSuites(!useClientMode);
}
sslConfig.isClientMode = useClientMode;
} }
} }
@Override @Override
public synchronized boolean getUseClientMode() { public void setWantClientAuth(boolean want) {
return sslConfig.isClientMode; serverSocketLock.lock();
try {
sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized void setEnableSessionCreation(boolean flag) { public boolean getWantClientAuth() {
sslConfig.enableSessionCreation = flag; serverSocketLock.lock();
try {
return (sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getEnableSessionCreation() { public void setUseClientMode(boolean useClientMode) {
return sslConfig.enableSessionCreation; serverSocketLock.lock();
try {
/*
* If we need to change the client mode and the enabled
* protocols and cipher suites haven't specifically been
* set by the user, change them to the corresponding
* default ones.
*/
if (sslConfig.isClientMode != useClientMode) {
if (sslContext.isDefaultProtocolVesions(
sslConfig.enabledProtocols)) {
sslConfig.enabledProtocols =
sslContext.getDefaultProtocolVersions(!useClientMode);
}
if (sslContext.isDefaultCipherSuiteList(
sslConfig.enabledCipherSuites)) {
sslConfig.enabledCipherSuites =
sslContext.getDefaultCipherSuites(!useClientMode);
}
sslConfig.isClientMode = useClientMode;
}
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized SSLParameters getSSLParameters() { public boolean getUseClientMode() {
return sslConfig.getSSLParameters(); serverSocketLock.lock();
try {
return sslConfig.isClientMode;
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override
public synchronized void setSSLParameters(SSLParameters params) { public void setEnableSessionCreation(boolean flag) {
sslConfig.setSSLParameters(params); serverSocketLock.lock();
try {
sslConfig.enableSessionCreation = flag;
} finally {
serverSocketLock.unlock();
}
}
@Override
public boolean getEnableSessionCreation() {
serverSocketLock.lock();
try {
return sslConfig.enableSessionCreation;
} finally {
serverSocketLock.unlock();
}
}
@Override
public SSLParameters getSSLParameters() {
serverSocketLock.lock();
try {
return sslConfig.getSSLParameters();
} finally {
serverSocketLock.unlock();
}
}
@Override
public void setSSLParameters(SSLParameters params) {
serverSocketLock.lock();
try {
sslConfig.setSSLParameters(params);
} finally {
serverSocketLock.unlock();
}
} }
@Override @Override

View File

@ -38,6 +38,7 @@ import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.net.ssl.ExtendedSSLSession; import javax.net.ssl.ExtendedSSLSession;
import javax.net.ssl.SNIServerName; import javax.net.ssl.SNIServerName;
@ -133,7 +134,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
// The endpoint identification algorithm used to check certificates // The endpoint identification algorithm used to check certificates
// in this session. // in this session.
private final String identificationProtocol; private final String identificationProtocol;
private final ReentrantLock sessionLock = new ReentrantLock();
/* /*
* Create a new non-rejoinable session, using the default (null) * Create a new non-rejoinable session, using the default (null)
@ -289,15 +292,22 @@ final class SSLSessionImpl extends ExtendedSSLSession {
return resumptionMasterSecret; return resumptionMasterSecret;
} }
synchronized SecretKey getPreSharedKey() { SecretKey getPreSharedKey() {
return preSharedKey; sessionLock.lock();
try {
return preSharedKey;
} finally {
sessionLock.unlock();
}
} }
synchronized SecretKey consumePreSharedKey() { SecretKey consumePreSharedKey() {
sessionLock.lock();
try { try {
return preSharedKey; return preSharedKey;
} finally { } finally {
preSharedKey = null; preSharedKey = null;
sessionLock.unlock();
} }
} }
@ -313,11 +323,13 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* be used once. This method will return the identity and then clear it * be used once. This method will return the identity and then clear it
* so it cannot be used again. * so it cannot be used again.
*/ */
synchronized byte[] consumePskIdentity() { byte[] consumePskIdentity() {
sessionLock.lock();
try { try {
return pskIdentity; return pskIdentity;
} finally { } finally {
pskIdentity = null; pskIdentity = null;
sessionLock.unlock();
} }
} }
@ -393,8 +405,13 @@ final class SSLSessionImpl extends ExtendedSSLSession {
} }
@Override @Override
public synchronized boolean isValid() { public boolean isValid() {
return isRejoinable(); sessionLock.lock();
try {
return isRejoinable();
} finally {
sessionLock.unlock();
}
} }
/** /**
@ -777,29 +794,35 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* no connections will be able to rejoin this session. * no connections will be able to rejoin this session.
*/ */
@Override @Override
public synchronized void invalidate() { public void invalidate() {
// sessionLock.lock();
// Can't invalidate the NULL session -- this would be try {
// attempted when we get a handshaking error on a brand //
// new connection, with no "real" session yet. // Can't invalidate the NULL session -- this would be
// // attempted when we get a handshaking error on a brand
if (this == nullSession) { // new connection, with no "real" session yet.
return; //
} if (this == nullSession) {
return;
}
if (context != null) { if (context != null) {
context.remove(sessionId); context.remove(sessionId);
context = null; context = null;
} }
if (invalidated) {
return; if (invalidated) {
} return;
invalidated = true; }
if (SSLLogger.isOn && SSLLogger.isOn("session")) { invalidated = true;
SSLLogger.finest("Invalidated session: " + this); if (SSLLogger.isOn && SSLLogger.isOn("session")) {
} SSLLogger.finest("Invalidated session: " + this);
for (SSLSessionImpl child : childSessions) { }
child.invalidate(); for (SSLSessionImpl child : childSessions) {
child.invalidate();
}
} finally {
sessionLock.unlock();
} }
} }
@ -912,8 +935,13 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* Expand the buffer size of both SSL/TLS network packet and * Expand the buffer size of both SSL/TLS network packet and
* application data. * application data.
*/ */
protected synchronized void expandBufferSizes() { protected void expandBufferSizes() {
acceptLargeFragments = true; sessionLock.lock();
try {
acceptLargeFragments = true;
} finally {
sessionLock.unlock();
}
} }
/** /**
@ -921,30 +949,35 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* when using this session. * when using this session.
*/ */
@Override @Override
public synchronized int getPacketBufferSize() { public int getPacketBufferSize() {
// Use the bigger packet size calculated from maximumPacketSize sessionLock.lock();
// and negotiatedMaxFragLen. try {
int packetSize = 0; // Use the bigger packet size calculated from maximumPacketSize
if (negotiatedMaxFragLen > 0) { // and negotiatedMaxFragLen.
packetSize = cipherSuite.calculatePacketSize( int packetSize = 0;
negotiatedMaxFragLen, protocolVersion, if (negotiatedMaxFragLen > 0) {
protocolVersion.isDTLS); packetSize = cipherSuite.calculatePacketSize(
} negotiatedMaxFragLen, protocolVersion,
protocolVersion.isDTLS);
}
if (maximumPacketSize > 0) { if (maximumPacketSize > 0) {
return (maximumPacketSize > packetSize) ? return (maximumPacketSize > packetSize) ?
maximumPacketSize : packetSize; maximumPacketSize : packetSize;
} }
if (packetSize != 0) { if (packetSize != 0) {
return packetSize; return packetSize;
} }
if (protocolVersion.isDTLS) { if (protocolVersion.isDTLS) {
return DTLSRecord.maxRecordSize; return DTLSRecord.maxRecordSize;
} else { } else {
return acceptLargeFragments ? return acceptLargeFragments ?
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize; SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
}
} finally {
sessionLock.unlock();
} }
} }
@ -953,31 +986,36 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* expected when using this session. * expected when using this session.
*/ */
@Override @Override
public synchronized int getApplicationBufferSize() { public int getApplicationBufferSize() {
// Use the bigger fragment size calculated from maximumPacketSize sessionLock.lock();
// and negotiatedMaxFragLen. try {
int fragmentSize = 0; // Use the bigger fragment size calculated from maximumPacketSize
if (maximumPacketSize > 0) { // and negotiatedMaxFragLen.
fragmentSize = cipherSuite.calculateFragSize( int fragmentSize = 0;
maximumPacketSize, protocolVersion, if (maximumPacketSize > 0) {
protocolVersion.isDTLS); fragmentSize = cipherSuite.calculateFragSize(
} maximumPacketSize, protocolVersion,
protocolVersion.isDTLS);
}
if (negotiatedMaxFragLen > 0) { if (negotiatedMaxFragLen > 0) {
return (negotiatedMaxFragLen > fragmentSize) ? return (negotiatedMaxFragLen > fragmentSize) ?
negotiatedMaxFragLen : fragmentSize; negotiatedMaxFragLen : fragmentSize;
} }
if (fragmentSize != 0) { if (fragmentSize != 0) {
return fragmentSize; return fragmentSize;
} }
if (protocolVersion.isDTLS) { if (protocolVersion.isDTLS) {
return Record.maxDataSize; return Record.maxDataSize;
} else { } else {
int maxPacketSize = acceptLargeFragments ? int maxPacketSize = acceptLargeFragments ?
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize; SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
return (maxPacketSize - SSLRecord.headerSize); return (maxPacketSize - SSLRecord.headerSize);
}
} finally {
sessionLock.unlock();
} }
} }
@ -989,10 +1027,14 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* the negotiated maximum fragment length, or {@code -1} if * the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated. * no such length has been negotiated.
*/ */
synchronized void setNegotiatedMaxFragSize( void setNegotiatedMaxFragSize(
int negotiatedMaxFragLen) { int negotiatedMaxFragLen) {
sessionLock.lock();
this.negotiatedMaxFragLen = negotiatedMaxFragLen; try {
this.negotiatedMaxFragLen = negotiatedMaxFragLen;
} finally {
sessionLock.unlock();
}
} }
/** /**
@ -1002,16 +1044,31 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* @return the negotiated maximum fragment length, or {@code -1} if * @return the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated. * no such length has been negotiated.
*/ */
synchronized int getNegotiatedMaxFragSize() { int getNegotiatedMaxFragSize() {
return negotiatedMaxFragLen; sessionLock.lock();
try {
return negotiatedMaxFragLen;
} finally {
sessionLock.unlock();
}
} }
synchronized void setMaximumPacketSize(int maximumPacketSize) { void setMaximumPacketSize(int maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize; sessionLock.lock();
try {
this.maximumPacketSize = maximumPacketSize;
} finally {
sessionLock.unlock();
}
} }
synchronized int getMaximumPacketSize() { int getMaximumPacketSize() {
return maximumPacketSize; sessionLock.lock();
try {
return maximumPacketSize;
} finally {
sessionLock.unlock();
}
} }
/** /**

View File

@ -38,6 +38,7 @@ import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
@ -84,6 +85,9 @@ public final class SSLSocketImpl
private boolean isConnected = false; private boolean isConnected = false;
private volatile boolean tlsIsClosed = false; private volatile boolean tlsIsClosed = false;
private final ReentrantLock socketLock = new ReentrantLock();
private final ReentrantLock handshakeLock = new ReentrantLock();
/* /*
* Is the local name service trustworthy? * Is the local name service trustworthy?
* *
@ -292,14 +296,25 @@ public final class SSLSocketImpl
} }
@Override @Override
public synchronized String[] getEnabledCipherSuites() { public String[] getEnabledCipherSuites() {
return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites); socketLock.lock();
try {
return CipherSuite.namesOf(
conContext.sslConfig.enabledCipherSuites);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void setEnabledCipherSuites(String[] suites) { public void setEnabledCipherSuites(String[] suites) {
conContext.sslConfig.enabledCipherSuites = socketLock.lock();
CipherSuite.validValuesOf(suites); try {
conContext.sslConfig.enabledCipherSuites =
CipherSuite.validValuesOf(suites);
} finally {
socketLock.unlock();
}
} }
@Override @Override
@ -309,19 +324,29 @@ public final class SSLSocketImpl
} }
@Override @Override
public synchronized String[] getEnabledProtocols() { public String[] getEnabledProtocols() {
return ProtocolVersion.toStringArray( socketLock.lock();
conContext.sslConfig.enabledProtocols); try {
return ProtocolVersion.toStringArray(
conContext.sslConfig.enabledProtocols);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void setEnabledProtocols(String[] protocols) { public void setEnabledProtocols(String[] protocols) {
if (protocols == null) { if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null"); throw new IllegalArgumentException("Protocols cannot be null");
} }
conContext.sslConfig.enabledProtocols = socketLock.lock();
ProtocolVersion.namesOf(protocols); try {
conContext.sslConfig.enabledProtocols =
ProtocolVersion.namesOf(protocols);
} finally {
socketLock.unlock();
}
} }
@Override @Override
@ -341,29 +366,44 @@ public final class SSLSocketImpl
} }
@Override @Override
public synchronized SSLSession getHandshakeSession() { public SSLSession getHandshakeSession() {
return conContext.handshakeContext == null ? socketLock.lock();
null : conContext.handshakeContext.handshakeSession; try {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.handshakeSession;
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void addHandshakeCompletedListener( public void addHandshakeCompletedListener(
HandshakeCompletedListener listener) { HandshakeCompletedListener listener) {
if (listener == null) { if (listener == null) {
throw new IllegalArgumentException("listener is null"); throw new IllegalArgumentException("listener is null");
} }
conContext.sslConfig.addHandshakeCompletedListener(listener); socketLock.lock();
try {
conContext.sslConfig.addHandshakeCompletedListener(listener);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void removeHandshakeCompletedListener( public void removeHandshakeCompletedListener(
HandshakeCompletedListener listener) { HandshakeCompletedListener listener) {
if (listener == null) { if (listener == null) {
throw new IllegalArgumentException("listener is null"); throw new IllegalArgumentException("listener is null");
} }
conContext.sslConfig.removeHandshakeCompletedListener(listener); socketLock.lock();
try {
conContext.sslConfig.removeHandshakeCompletedListener(listener);
} finally {
socketLock.unlock();
}
} }
@Override @Override
@ -377,7 +417,8 @@ public final class SSLSocketImpl
throw new SocketException("Socket has been closed or broken"); throw new SocketException("Socket has been closed or broken");
} }
synchronized (conContext) { // handshake lock handshakeLock.lock();
try {
// double check the context status // double check the context status
if (conContext.isBroken || conContext.isInboundClosed() || if (conContext.isBroken || conContext.isInboundClosed() ||
conContext.isOutboundClosed()) { conContext.isOutboundClosed()) {
@ -400,53 +441,95 @@ public final class SSLSocketImpl
} catch (Exception oe) { // including RuntimeException } catch (Exception oe) { // including RuntimeException
handleException(oe); handleException(oe);
} }
} finally {
handshakeLock.unlock();
} }
} }
@Override @Override
public synchronized void setUseClientMode(boolean mode) { public void setUseClientMode(boolean mode) {
conContext.setUseClientMode(mode); socketLock.lock();
try {
conContext.setUseClientMode(mode);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getUseClientMode() { public boolean getUseClientMode() {
return conContext.sslConfig.isClientMode; socketLock.lock();
try {
return conContext.sslConfig.isClientMode;
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void setNeedClientAuth(boolean need) { public void setNeedClientAuth(boolean need) {
conContext.sslConfig.clientAuthType = socketLock.lock();
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED : try {
ClientAuthType.CLIENT_AUTH_NONE); conContext.sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getNeedClientAuth() { public boolean getNeedClientAuth() {
return (conContext.sslConfig.clientAuthType == socketLock.lock();
try {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED); ClientAuthType.CLIENT_AUTH_REQUIRED);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void setWantClientAuth(boolean want) { public void setWantClientAuth(boolean want) {
conContext.sslConfig.clientAuthType = socketLock.lock();
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED : try {
ClientAuthType.CLIENT_AUTH_NONE); conContext.sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getWantClientAuth() { public boolean getWantClientAuth() {
return (conContext.sslConfig.clientAuthType == socketLock.lock();
try {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED); ClientAuthType.CLIENT_AUTH_REQUESTED);
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized void setEnableSessionCreation(boolean flag) { public void setEnableSessionCreation(boolean flag) {
conContext.sslConfig.enableSessionCreation = flag; socketLock.lock();
try {
conContext.sslConfig.enableSessionCreation = flag;
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized boolean getEnableSessionCreation() { public boolean getEnableSessionCreation() {
return conContext.sslConfig.enableSessionCreation; socketLock.lock();
try {
return conContext.sslConfig.enableSessionCreation;
} finally {
socketLock.unlock();
}
} }
@Override @Override
@ -535,8 +618,9 @@ public final class SSLSocketImpl
// Need a lock here so that the user_canceled alert and the // Need a lock here so that the user_canceled alert and the
// close_notify alert can be delivered together. // close_notify alert can be delivered together.
conContext.outputRecord.recordLock.lock();
try { try {
synchronized (conContext.outputRecord) { try {
// send a user_canceled alert if needed. // send a user_canceled alert if needed.
if (useUserCanceled) { if (useUserCanceled) {
conContext.warning(Alert.USER_CANCELED); conContext.warning(Alert.USER_CANCELED);
@ -544,15 +628,17 @@ public final class SSLSocketImpl
// send a close_notify alert // send a close_notify alert
conContext.warning(Alert.CLOSE_NOTIFY); conContext.warning(Alert.CLOSE_NOTIFY);
} finally {
if (!conContext.isOutboundClosed()) {
conContext.outputRecord.close();
}
if ((autoClose || !isLayered()) && !super.isOutputShutdown()) {
super.shutdownOutput();
}
} }
} finally { } finally {
if (!conContext.isOutboundClosed()) { conContext.outputRecord.recordLock.unlock();
conContext.outputRecord.close();
}
if ((autoClose || !isLayered()) && !super.isOutputShutdown()) {
super.shutdownOutput();
}
} }
if (!isInputShutdown()) { if (!isInputShutdown()) {
@ -681,20 +767,25 @@ public final class SSLSocketImpl
} }
@Override @Override
public synchronized InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
if (isClosed()) { socketLock.lock();
throw new SocketException("Socket is closed"); try {
} if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected) { if (!isConnected) {
throw new SocketException("Socket is not connected"); throw new SocketException("Socket is not connected");
} }
if (conContext.isInboundClosed() || isInputShutdown()) { if (conContext.isInboundClosed() || isInputShutdown()) {
throw new SocketException("Socket input is already shutdown"); throw new SocketException("Socket input is already shutdown");
} }
return appInput; return appInput;
} finally {
socketLock.unlock();
}
} }
private void ensureNegotiated() throws IOException { private void ensureNegotiated() throws IOException {
@ -703,7 +794,8 @@ public final class SSLSocketImpl
return; return;
} }
synchronized (conContext) { // handshake lock handshakeLock.lock();
try {
// double check the context status // double check the context status
if (conContext.isNegotiated || conContext.isBroken || if (conContext.isNegotiated || conContext.isBroken ||
conContext.isInboundClosed() || conContext.isInboundClosed() ||
@ -712,6 +804,8 @@ public final class SSLSocketImpl
} }
startHandshake(); startHandshake();
} finally {
handshakeLock.unlock();
} }
} }
@ -729,6 +823,9 @@ public final class SSLSocketImpl
// Is application data available in the stream? // Is application data available in the stream?
private volatile boolean appDataIsAvailable; private volatile boolean appDataIsAvailable;
// reading lock
private final ReentrantLock readLock = new ReentrantLock();
AppInputStream() { AppInputStream() {
this.appDataIsAvailable = false; this.appDataIsAvailable = false;
this.buffer = ByteBuffer.allocate(4096); this.buffer = ByteBuffer.allocate(4096);
@ -807,7 +904,8 @@ public final class SSLSocketImpl
// //
// Note that the receiving and processing of post-handshake message // Note that the receiving and processing of post-handshake message
// are also synchronized with the read lock. // are also synchronized with the read lock.
synchronized (this) { readLock.lock();
try {
int remains = available(); int remains = available();
if (remains > 0) { if (remains > 0) {
int howmany = Math.min(remains, len); int howmany = Math.min(remains, len);
@ -839,6 +937,8 @@ public final class SSLSocketImpl
// dummy for compiler // dummy for compiler
return -1; return -1;
} }
} finally {
readLock.unlock();
} }
} }
@ -850,19 +950,24 @@ public final class SSLSocketImpl
* things simpler. * things simpler.
*/ */
@Override @Override
public synchronized long skip(long n) throws IOException { public long skip(long n) throws IOException {
// dummy array used to implement skip() // dummy array used to implement skip()
byte[] skipArray = new byte[256]; byte[] skipArray = new byte[256];
long skipped = 0; long skipped = 0;
while (n > 0) {
int len = (int)Math.min(n, skipArray.length); readLock.lock();
int r = read(skipArray, 0, len); try {
if (r <= 0) { while (n > 0) {
break; int len = (int)Math.min(n, skipArray.length);
int r = read(skipArray, 0, len);
if (r <= 0) {
break;
}
n -= r;
skipped += r;
} }
n -= r; } finally {
skipped += r; readLock.unlock();
} }
return skipped; return skipped;
@ -910,8 +1015,18 @@ public final class SSLSocketImpl
* Try the best to use up the input records so as to close the * Try the best to use up the input records so as to close the
* socket gracefully, without impact the performance too much. * socket gracefully, without impact the performance too much.
*/ */
private synchronized void deplete() { private void deplete() {
if (!conContext.isInboundClosed()) { if (conContext.isInboundClosed()) {
return;
}
readLock.lock();
try {
// double check
if (conContext.isInboundClosed()) {
return;
}
if (!(conContext.inputRecord instanceof SSLSocketInputRecord)) { if (!(conContext.inputRecord instanceof SSLSocketInputRecord)) {
return; return;
} }
@ -927,25 +1042,32 @@ public final class SSLSocketImpl
"input stream close depletion failed", ioe); "input stream close depletion failed", ioe);
} }
} }
} finally {
readLock.unlock();
} }
} }
} }
@Override @Override
public synchronized OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
if (isClosed()) { socketLock.lock();
throw new SocketException("Socket is closed"); try {
} if (isClosed()) {
throw new SocketException("Socket is closed");
}
if (!isConnected) { if (!isConnected) {
throw new SocketException("Socket is not connected"); throw new SocketException("Socket is not connected");
} }
if (conContext.isOutboundDone() || isOutputShutdown()) { if (conContext.isOutboundDone() || isOutputShutdown()) {
throw new SocketException("Socket output is already shutdown"); throw new SocketException("Socket output is already shutdown");
} }
return appOutput; return appOutput;
} finally {
socketLock.unlock();
}
} }
@ -1035,44 +1157,74 @@ public final class SSLSocketImpl
} }
@Override @Override
public synchronized SSLParameters getSSLParameters() { public SSLParameters getSSLParameters() {
return conContext.sslConfig.getSSLParameters(); socketLock.lock();
} try {
return conContext.sslConfig.getSSLParameters();
@Override } finally {
public synchronized void setSSLParameters(SSLParameters params) { socketLock.unlock();
conContext.sslConfig.setSSLParameters(params);
if (conContext.sslConfig.maximumPacketSize != 0) {
conContext.outputRecord.changePacketSize(
conContext.sslConfig.maximumPacketSize);
} }
} }
@Override @Override
public synchronized String getApplicationProtocol() { public void setSSLParameters(SSLParameters params) {
return conContext.applicationProtocol; socketLock.lock();
try {
conContext.sslConfig.setSSLParameters(params);
if (conContext.sslConfig.maximumPacketSize != 0) {
conContext.outputRecord.changePacketSize(
conContext.sslConfig.maximumPacketSize);
}
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized String getHandshakeApplicationProtocol() { public String getApplicationProtocol() {
if (conContext.handshakeContext != null) { socketLock.lock();
return conContext.handshakeContext.applicationProtocol; try {
return conContext.applicationProtocol;
} finally {
socketLock.unlock();
}
}
@Override
public String getHandshakeApplicationProtocol() {
socketLock.lock();
try {
if (conContext.handshakeContext != null) {
return conContext.handshakeContext.applicationProtocol;
}
} finally {
socketLock.unlock();
} }
return null; return null;
} }
@Override @Override
public synchronized void setHandshakeApplicationProtocolSelector( public void setHandshakeApplicationProtocolSelector(
BiFunction<SSLSocket, List<String>, String> selector) { BiFunction<SSLSocket, List<String>, String> selector) {
conContext.sslConfig.socketAPSelector = selector; socketLock.lock();
try {
conContext.sslConfig.socketAPSelector = selector;
} finally {
socketLock.unlock();
}
} }
@Override @Override
public synchronized BiFunction<SSLSocket, List<String>, String> public BiFunction<SSLSocket, List<String>, String>
getHandshakeApplicationProtocolSelector() { getHandshakeApplicationProtocolSelector() {
return conContext.sslConfig.socketAPSelector; socketLock.lock();
try {
return conContext.sslConfig.socketAPSelector;
} finally {
socketLock.unlock();
}
} }
/** /**
@ -1142,8 +1294,11 @@ public final class SSLSocketImpl
try { try {
Plaintext plainText; Plaintext plainText;
synchronized (this) { socketLock.lock();
try {
plainText = decode(buffer); plainText = decode(buffer);
} finally {
socketLock.unlock();
} }
if (plainText.contentType == ContentType.APPLICATION_DATA.id && if (plainText.contentType == ContentType.APPLICATION_DATA.id &&
buffer.position() > 0) { buffer.position() > 0) {
@ -1222,27 +1377,33 @@ public final class SSLSocketImpl
* *
* Called by connect, the layered constructor, and SSLServerSocket. * Called by connect, the layered constructor, and SSLServerSocket.
*/ */
synchronized void doneConnect() throws IOException { void doneConnect() throws IOException {
// In server mode, it is not necessary to set host and serverNames. socketLock.lock();
// Otherwise, would require a reverse DNS lookup to get the hostname. try {
if (peerHost == null || peerHost.isEmpty()) { // In server mode, it is not necessary to set host and serverNames.
boolean useNameService = // Otherwise, would require a reverse DNS lookup to get
trustNameService && conContext.sslConfig.isClientMode; // the hostname.
useImplicitHost(useNameService); if (peerHost == null || peerHost.isEmpty()) {
} else { boolean useNameService =
conContext.sslConfig.serverNames = trustNameService && conContext.sslConfig.isClientMode;
Utilities.addToSNIServerNameList( useImplicitHost(useNameService);
conContext.sslConfig.serverNames, peerHost); } else {
conContext.sslConfig.serverNames =
Utilities.addToSNIServerNameList(
conContext.sslConfig.serverNames, peerHost);
}
InputStream sockInput = super.getInputStream();
conContext.inputRecord.setReceiverStream(sockInput);
OutputStream sockOutput = super.getOutputStream();
conContext.inputRecord.setDeliverStream(sockOutput);
conContext.outputRecord.setDeliverStream(sockOutput);
this.isConnected = true;
} finally {
socketLock.unlock();
} }
InputStream sockInput = super.getInputStream();
conContext.inputRecord.setReceiverStream(sockInput);
OutputStream sockOutput = super.getOutputStream();
conContext.inputRecord.setDeliverStream(sockOutput);
conContext.outputRecord.setDeliverStream(sockOutput);
this.isConnected = true;
} }
private void useImplicitHost(boolean useNameService) { private void useImplicitHost(boolean useNameService) {
@ -1288,11 +1449,16 @@ public final class SSLSocketImpl
// Please NOTE that this method MUST be called before calling to // Please NOTE that this method MUST be called before calling to
// SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter
// may override SNIHostName in the customized server name indication. // may override SNIHostName in the customized server name indication.
public synchronized void setHost(String host) { public void setHost(String host) {
this.peerHost = host; socketLock.lock();
this.conContext.sslConfig.serverNames = try {
Utilities.addToSNIServerNameList( this.peerHost = host;
conContext.sslConfig.serverNames, host); this.conContext.sslConfig.serverNames =
Utilities.addToSNIServerNameList(
conContext.sslConfig.serverNames, host);
} finally {
socketLock.unlock();
}
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -51,123 +51,206 @@ final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
} }
@Override @Override
synchronized void encodeAlert( void encodeAlert(byte level, byte description) throws IOException {
byte level, byte description) throws IOException { recordLock.lock();
if (isClosed()) { try {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { if (isClosed()) {
SSLLogger.warning("outbound has closed, ignore outbound " + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
"alert message: " + Alert.nameOf(description)); SSLLogger.warning("outbound has closed, ignore outbound " +
"alert message: " + Alert.nameOf(description));
}
return;
} }
return;
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(level);
write(description);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine("WRITE: " + protocolVersion +
" " + ContentType.ALERT.name +
"(" + Alert.nameOf(description) + ")" +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.ALERT.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
} finally {
recordLock.unlock();
} }
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(level);
write(description);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine("WRITE: " + protocolVersion +
" " + ContentType.ALERT.name +
"(" + Alert.nameOf(description) + ")" +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.ALERT.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
} }
@Override @Override
synchronized void encodeHandshake(byte[] source, void encodeHandshake(byte[] source,
int offset, int length) throws IOException { int offset, int length) throws IOException {
if (isClosed()) { recordLock.lock();
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { try {
SSLLogger.warning("outbound has closed, ignore outbound " + if (isClosed()) {
"handshake message", if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
ByteBuffer.wrap(source, offset, length)); SSLLogger.warning("outbound has closed, ignore outbound " +
"handshake message",
ByteBuffer.wrap(source, offset, length));
}
return;
} }
return;
}
if (firstMessage) { if (firstMessage) {
firstMessage = false; firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) && if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == SSLHandshake.CLIENT_HELLO.id) && (source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
// 5: recode header size // 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) { (source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty // V3 session ID is empty
// 4: handshake header size // 4: handshake header size
// 2: client_version in ClientHello // 2: client_version in ClientHello
// 32: random in ClientHello // 32: random in ClientHello
ByteBuffer v2ClientHello = encodeV2ClientHello( ByteBuffer v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4)); source, (offset + 4), (length - 4));
byte[] record = v2ClientHello.array(); // array offset is zero // array offset is zero
int limit = v2ClientHello.limit(); byte[] record = v2ClientHello.array();
handshakeHash.deliver(record, 2, (limit - 2)); int limit = v2ClientHello.limit();
handshakeHash.deliver(record, 2, (limit - 2));
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: SSLv2 ClientHello message" +
", length = " + limit);
}
// deliver this message
//
// Version 2 ClientHello message should be plaintext.
//
// No max fragment length negotiation.
deliverStream.write(record, 0, limit);
deliverStream.flush();
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(record, 0, limit)));
}
return;
}
}
byte handshakeType = source[0];
if (handshakeHash.isHashable(handshakeType)) {
handshakeHash.deliver(source, offset, length);
}
int fragLimit = getFragLimit();
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count == 0) {
count = position;
}
if ((count - position) < (fragLimit - length)) {
write(source, offset, length);
return;
}
for (int limit = (offset + length); offset < limit;) {
int remains = (limit - offset) + (count - position);
int fragLen = Math.min(fragLimit, remains);
// use the buf of ByteArrayOutputStream
write(source, offset, fragLen);
if (remains < fragLimit) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("record")) { if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine( SSLLogger.fine(
"WRITE: SSLv2 ClientHello message" + "WRITE: " + protocolVersion +
", length = " + limit); " " + ContentType.HANDSHAKE.name +
", length = " + (count - headerSize));
} }
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize);
// deliver this message // deliver this message
// deliverStream.write(buf, 0, count); // may throw IOException
// Version 2 ClientHello message should be plaintext. deliverStream.flush(); // may throw IOException
//
// No max fragment length negotiation.
deliverStream.write(record, 0, limit);
deliverStream.flush();
if (SSLLogger.isOn && SSLLogger.isOn("packet")) { if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write", SSLLogger.fine("Raw write",
(new ByteArrayInputStream(record, 0, limit))); (new ByteArrayInputStream(buf, 0, count)));
} }
// reset the offset
offset += fragLen;
// reset the internal buffer
count = position;
}
} finally {
recordLock.unlock();
}
}
@Override
void encodeChangeCipherSpec() throws IOException {
recordLock.lock();
try {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"change_cipher_spec message");
}
return; return;
} }
}
byte handshakeType = source[0];
if (handshakeHash.isHashable(handshakeType)) {
handshakeHash.deliver(source, offset, length);
}
int fragLimit = getFragLimit();
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count == 0) {
count = position;
}
if ((count - position) < (fragLimit - length)) {
write(source, offset, length);
return;
}
for (int limit = (offset + length); offset < limit;) {
int remains = (limit - offset) + (count - position);
int fragLen = Math.min(fragLimit, remains);
// use the buf of ByteArrayOutputStream // use the buf of ByteArrayOutputStream
write(source, offset, fragLen); int position = headerSize + writeCipher.getExplicitNonceSize();
if (remains < fragLimit) { count = position;
write((byte)1); // byte 1: change_cipher_spec(
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
// deliverStream.flush(); // flush in Finished
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
} finally {
recordLock.unlock();
}
}
@Override
public void flush() throws IOException {
recordLock.lock();
try {
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count <= position) {
return; return;
} }
@ -190,155 +273,103 @@ final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
(new ByteArrayInputStream(buf, 0, count))); (new ByteArrayInputStream(buf, 0, count)));
} }
// reset the offset
offset += fragLen;
// reset the internal buffer // reset the internal buffer
count = position; count = 0; // DON'T use position
} finally {
recordLock.unlock();
} }
} }
@Override @Override
synchronized void encodeChangeCipherSpec() throws IOException { void deliver(byte[] source, int offset, int length) throws IOException {
if (isClosed()) { recordLock.lock();
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { try {
SSLLogger.warning("outbound has closed, ignore outbound " + if (isClosed()) {
"change_cipher_spec message"); throw new SocketException(
} "Connection or outbound has been closed");
return;
}
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write((byte)1); // byte 1: change_cipher_spec(
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
// deliverStream.flush(); // flush in Finished
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
}
@Override
public synchronized void flush() throws IOException {
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count <= position) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion +
" " + ContentType.HANDSHAKE.name +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0; // DON'T use position
}
@Override
synchronized void deliver(
byte[] source, int offset, int length) throws IOException {
if (isClosed()) {
throw new SocketException("Connection or outbound has been closed");
}
if (writeCipher.authenticator.seqNumOverflow()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine(
"sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
} }
throw new SSLHandshakeException("sequence number overflow"); if (writeCipher.authenticator.seqNumOverflow()) {
} if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine(
"sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
boolean isFirstRecordOfThePayload = true; throw new SSLHandshakeException("sequence number overflow");
for (int limit = (offset + length); offset < limit;) {
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen =
writeCipher.calculateFragmentSize(fragLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
} }
if (fragmentSize > 0) { boolean isFirstRecordOfThePayload = true;
fragLen = Math.min(fragLen, fragmentSize); for (int limit = (offset + length); offset < limit;) {
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
fragLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLen = Math.min(fragLen, fragmentSize);
}
if (isFirstRecordOfThePayload && needToSplitPayload()) {
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
fragLen = Math.min(fragLen, (limit - offset));
}
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(source, offset, fragLen);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion +
" " + ContentType.APPLICATION_DATA.name +
", length = " + (count - position));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher,
ContentType.APPLICATION_DATA.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
offset += fragLen;
} }
} finally {
if (isFirstRecordOfThePayload && needToSplitPayload()) { recordLock.unlock();
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
fragLen = Math.min(fragLen, (limit - offset));
}
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(source, offset, fragLen);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion +
" " + ContentType.APPLICATION_DATA.name +
", length = " + (count - position));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeCipher, ContentType.APPLICATION_DATA.id, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write",
(new ByteArrayInputStream(buf, 0, count)));
}
// reset the internal buffer
count = 0;
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
offset += fragLen;
} }
} }
@Override @Override
synchronized void setDeliverStream(OutputStream outputStream) { void setDeliverStream(OutputStream outputStream) {
this.deliverStream = outputStream; recordLock.lock();
try {
this.deliverStream = outputStream;
} finally {
recordLock.unlock();
}
} }
/* /*

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -102,25 +102,21 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
* Basic container for credentials implemented as an inner class. * Basic container for credentials implemented as an inner class.
*/ */
private static class X509Credentials { private static class X509Credentials {
PrivateKey privateKey; final PrivateKey privateKey;
X509Certificate[] certificates; final X509Certificate[] certificates;
private Set<X500Principal> issuerX500Principals; private final Set<X500Principal> issuerX500Principals;
X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) { X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) {
// assert privateKey and certificates != null // assert privateKey and certificates != null
this.privateKey = privateKey; this.privateKey = privateKey;
this.certificates = certificates; this.certificates = certificates;
this.issuerX500Principals = new HashSet<>(certificates.length);
for (X509Certificate certificate : certificates) {
issuerX500Principals.add(certificate.getIssuerX500Principal());
}
} }
synchronized Set<X500Principal> getIssuerX500Principals() { Set<X500Principal> getIssuerX500Principals() {
// lazy initialization
if (issuerX500Principals == null) {
issuerX500Principals = new HashSet<X500Principal>();
for (int i = 0; i < certificates.length; i++) {
issuerX500Principals.add(
certificates[i].getIssuerX500Principal());
}
}
return issuerX500Principals; return issuerX500Principals;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -496,13 +496,16 @@ class TransportContext implements ConnectionContext {
} }
if (needCloseNotify) { if (needCloseNotify) {
synchronized (outputRecord) { outputRecord.recordLock.lock();
try {
try { try {
// send a close_notify alert // send a close_notify alert
warning(Alert.CLOSE_NOTIFY); warning(Alert.CLOSE_NOTIFY);
} finally { } finally {
outputRecord.close(); outputRecord.close();
} }
} finally {
outputRecord.recordLock.unlock();
} }
} }
} }
@ -541,7 +544,8 @@ class TransportContext implements ConnectionContext {
// Need a lock here so that the user_canceled alert and the // Need a lock here so that the user_canceled alert and the
// close_notify alert can be delivered together. // close_notify alert can be delivered together.
synchronized (outputRecord) { outputRecord.recordLock.lock();
try {
try { try {
// send a user_canceled alert if needed. // send a user_canceled alert if needed.
if (useUserCanceled) { if (useUserCanceled) {
@ -553,6 +557,8 @@ class TransportContext implements ConnectionContext {
} finally { } finally {
outputRecord.close(); outputRecord.close();
} }
} finally {
outputRecord.recordLock.unlock();
} }
} }

View File

@ -30,6 +30,7 @@ import java.lang.ref.WeakReference;
import java.security.*; import java.security.*;
import java.security.cert.*; import java.security.cert.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import sun.security.action.*; import sun.security.action.*;
import sun.security.validator.TrustStoreUtil; import sun.security.validator.TrustStoreUtil;
@ -244,6 +245,8 @@ final class TrustStoreManager {
// objects can be atomically cleared, and reloaded if needed. // objects can be atomically cleared, and reloaded if needed.
private WeakReference<Set<X509Certificate>> csRef; private WeakReference<Set<X509Certificate>> csRef;
private final ReentrantLock tamLock = new ReentrantLock();
private TrustAnchorManager() { private TrustAnchorManager() {
this.descriptor = null; this.descriptor = null;
this.ksRef = new WeakReference<>(null); this.ksRef = new WeakReference<>(null);
@ -255,7 +258,7 @@ final class TrustStoreManager {
* *
* @return null if the underlying KeyStore is not available. * @return null if the underlying KeyStore is not available.
*/ */
synchronized KeyStore getKeyStore( KeyStore getKeyStore(
TrustStoreDescriptor descriptor) throws Exception { TrustStoreDescriptor descriptor) throws Exception {
TrustStoreDescriptor temporaryDesc = this.descriptor; TrustStoreDescriptor temporaryDesc = this.descriptor;
@ -264,14 +267,25 @@ final class TrustStoreManager {
return ks; return ks;
} }
// Reload a new key store. tamLock.lock();
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { try {
SSLLogger.fine("Reload the trust store"); // double check
} ks = ksRef.get();
if ((ks != null) && descriptor.equals(temporaryDesc)) {
return ks;
}
ks = loadKeyStore(descriptor); // Reload a new key store.
this.descriptor = descriptor; if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
this.ksRef = new WeakReference<>(ks); SSLLogger.fine("Reload the trust store");
}
ks = loadKeyStore(descriptor);
this.descriptor = descriptor;
this.ksRef = new WeakReference<>(ks);
} finally {
tamLock.unlock();
}
return ks; return ks;
} }
@ -282,51 +296,62 @@ final class TrustStoreManager {
* *
* @return empty collection if the underlying KeyStore is not available. * @return empty collection if the underlying KeyStore is not available.
*/ */
synchronized Set<X509Certificate> getTrustedCerts( Set<X509Certificate> getTrustedCerts(
TrustStoreDescriptor descriptor) throws Exception { TrustStoreDescriptor descriptor) throws Exception {
KeyStore ks = null; KeyStore ks = null;
TrustStoreDescriptor temporaryDesc = this.descriptor; TrustStoreDescriptor temporaryDesc = this.descriptor;
Set<X509Certificate> certs = csRef.get(); Set<X509Certificate> certs = csRef.get();
if (certs != null) { if ((certs != null) && descriptor.equals(temporaryDesc)) {
if (descriptor.equals(temporaryDesc)) { return certs;
return certs;
} else {
// Use the new descriptor.
this.descriptor = descriptor;
}
} else {
// Try to use the cached store at first.
if (descriptor.equals(temporaryDesc)) {
ks = ksRef.get();
} else {
// Use the new descriptor.
this.descriptor = descriptor;
}
} }
// Reload the trust store if needed. tamLock.lock();
if (ks == null) { try {
// double check
temporaryDesc = this.descriptor;
certs = csRef.get();
if (certs != null) {
if (descriptor.equals(temporaryDesc)) {
return certs;
} else {
// Use the new descriptor.
this.descriptor = descriptor;
}
} else {
// Try to use the cached store at first.
if (descriptor.equals(temporaryDesc)) {
ks = ksRef.get();
} else {
// Use the new descriptor.
this.descriptor = descriptor;
}
}
// Reload the trust store if needed.
if (ks == null) {
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
SSLLogger.fine("Reload the trust store");
}
ks = loadKeyStore(descriptor);
this.ksRef = new WeakReference<>(ks);
}
// Reload trust certs from the key store.
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
SSLLogger.fine("Reload the trust store"); SSLLogger.fine("Reload trust certs");
} }
ks = loadKeyStore(descriptor);
}
// Reload trust certs from the key store. certs = loadTrustedCerts(ks);
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
SSLLogger.fine("Reload trust certs"); SSLLogger.fine("Reloaded " + certs.size() + " trust certs");
} }
certs = loadTrustedCerts(ks); this.csRef = new WeakReference<>(certs);
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { } finally {
SSLLogger.fine("Reloaded " + certs.size() + " trust certs"); tamLock.unlock();
} }
// Note that as ks is a local variable, it is not
// necessary to add it to the ksRef weak reference.
this.csRef = new WeakReference<>(certs);
return certs; return certs;
} }

View File

@ -29,6 +29,7 @@ import java.net.Socket;
import java.security.*; import java.security.*;
import java.security.cert.*; import java.security.cert.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.*; import javax.net.ssl.*;
import sun.security.util.AnchorCertificates; import sun.security.util.AnchorCertificates;
import sun.security.util.HostnameChecker; import sun.security.util.HostnameChecker;
@ -63,6 +64,8 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// the different extension checks. They are initialized lazily on demand. // the different extension checks. They are initialized lazily on demand.
private volatile Validator clientValidator, serverValidator; private volatile Validator clientValidator, serverValidator;
private final ReentrantLock validatorLock = new ReentrantLock();
X509TrustManagerImpl(String validatorType, X509TrustManagerImpl(String validatorType,
Collection<X509Certificate> trustedCerts) { Collection<X509Certificate> trustedCerts) {
@ -157,12 +160,15 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
if (isClient) { if (isClient) {
v = clientValidator; v = clientValidator;
if (v == null) { if (v == null) {
synchronized (this) { validatorLock.lock();
try {
v = clientValidator; v = clientValidator;
if (v == null) { if (v == null) {
v = getValidator(Validator.VAR_TLS_CLIENT); v = getValidator(Validator.VAR_TLS_CLIENT);
clientValidator = v; clientValidator = v;
} }
} finally {
validatorLock.unlock();
} }
} }
} else { } else {
@ -170,12 +176,15 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// (guaranteed under the new Tiger memory model) // (guaranteed under the new Tiger memory model)
v = serverValidator; v = serverValidator;
if (v == null) { if (v == null) {
synchronized (this) { validatorLock.lock();
try {
v = serverValidator; v = serverValidator;
if (v == null) { if (v == null) {
v = getValidator(Validator.VAR_TLS_SERVER); v = getValidator(Validator.VAR_TLS_SERVER);
serverValidator = v; serverValidator = v;
} }
} finally {
validatorLock.unlock();
} }
} }
} }