1212import java .security .cert .CertificateException ;
1313import java .util .ArrayList ;
1414import java .util .List ;
15+ import java .util .concurrent .atomic .AtomicInteger ;
1516
1617import javax .net .ssl .SSLEngineResult ;
1718import javax .net .ssl .SSLException ;
19+ import java .lang .ref .Cleaner ;
1820import javax .net .ssl .SSLHandshakeException ;
1921import javax .net .ssl .SSLPeerUnverifiedException ;
2022import javax .net .ssl .X509ExtendedTrustManager ;
6163 * as being from the appropriate side of the TLS connection.
6264 */
6365public class JSSEngineReferenceImpl extends JSSEngine {
66+
67+ /**
68+ * Cleaner instance for guaranteed resource cleanup.
69+ * Used as a safety net when close()/cleanup() is not called explicitly.
70+ */
71+ private static final Cleaner cleaner = Cleaner .create ();
72+ private Cleaner .Cleanable cleanable ;
73+
74+ // Used for further identifying the engine instance being cleaned.
75+ private static java .util .concurrent .atomic .AtomicInteger engineCounter = new AtomicInteger (0 );
76+ private int engineId = engineCounter .incrementAndGet ();
77+
78+
79+ /**
80+ * Cleaner action - must be a static class to avoid preventing GC
81+ * of the JSSEngineReferenceImpl instance.
82+ */
83+ private static class EngineCleanup implements Runnable {
84+ private final String name ;
85+
86+ private SSLFDProxy ssl_fd ;
87+ private BufferProxy read_buf ;
88+ private BufferProxy write_buf ;
89+
90+ EngineCleanup (String name , SSLFDProxy fd , BufferProxy rb , BufferProxy wb ) {
91+ this .name = name ;
92+ this .ssl_fd = fd ;
93+ this .read_buf = rb ;
94+ this .write_buf = wb ;
95+ }
96+
97+ public void run () {
98+ logger .debug ("JSSEngine: CLEANER: EngineCleanup.run() fired for " + name );
99+ freeEngineResources (ssl_fd , read_buf , write_buf );
100+ ssl_fd = null ;
101+ read_buf = null ;
102+ write_buf = null ;
103+ }
104+ }
105+
64106 /**
65107 * Faked peer information that we pass to the underlying BufferPRFD
66108 * implementation.
@@ -224,7 +266,7 @@ public JSSEngineReferenceImpl(String peerHost, int peerPort,
224266 debug ("JSSEngine: constructor(" + peerHost + ", " + peerPort + ", " + localCert + ", " + localKey + ")" );
225267 }
226268
227- private void debug (String msg ) {
269+ void debug (String msg ) {
228270 logger .debug (prefix + msg );
229271 }
230272
@@ -236,6 +278,40 @@ private void warn(String msg) {
236278 logger .warn (prefix + msg );
237279 }
238280
281+ private static void freeEngineResources (SSLFDProxy fd , BufferProxy rb , BufferProxy wb ) {
282+ if (fd != null && !fd .isNull ()) {
283+ try {
284+ SSL .RemoveCallbacks (fd );
285+ fd .close ();
286+ } catch (Exception e ) {
287+ logger .error ("Error closing ssl_fd" , e );
288+ }
289+ }
290+ if (rb != null && !rb .isNull ()) {
291+ Buffer .Free (rb );
292+ }
293+ if (wb != null && !wb .isNull ()) {
294+ Buffer .Free (wb );
295+ }
296+ }
297+
298+
299+ boolean isSeenException () {
300+ return seen_exception ;
301+ }
302+
303+ void setSeenException (boolean val ) {
304+ seen_exception = val ;
305+ }
306+
307+ SSLException getSslException () {
308+ return ssl_exception ;
309+ }
310+
311+ void setSslException (SSLException e ) {
312+ ssl_exception = e ;
313+ }
314+
239315 /**
240316 * Set the name of this JSSEngine instance, to be printed in logging calls.
241317 *
@@ -349,6 +425,11 @@ private void createBufferFD() throws SSLException {
349425 fd = null ;
350426 closed_fd = false ;
351427
428+ cleanable = cleaner .register (this , new EngineCleanup (
429+ "engine-" + engineId + " " + peer_info , ssl_fd , read_buf , write_buf ));
430+
431+ logger .debug ("Registering cleaner: " + "engine-" + engineId + " " + peer_info );
432+
352433 // Turn on SSL Alert Logging for the ssl_fd object.
353434 int ret = SSL .EnableAlertLogging (ssl_fd );
354435 if (ret == SSL .SECFailure ) {
@@ -588,7 +669,7 @@ private void applyTrustManagers() throws SSLException {
588669 // from Runnable, so we can reuse it here as well. We can create
589670 // it ahead of time though. In this case, checkNeedCertValidation()
590671 // is never called.
591- ssl_fd .certAuthHandler = new CertValidationTask (ssl_fd );
672+ ssl_fd .certAuthHandler = new CertValidationTask (ssl_fd , as_server , need_client_auth , trust_managers , this );
592673
593674 if (SSL .ConfigSyncTrustManagerCertAuthCallback (ssl_fd ) == SSL .SECFailure ) {
594675 throw new SSLException ("Unable to configure TrustManager validation on this JSSengine: " + errorText (PR .GetError ()));
@@ -842,7 +923,7 @@ private boolean checkNeedCertValidation() {
842923 debug ("JSSEngine: checkNeedCertValidation() - creating task" );
843924
844925 // OK, time to create our runnable task.
845- task = new CertValidationTask (ssl_fd );
926+ task = new CertValidationTask (ssl_fd , as_server , need_client_auth , trust_managers , this );
846927
847928 // Update our handshake state so we know what to do next.
848929 handshake_state = SSLEngineResult .HandshakeStatus .NEED_TASK ;
@@ -1686,6 +1767,10 @@ public synchronized void tryCleanup() {
16861767 }
16871768 }
16881769
1770+ public int getEngineId () {
1771+ return engineId ;
1772+ }
1773+
16891774 /**
16901775 * Performs cleanup of internal data, closing both inbound and outbound
16911776 * data streams if still open.
@@ -1720,6 +1805,13 @@ public synchronized void cleanup() {
17201805 session .close ();
17211806 session = null ;
17221807 }
1808+
1809+ // Deregister the Cleaner. clean() runs EngineCleanup.run()
1810+ // which is a no-op since cleanupSSLFD() already freed everything.
1811+ if (cleanable != null ) {
1812+ cleanable .clean ();
1813+ cleanable = null ;
1814+ }
17231815 }
17241816
17251817 private void cleanupLoggingSocket () {
@@ -1739,42 +1831,39 @@ private void cleanupLoggingSocket() {
17391831 }
17401832
17411833 private void cleanupSSLFD () {
1742- if (ssl_fd != null ) {
1743- // closed_fd is already set to true in cleanup() before this is called.
1744- // This prevents concurrent calls to closeInbound()/closeOutbound() from
1745- // attempting PR.Shutdown() on ssl_fd that is being freed.
1746- try {
1747- SSL .RemoveCallbacks (ssl_fd );
1748- ssl_fd .close ();
1749- } catch (Exception e ) {
1750- logger .error ("Got exception trying to cleanup SSLFD" , e );
1751- } finally {
1752- ssl_fd = null ;
1753- }
1754- }
1755-
1756- if (read_buf != null ) {
1757- Buffer .Free (read_buf );
1758- read_buf = null ;
1759- }
1760-
1761- if (write_buf != null ) {
1762- Buffer .Free (write_buf );
1763- write_buf = null ;
1764- }
1834+ freeEngineResources (ssl_fd , read_buf , write_buf );
1835+ ssl_fd = null ;
1836+ read_buf = null ;
1837+ write_buf = null ;
17651838 }
17661839
17671840 // During testing with Tomcat 8.5, most instances did not call
17681841 // cleanup, so all the JNI resources end up getting leaked: ssl_fd
17691842 // (and its global ref), read_buf, and write_buf.
17701843 @ Override
17711844 protected void finalize () {
1772- cleanup ();
1845+ // For now clear this while implementing the Cleaner class based solution
1846+
1847+ logger .debug ("JSSEngine: CLEANER: finalize() called - no-op, Cleaner handles cleanup" );
1848+ //cleanup();
17731849 }
17741850
1775- private class CertValidationTask extends CertAuthHandler {
1776- public CertValidationTask (SSLFDProxy fd ) {
1851+ private static class CertValidationTask extends CertAuthHandler {
1852+ private final java .lang .ref .WeakReference <JSSEngineReferenceImpl > engineRef ;
1853+ private final boolean as_server ;
1854+ private final boolean need_client_auth ;
1855+ private final X509TrustManager [] trust_managers ;
1856+
1857+ public CertValidationTask (SSLFDProxy fd , boolean asServer ,
1858+ boolean needClientAuth , X509TrustManager [] trustManagers ,
1859+ JSSEngineReferenceImpl engine ) {
1860+
17771861 super (fd );
1862+ this .as_server = asServer ;
1863+ this .need_client_auth = needClientAuth ;
1864+ this .trust_managers = trustManagers ;
1865+ this .engineRef = new java .lang .ref .WeakReference <>(engine );
1866+
17781867 }
17791868
17801869 public String findAuthType (SSLFDProxy ssl_fd , PK11Cert [] chain ) throws Exception {
@@ -1851,13 +1940,18 @@ public String findAuthType(SSLFDProxy ssl_fd, PK11Cert[] chain) throws Exception
18511940 @ Override
18521941 public int check (SSLFDProxy fd ) {
18531942 // Needs to be available for assignException() below.
1943+
1944+ JSSEngineReferenceImpl engine = engineRef .get ();
1945+
18541946 PK11Cert [] chain = null ;
18551947 String authType ;
18561948
18571949 try {
18581950 chain = SSL .PeerCertificateChain (fd );
18591951 authType = findAuthType (fd , chain );
1860- debug ("CertAuthType: " + authType );
1952+ if (engine != null ) {
1953+ engine .debug ("CertAuthType: " + authType );
1954+ }
18611955
18621956 if (chain == null || chain .length == 0 ) {
18631957 // When the chain is NULL, we'd always fail in the
@@ -1869,12 +1963,14 @@ public int check(SSLFDProxy fd) {
18691963 // Since we're a server validating the client's
18701964 // chain (and they didn't provide one), we should
18711965 // ignore it instead of forcing the problem.
1872- debug ("No client certificate chain and client cert not needed." );
1966+ if (engine != null ) {
1967+ engine .debug ("No client certificate chain and client cert not needed." );
1968+ }
18731969 return 0 ;
18741970 }
18751971 }
18761972 } catch (Exception excpt ) {
1877- return assignException (excpt , chain );
1973+ return assignException (excpt , chain , engine );
18781974 }
18791975
18801976 try {
@@ -1886,9 +1982,9 @@ public int check(SSLFDProxy fd) {
18861982 if (tm instanceof X509ExtendedTrustManager ) {
18871983 X509ExtendedTrustManager etm = (X509ExtendedTrustManager ) tm ;
18881984 if (as_server ) {
1889- etm .checkClientTrusted (chain , authType , JSSEngineReferenceImpl . this );
1985+ etm .checkClientTrusted (chain , authType , engine );
18901986 } else {
1891- etm .checkServerTrusted (chain , authType , JSSEngineReferenceImpl . this );
1987+ etm .checkServerTrusted (chain , authType , engine );
18921988 }
18931989 } else {
18941990 if (as_server ) {
@@ -1899,16 +1995,16 @@ public int check(SSLFDProxy fd) {
18991995 }
19001996 }
19011997 } catch (CertificateException excpt ) {
1902- return handleCertificateException (excpt , chain );
1998+ return handleCertificateException (excpt , chain , engine );
19031999 }
19042000
19052001 return 0 ;
19062002 }
19072003
1908- private int assignException (Exception excpt , PK11Cert [] chain ) {
2004+ private int assignException (Exception excpt , PK11Cert [] chain , JSSEngineReferenceImpl engine ) {
19092005 int nss_code = Cert .MatchExceptionToNSSError (excpt );
19102006
1911- if (seen_exception ) {
2007+ if (engine == null || engine . isSeenException () ) {
19122008 return nss_code ;
19132009 }
19142010
@@ -1935,29 +2031,30 @@ private int assignException(Exception excpt, PK11Cert[] chain) {
19352031 }
19362032 msg += "exception message: " + excpt .getMessage ();
19372033
1938- seen_exception = true ;
1939- ssl_exception = new SSLException (msg , excpt );
2034+ engine . setSeenException ( true ) ;
2035+ engine . setSslException ( new SSLException (msg , excpt ) );
19402036 return nss_code ;
19412037 }
19422038
1943- private int handleCertificateException (Exception excpt , PK11Cert [] chain ) {
2039+ private int handleCertificateException (Exception excpt , PK11Cert [] chain , JSSEngineReferenceImpl engine ) {
19442040 int nss_code = Cert .MatchExceptionToNSSError (excpt );
19452041
1946- if (seen_exception ) {
2042+ if (engine == null || engine . isSeenException () ) {
19472043 return nss_code ;
19482044 }
19492045
19502046 String msg = "Unable to validate "
19512047 + chain [0 ].getSubjectX500Principal () + ": "
19522048 + excpt .getMessage ();
19532049
1954- seen_exception = true ;
1955- ssl_exception = new SSLPeerUnverifiedException (msg );
2050+ engine .setSeenException (true );
2051+ engine .setSslException (new SSLPeerUnverifiedException (msg ));
2052+
19562053 return nss_code ;
19572054 }
19582055 }
19592056
1960- private class BypassBadHostname extends BadCertHandler {
2057+ private static class BypassBadHostname extends BadCertHandler {
19612058 public BypassBadHostname (SSLFDProxy fd , int error ) {
19622059 super (fd , error );
19632060 }
0 commit comments