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.
@@ -1739,28 +1824,16 @@ private void cleanupLoggingSocket() {
17391824 }
17401825
17411826 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- }
1827+ freeEngineResources (ssl_fd , read_buf , write_buf );
1828+ ssl_fd = null ;
1829+ read_buf = null ;
1830+ write_buf = null ;
17601831
1761- if (write_buf != null ) {
1762- Buffer .Free (write_buf );
1763- write_buf = null ;
1832+ // Deregister the Cleaner, EngineCleanup.run will be a no-op
1833+ // since freeEngineResources() already freed everything above.
1834+ if (cleanable != null ) {
1835+ cleanable .clean ();
1836+ cleanable = null ;
17641837 }
17651838 }
17661839
@@ -1769,12 +1842,27 @@ private void cleanupSSLFD() {
17691842 // (and its global ref), read_buf, and write_buf.
17701843 @ Override
17711844 protected void finalize () {
1772- cleanup ();
1845+ // Intentionally empty — prevents NativeProxy.finalize() from racing
1846+ // with the Cleaner. Resource cleanup is handled by cleanup() or the
1847+ // Cleaner's EngineCleanup.run(). See cleanupSSLFD() and EngineCleanup.
17731848 }
17741849
1775- private class CertValidationTask extends CertAuthHandler {
1776- public CertValidationTask (SSLFDProxy fd ) {
1850+ private static class CertValidationTask extends CertAuthHandler {
1851+ private final java .lang .ref .WeakReference <JSSEngineReferenceImpl > engineRef ;
1852+ private final boolean as_server ;
1853+ private final boolean need_client_auth ;
1854+ private final X509TrustManager [] trust_managers ;
1855+
1856+ public CertValidationTask (SSLFDProxy fd , boolean asServer ,
1857+ boolean needClientAuth , X509TrustManager [] trustManagers ,
1858+ JSSEngineReferenceImpl engine ) {
1859+
17771860 super (fd );
1861+ this .as_server = asServer ;
1862+ this .need_client_auth = needClientAuth ;
1863+ this .trust_managers = trustManagers ;
1864+ this .engineRef = new java .lang .ref .WeakReference <>(engine );
1865+
17781866 }
17791867
17801868 public String findAuthType (SSLFDProxy ssl_fd , PK11Cert [] chain ) throws Exception {
@@ -1851,13 +1939,18 @@ public String findAuthType(SSLFDProxy ssl_fd, PK11Cert[] chain) throws Exception
18511939 @ Override
18521940 public int check (SSLFDProxy fd ) {
18531941 // Needs to be available for assignException() below.
1942+
1943+ JSSEngineReferenceImpl engine = engineRef .get ();
1944+
18541945 PK11Cert [] chain = null ;
18551946 String authType ;
18561947
18571948 try {
18581949 chain = SSL .PeerCertificateChain (fd );
18591950 authType = findAuthType (fd , chain );
1860- debug ("CertAuthType: " + authType );
1951+ if (engine != null ) {
1952+ engine .debug ("CertAuthType: " + authType );
1953+ }
18611954
18621955 if (chain == null || chain .length == 0 ) {
18631956 // When the chain is NULL, we'd always fail in the
@@ -1869,12 +1962,14 @@ public int check(SSLFDProxy fd) {
18691962 // Since we're a server validating the client's
18701963 // chain (and they didn't provide one), we should
18711964 // ignore it instead of forcing the problem.
1872- debug ("No client certificate chain and client cert not needed." );
1965+ if (engine != null ) {
1966+ engine .debug ("No client certificate chain and client cert not needed." );
1967+ }
18731968 return 0 ;
18741969 }
18751970 }
18761971 } catch (Exception excpt ) {
1877- return assignException (excpt , chain );
1972+ return assignException (excpt , chain , engine );
18781973 }
18791974
18801975 try {
@@ -1886,9 +1981,9 @@ public int check(SSLFDProxy fd) {
18861981 if (tm instanceof X509ExtendedTrustManager ) {
18871982 X509ExtendedTrustManager etm = (X509ExtendedTrustManager ) tm ;
18881983 if (as_server ) {
1889- etm .checkClientTrusted (chain , authType , JSSEngineReferenceImpl . this );
1984+ etm .checkClientTrusted (chain , authType , engine );
18901985 } else {
1891- etm .checkServerTrusted (chain , authType , JSSEngineReferenceImpl . this );
1986+ etm .checkServerTrusted (chain , authType , engine );
18921987 }
18931988 } else {
18941989 if (as_server ) {
@@ -1899,16 +1994,16 @@ public int check(SSLFDProxy fd) {
18991994 }
19001995 }
19011996 } catch (CertificateException excpt ) {
1902- return handleCertificateException (excpt , chain );
1997+ return handleCertificateException (excpt , chain , engine );
19031998 }
19041999
19052000 return 0 ;
19062001 }
19072002
1908- private int assignException (Exception excpt , PK11Cert [] chain ) {
2003+ private int assignException (Exception excpt , PK11Cert [] chain , JSSEngineReferenceImpl engine ) {
19092004 int nss_code = Cert .MatchExceptionToNSSError (excpt );
19102005
1911- if (seen_exception ) {
2006+ if (engine == null || engine . isSeenException () ) {
19122007 return nss_code ;
19132008 }
19142009
@@ -1935,29 +2030,30 @@ private int assignException(Exception excpt, PK11Cert[] chain) {
19352030 }
19362031 msg += "exception message: " + excpt .getMessage ();
19372032
1938- seen_exception = true ;
1939- ssl_exception = new SSLException (msg , excpt );
2033+ engine . setSeenException ( true ) ;
2034+ engine . setSslException ( new SSLException (msg , excpt ) );
19402035 return nss_code ;
19412036 }
19422037
1943- private int handleCertificateException (Exception excpt , PK11Cert [] chain ) {
2038+ private int handleCertificateException (Exception excpt , PK11Cert [] chain , JSSEngineReferenceImpl engine ) {
19442039 int nss_code = Cert .MatchExceptionToNSSError (excpt );
19452040
1946- if (seen_exception ) {
2041+ if (engine == null || engine . isSeenException () ) {
19472042 return nss_code ;
19482043 }
19492044
19502045 String msg = "Unable to validate "
19512046 + chain [0 ].getSubjectX500Principal () + ": "
19522047 + excpt .getMessage ();
19532048
1954- seen_exception = true ;
1955- ssl_exception = new SSLPeerUnverifiedException (msg );
2049+ engine .setSeenException (true );
2050+ engine .setSslException (new SSLPeerUnverifiedException (msg ));
2051+
19562052 return nss_code ;
19572053 }
19582054 }
19592055
1960- private class BypassBadHostname extends BadCertHandler {
2056+ private static class BypassBadHostname extends BadCertHandler {
19612057 public BypassBadHostname (SSLFDProxy fd , int error ) {
19622058 super (fd , error );
19632059 }
0 commit comments