if ((connectionState != cs_START) && !recvCN) { recvCN = true; // Only receive the Exception once fatal(Alerts.alert_internal_error, "Inbound closed before receiving peer's close_notify: " + "possible truncation attack?"); } else { /* * Currently, this is a no-op, but in case we change * the close inbound code later. */ closeInboundInternal(); } } /** * Returns the network inbound data closure state */ synchronized public boolean isInboundDone() { return inboundDone; } // // Misc stuff // /** * Returns the current <code>SSLSession</code> for this * <code>SSLEngine</code> * <P> * These can be long lived, and frequently correspond to an * entire login session for some user. */ synchronized public SSLSession getSession() { return sess; } @Override synchronized public SSLSession getHandshakeSession() { return handshakeSession; } synchronized void setHandshakeSession(SSLSessionImpl session) { handshakeSession = session; } /** * Returns a delegated <code>Runnable</code> task for * this <code>SSLEngine</code>. */ synchronized public Runnable getDelegatedTask() { if (handshaker != null) { return handshaker.getTask(); } return null; } // // EXCEPTION AND ALERT HANDLING // /* * Send a warning alert. */ void warning(byte description) { sendAlert(Alerts.alert_warning, description); } synchronized void fatal(byte description, String diagnostic) throws SSLException { fatal(description, diagnostic, null); } synchronized void fatal(byte description, Throwable cause) throws SSLException { fatal(description, null, cause); } /* * We've got a fatal error here, so start the shutdown process. * * Because of the way the code was written, we have some code * calling fatal directly when the "description" is known * and some throwing Exceptions which are then caught by higher * levels which then call here. This code needs to determine * if one of the lower levels has already started the process. * * We won't worry about Error's, if we have one of those, * we're in worse trouble. Note: the networking code doesn't * deal with Errors either. */ synchronized void fatal(byte description, String diagnostic, Throwable cause) throws SSLException { /* * If we have no further information, make a general-purpose * message for folks to see. We generally have one or the other. */ if (diagnostic == null) { diagnostic = "General SSLEngine problem"; } if (cause == null) { cause = Alerts.getSSLException(description, cause, diagnostic); } /* * If we've already shutdown because of an error, * there is nothing we can do except rethrow the exception. * * Most exceptions seen here will be SSLExceptions. * We may find the occasional Exception which hasn't been * converted to a SSLException, so we'll do it here. */ if (closeReason != null) { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", fatal: engine already closed. Rethrowing " + cause.toString()); } if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else if (cause instanceof SSLException) { throw (SSLException)cause; } else if (cause instanceof Exception) { SSLException ssle = new SSLException( "fatal SSLEngine condition"); ssle.initCause(cause); throw ssle; } } if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", fatal error: " + description + ": " + diagnostic + "\n" + cause.toString()); } /* * Ok, this engine's going down. */ int oldState = connectionState; connectionState = cs_ERROR; inboundDone = true; sess.invalidate(); if (handshakeSession != null) { handshakeSession.invalidate(); } /* * If we haven't even started handshaking yet, no need * to generate the fatal close alert. */ if (oldState != cs_START) { sendAlert(Alerts.alert_fatal, description); } if (cause instanceof SSLException) { // only true if != null closeReason = (SSLException)cause; } else { /* * Including RuntimeExceptions, but we'll throw those * down below. The closeReason isn't used again, * except for null checks. */ closeReason = Alerts.getSSLException(description, cause, diagnostic); } writer.closeOutbound(); connectionState = cs_CLOSED; // See comment in changeReadCiphers() readCipher.dispose(); writeCipher.dispose(); if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else { throw closeReason; } } /* * Process an incoming alert ... caller must already have synchronized * access to "this". */ private void recvAlert() throws IOException { byte level = (byte)inputRecord.read(); byte description = (byte)inputRecord.read(); if (description == -1) { // check for short message fatal(Alerts.alert_illegal_parameter, "Short alert message"); } if (debug != null && (Debug.isOn("record") || Debug.isOn("handshake"))) { synchronized (System.out) { System.out.print(threadName()); System.out.print(", RECV " + protocolVersion + " ALERT: "); if (level == Alerts.alert_fatal) { System.out.print("fatal, "); } else if (level == Alerts.alert_warning) { System.out.print("warning, "); } else { System.out.print("<level " + (0x0ff & level) + ">, "); } System.out.println(Alerts.alertDescription(description)); } } if (level == Alerts.alert_warning) { if (description == Alerts.alert_close_notify) { if (connectionState == cs_HANDSHAKE) { fatal(Alerts.alert_unexpected_message, "Received close_notify during handshake"); } else { recvCN = true; closeInboundInternal(); // reply to close } } else { // // The other legal warnings relate to certificates, // e.g. no_certificate, bad_certificate, etc; these // are important to the handshaking code, which can // also handle illegal protocol alerts if needed. // if (handshaker != null) { handshaker.handshakeAlert(description); } } } else { // fatal or unknown level String reason = "Received fatal alert: " + Alerts.alertDescription(description); if (closeReason == null) { closeReason = Alerts.getSSLException(description, reason); } fatal(Alerts.alert_unexpected_message, reason); } } /* * Emit alerts. Caller must have synchronized with "this". */ private void sendAlert(byte level, byte description) { // the connectionState cannot be cs_START if (connectionState >= cs_CLOSED) { return; } // For initial handshaking, don't send alert message to peer if // handshaker has not started. if (connectionState == cs_HANDSHAKE && (handshaker == null || !handshaker.started())) { return; } EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this); r.setVersion(protocolVersion); boolean useDebug = debug != null && Debug.isOn("ssl"); if (useDebug) { synchronized (System.out) { System.out.print(threadName()); System.out.print(", SEND " + protocolVersion + " ALERT: "); if (level == Alerts.alert_fatal) { System.out.print("fatal, "); } else if (level == Alerts.alert_warning) { System.out.print("warning, "); } else { System.out.print("<level = " + (0x0ff & level) + ">, "); } System.out.println("description = " + Alerts.alertDescription(description)); } } r.write(level); r.write(description); try { writeRecord(r); } catch (IOException e) { if (useDebug) { System.out.println(threadName() + ", Exception sending alert: " + e); } } } // // VARIOUS OTHER METHODS (COMMON TO SSLSocket) // /** * Controls whether new connections may cause creation of new SSL * sessions. * * As long as handshaking has not started, we can change * whether we enable session creations. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setEnableSessionCreation(boolean flag) { enableSessionCreation = flag; if ((handshaker != null) && !handshaker.activated()) { handshaker.setEnableSessionCreation(enableSessionCreation); } } /** * Returns true if new connections may cause creation of new SSL * sessions. */ synchronized public boolean getEnableSessionCreation() { return enableSessionCreation; } /** * Sets the flag controlling whether a server mode engine * *REQUIRES* SSL client authentication. * * As long as handshaking has not started, we can change * whether client authentication is needed. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setNeedClientAuth(boolean flag) { doClientAuth = (flag ? SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); if ((handshaker != null) && (handshaker instanceof ServerHandshaker) && !handshaker.activated()) { ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); } } synchronized public boolean getNeedClientAuth() { return (doClientAuth == SSLEngineImpl.clauth_required); } /** * Sets the flag controlling whether a server mode engine * *REQUESTS* SSL client authentication. * * As long as handshaking has not started, we can change * whether client authentication is requested. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setWantClientAuth(boolean flag) { doClientAuth = (flag ? SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); if ((handshaker != null) && (handshaker instanceof ServerHandshaker) && !handshaker.activated()) { ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); } } synchronized public boolean getWantClientAuth() { return (doClientAuth == SSLEngineImpl.clauth_requested); } /** * Sets the flag controlling whether the engine is in SSL * client or server mode. Must be called before any SSL * traffic has started. */ synchronized public void setUseClientMode(boolean flag) { switch (connectionState) { case cs_START: /* * If we need to change the engine mode and the enabled * protocols haven't specifically been set by the user, * change them to the corresponding default ones. */ if (roleIsServer != (!flag) && sslContext.isDefaultProtocolList(enabledProtocols)) { enabledProtocols = sslContext.getDefaultProtocolList(!flag); } roleIsServer = !flag; serverModeSet = true; break; case cs_HANDSHAKE: /* * If we have a handshaker, but haven't started * SSL traffic, we can throw away our current * handshaker, and start from scratch. Don't * need to call doneConnect() again, we already * have the streams. */ assert(handshaker != null); if (!handshaker.activated()) { /* * If we need to change the engine mode and the enabled * protocols haven't specifically been set by the user, * change them to the corresponding default ones. */ if (roleIsServer != (!flag) && sslContext.isDefaultProtocolList(enabledProtocols)) { enabledProtocols = sslContext.getDefaultProtocolList(!flag); } roleIsServer = !flag; connectionState = cs_START; initHandshaker(); break; } // If handshake has started, that's an error. Fall through... default: if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", setUseClientMode() invoked in state = " + connectionState); } /* * We can let them continue if they catch this correctly, * we don't need to shut this down. */ throw new IllegalArgumentException( "Cannot change mode after SSL traffic has started"); } } synchronized public boolean getUseClientMode() { return !roleIsServer; } /** * Returns the names of the cipher suites which could be enabled for use * on an SSL connection. Normally, only a subset of these will actually * be enabled by default, since this list may include cipher suites which * do not support the mutual authentication of servers and clients, or * which do not protect data confidentiality. Servers may also need * certain kinds of certificates to use certain cipher suites. * * @return an array of cipher suite names */ public String[] getSupportedCipherSuites() { return sslContext.getSuportedCipherSuiteList().toStringArray(); } /** * Controls which particular cipher suites are enabled for use on * this connection. The cipher suites must have been listed by * getCipherSuites() as being supported. Even if a suite has been * enabled, it might never be used if no peer supports it or the * requisite certificates (and private keys) are not available. * * @param suites Names of all the cipher suites to enable. */ synchronized public void setEnabledCipherSuites(String[] suites) { enabledCipherSuites = new CipherSuiteList(suites); if ((handshaker != null) && !handshaker.activated()) { handshaker.setEnabledCipherSuites(enabledCipherSuites); } } /** * Returns the names of the SSL cipher suites which are currently enabled * for use on this connection. When an SSL engine is first created, * all enabled cipher suites <em>(a)</em> protect data confidentiality, * by traffic encryption, and <em>(b)</em> can mutually authenticate * both clients and servers. Thus, in some environments, this value * might be empty. * * @return an array of cipher suite names */ synchronized public String[] getEnabledCipherSuites() { return enabledCipherSuites.toStringArray(); } /** * Returns the protocols that are supported by this implementation. * A subset of the supported protocols may be enabled for this connection * @return an array of protocol names. */ public String[] getSupportedProtocols() { return sslContext.getSuportedProtocolList().toStringArray(); } /** * Controls which protocols are enabled for use onLines 1501–2000 of 2073. Previous lines Load more
if ((connectionState != cs_START) && !recvCN) { recvCN = true; // Only receive the Exception once fatal(Alerts.alert_internal_error, "Inbound closed before receiving peer's close_notify: " + "possible truncation attack?"); } else { /* * Currently, this is a no-op, but in case we change * the close inbound code later. */ closeInboundInternal(); } } /** * Returns the network inbound data closure state */ synchronized public boolean isInboundDone() { return inboundDone; } // // Misc stuff // /** * Returns the current <code>SSLSession</code> for this * <code>SSLEngine</code> * <P> * These can be long lived, and frequently correspond to an * entire login session for some user. */ synchronized public SSLSession getSession() { return sess; } @Override synchronized public SSLSession getHandshakeSession() { return handshakeSession; } synchronized void setHandshakeSession(SSLSessionImpl session) { handshakeSession = session; } /** * Returns a delegated <code>Runnable</code> task for * this <code>SSLEngine</code>. */ synchronized public Runnable getDelegatedTask() { if (handshaker != null) { return handshaker.getTask(); } return null; } // // EXCEPTION AND ALERT HANDLING // /* * Send a warning alert. */ void warning(byte description) { sendAlert(Alerts.alert_warning, description); } synchronized void fatal(byte description, String diagnostic) throws SSLException { fatal(description, diagnostic, null); } synchronized void fatal(byte description, Throwable cause) throws SSLException { fatal(description, null, cause); } /* * We've got a fatal error here, so start the shutdown process. * * Because of the way the code was written, we have some code * calling fatal directly when the "description" is known * and some throwing Exceptions which are then caught by higher * levels which then call here. This code needs to determine * if one of the lower levels has already started the process. * * We won't worry about Error's, if we have one of those, * we're in worse trouble. Note: the networking code doesn't * deal with Errors either. */ synchronized void fatal(byte description, String diagnostic, Throwable cause) throws SSLException { /* * If we have no further information, make a general-purpose * message for folks to see. We generally have one or the other. */ if (diagnostic == null) { diagnostic = "General SSLEngine problem"; } if (cause == null) { cause = Alerts.getSSLException(description, cause, diagnostic); } /* * If we've already shutdown because of an error, * there is nothing we can do except rethrow the exception. * * Most exceptions seen here will be SSLExceptions. * We may find the occasional Exception which hasn't been * converted to a SSLException, so we'll do it here. */ if (closeReason != null) { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", fatal: engine already closed. Rethrowing " + cause.toString()); } if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else if (cause instanceof SSLException) { throw (SSLException)cause; } else if (cause instanceof Exception) { SSLException ssle = new SSLException( "fatal SSLEngine condition"); ssle.initCause(cause); throw ssle; } } if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", fatal error: " + description + ": " + diagnostic + "\n" + cause.toString()); } /* * Ok, this engine's going down. */ int oldState = connectionState; connectionState = cs_ERROR; inboundDone = true; sess.invalidate(); if (handshakeSession != null) { handshakeSession.invalidate(); } /* * If we haven't even started handshaking yet, no need * to generate the fatal close alert. */ if (oldState != cs_START) { sendAlert(Alerts.alert_fatal, description); } if (cause instanceof SSLException) { // only true if != null closeReason = (SSLException)cause; } else { /* * Including RuntimeExceptions, but we'll throw those * down below. The closeReason isn't used again, * except for null checks. */ closeReason = Alerts.getSSLException(description, cause, diagnostic); } writer.closeOutbound(); connectionState = cs_CLOSED; // See comment in changeReadCiphers() readCipher.dispose(); writeCipher.dispose(); if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else { throw closeReason; } } /* * Process an incoming alert ... caller must already have synchronized * access to "this". */ private void recvAlert() throws IOException { byte level = (byte)inputRecord.read(); byte description = (byte)inputRecord.read(); if (description == -1) { // check for short message fatal(Alerts.alert_illegal_parameter, "Short alert message"); } if (debug != null && (Debug.isOn("record") || Debug.isOn("handshake"))) { synchronized (System.out) { System.out.print(threadName()); System.out.print(", RECV " + protocolVersion + " ALERT: "); if (level == Alerts.alert_fatal) { System.out.print("fatal, "); } else if (level == Alerts.alert_warning) { System.out.print("warning, "); } else { System.out.print("<level " + (0x0ff & level) + ">, "); } System.out.println(Alerts.alertDescription(description)); } } if (level == Alerts.alert_warning) { if (description == Alerts.alert_close_notify) { if (connectionState == cs_HANDSHAKE) { fatal(Alerts.alert_unexpected_message, "Received close_notify during handshake"); } else { recvCN = true; closeInboundInternal(); // reply to close } } else { // // The other legal warnings relate to certificates, // e.g. no_certificate, bad_certificate, etc; these // are important to the handshaking code, which can // also handle illegal protocol alerts if needed. // if (handshaker != null) { handshaker.handshakeAlert(description); } } } else { // fatal or unknown level String reason = "Received fatal alert: " + Alerts.alertDescription(description); if (closeReason == null) { closeReason = Alerts.getSSLException(description, reason); } fatal(Alerts.alert_unexpected_message, reason); } } /* * Emit alerts. Caller must have synchronized with "this". */ private void sendAlert(byte level, byte description) { // the connectionState cannot be cs_START if (connectionState >= cs_CLOSED) { return; } // For initial handshaking, don't send alert message to peer if // handshaker has not started. if (connectionState == cs_HANDSHAKE && (handshaker == null || !handshaker.started())) { return; } EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this); r.setVersion(protocolVersion); boolean useDebug = debug != null && Debug.isOn("ssl"); if (useDebug) { synchronized (System.out) { System.out.print(threadName()); System.out.print(", SEND " + protocolVersion + " ALERT: "); if (level == Alerts.alert_fatal) { System.out.print("fatal, "); } else if (level == Alerts.alert_warning) { System.out.print("warning, "); } else { System.out.print("<level = " + (0x0ff & level) + ">, "); } System.out.println("description = " + Alerts.alertDescription(description)); } } r.write(level); r.write(description); try { writeRecord(r); } catch (IOException e) { if (useDebug) { System.out.println(threadName() + ", Exception sending alert: " + e); } } } // // VARIOUS OTHER METHODS (COMMON TO SSLSocket) // /** * Controls whether new connections may cause creation of new SSL * sessions. * * As long as handshaking has not started, we can change * whether we enable session creations. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setEnableSessionCreation(boolean flag) { enableSessionCreation = flag; if ((handshaker != null) && !handshaker.activated()) { handshaker.setEnableSessionCreation(enableSessionCreation); } } /** * Returns true if new connections may cause creation of new SSL * sessions. */ synchronized public boolean getEnableSessionCreation() { return enableSessionCreation; } /** * Sets the flag controlling whether a server mode engine * *REQUIRES* SSL client authentication. * * As long as handshaking has not started, we can change * whether client authentication is needed. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setNeedClientAuth(boolean flag) { doClientAuth = (flag ? SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); if ((handshaker != null) && (handshaker instanceof ServerHandshaker) && !handshaker.activated()) { ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); } } synchronized public boolean getNeedClientAuth() { return (doClientAuth == SSLEngineImpl.clauth_required); } /** * Sets the flag controlling whether a server mode engine * *REQUESTS* SSL client authentication. * * As long as handshaking has not started, we can change * whether client authentication is requested. Otherwise, * we will need to wait for the next handshake. */ synchronized public void setWantClientAuth(boolean flag) { doClientAuth = (flag ? SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); if ((handshaker != null) && (handshaker instanceof ServerHandshaker) && !handshaker.activated()) { ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); } } synchronized public boolean getWantClientAuth() { return (doClientAuth == SSLEngineImpl.clauth_requested); } /** * Sets the flag controlling whether the engine is in SSL * client or server mode. Must be called before any SSL * traffic has started. */ synchronized public void setUseClientMode(boolean flag) { switch (connectionState) { case cs_START: /* * If we need to change the engine mode and the enabled * protocols haven't specifically been set by the user, * change them to the corresponding default ones. */ if (roleIsServer != (!flag) && sslContext.isDefaultProtocolList(enabledProtocols)) { enabledProtocols = sslContext.getDefaultProtocolList(!flag); } roleIsServer = !flag; serverModeSet = true; break; case cs_HANDSHAKE: /* * If we have a handshaker, but haven't started * SSL traffic, we can throw away our current * handshaker, and start from scratch. Don't * need to call doneConnect() again, we already * have the streams. */ assert(handshaker != null); if (!handshaker.activated()) { /* * If we need to change the engine mode and the enabled * protocols haven't specifically been set by the user, * change them to the corresponding default ones. */ if (roleIsServer != (!flag) && sslContext.isDefaultProtocolList(enabledProtocols)) { enabledProtocols = sslContext.getDefaultProtocolList(!flag); } roleIsServer = !flag; connectionState = cs_START; initHandshaker(); break; } // If handshake has started, that's an error. Fall through... default: if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", setUseClientMode() invoked in state = " + connectionState); } /* * We can let them continue if they catch this correctly, * we don't need to shut this down. */ throw new IllegalArgumentException( "Cannot change mode after SSL traffic has started"); } } synchronized public boolean getUseClientMode() { return !roleIsServer; } /** * Returns the names of the cipher suites which could be enabled for use * on an SSL connection. Normally, only a subset of these will actually * be enabled by default, since this list may include cipher suites which * do not support the mutual authentication of servers and clients, or * which do not protect data confidentiality. Servers may also need * certain kinds of certificates to use certain cipher suites. * * @return an array of cipher suite names */ public String[] getSupportedCipherSuites() { return sslContext.getSuportedCipherSuiteList().toStringArray(); } /** * Controls which particular cipher suites are enabled for use on * this connection. The cipher suites must have been listed by * getCipherSuites() as being supported. Even if a suite has been * enabled, it might never be used if no peer supports it or the * requisite certificates (and private keys) are not available. * * @param suites Names of all the cipher suites to enable. */ synchronized public void setEnabledCipherSuites(String[] suites) { enabledCipherSuites = new CipherSuiteList(suites); if ((handshaker != null) && !handshaker.activated()) { handshaker.setEnabledCipherSuites(enabledCipherSuites); } } /** * Returns the names of the SSL cipher suites which are currently enabled * for use on this connection. When an SSL engine is first created, * all enabled cipher suites <em>(a)</em> protect data confidentiality, * by traffic encryption, and <em>(b)</em> can mutually authenticate * both clients and servers. Thus, in some environments, this value * might be empty. * * @return an array of cipher suite names */ synchronized public String[] getEnabledCipherSuites() { return enabledCipherSuites.toStringArray(); } /** * Returns the protocols that are supported by this implementation. * A subset of the supported protocols may be enabled for this connection * @return an array of protocol names. */ public String[] getSupportedProtocols() { return sslContext.getSuportedProtocolList().toStringArray(); } /** * Controls which protocols are enabled for use on