diff --git a/src/share/classes/sun/security/ssl/ClientHandshaker.java b/src/share/classes/sun/security/ssl/ClientHandshaker.java index 1fd38419fa5b46f40e0281b3488e853c3cce6f33..187ae314f76917199723d93bdd01fec740aa606a 100644 --- a/src/share/classes/sun/security/ssl/ClientHandshaker.java +++ b/src/share/classes/sun/security/ssl/ClientHandshaker.java @@ -191,19 +191,24 @@ final class ClientHandshaker extends Handshaker { */ @Override void processMessage(byte type, int messageLen) throws IOException { - if (state >= type - && (type != HandshakeMessage.ht_hello_request)) { - throw new SSLProtocolException( - "Handshake message sequence violation, " + type); - } + + // check the handshake state + List ignoredOptStates = handshakeState.check(type); switch (type) { case HandshakeMessage.ht_hello_request: - this.serverHelloRequest(new HelloRequest(input)); + HelloRequest helloRequest = new HelloRequest(input); + handshakeState.update(helloRequest, resumingSession); + this.serverHelloRequest(helloRequest); break; case HandshakeMessage.ht_server_hello: - this.serverHello(new ServerHello(input, messageLen)); + ServerHello serverHello = new ServerHello(input, messageLen); + this.serverHello(serverHello); + + // This handshake state update needs the resumingSession value + // set by serverHello(). + handshakeState.update(serverHello, resumingSession); break; case HandshakeMessage.ht_certificate: @@ -213,7 +218,9 @@ final class ClientHandshaker extends Handshaker { "unexpected server cert chain"); // NOTREACHED } - this.serverCertificate(new CertificateMsg(input)); + CertificateMsg certificateMsg = new CertificateMsg(input); + handshakeState.update(certificateMsg, resumingSession); + this.serverCertificate(certificateMsg); serverKey = session.getPeerCertificates()[0].getPublicKey(); break; @@ -249,15 +256,20 @@ final class ClientHandshaker extends Handshaker { } try { - this.serverKeyExchange(new RSA_ServerKeyExchange(input)); + RSA_ServerKeyExchange rsaSrvKeyExchange = + new RSA_ServerKeyExchange(input); + handshakeState.update(rsaSrvKeyExchange, resumingSession); + this.serverKeyExchange(rsaSrvKeyExchange); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } break; case K_DH_ANON: try { - this.serverKeyExchange(new DH_ServerKeyExchange( - input, protocolVersion)); + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange(input, protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } @@ -265,11 +277,14 @@ final class ClientHandshaker extends Handshaker { case K_DHE_DSS: case K_DHE_RSA: try { - this.serverKeyExchange(new DH_ServerKeyExchange( + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange( input, serverKey, clnt_random.random_bytes, svr_random.random_bytes, messageLen, - getLocalSupportedSignAlgs(), protocolVersion)); + getLocalSupportedSignAlgs(), protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } @@ -278,10 +293,13 @@ final class ClientHandshaker extends Handshaker { case K_ECDHE_RSA: case K_ECDH_ANON: try { - this.serverKeyExchange(new ECDH_ServerKeyExchange + ECDH_ServerKeyExchange ecdhSrvKeyExchange = + new ECDH_ServerKeyExchange (input, serverKey, clnt_random.random_bytes, svr_random.random_bytes, - getLocalSupportedSignAlgs(), protocolVersion)); + getLocalSupportedSignAlgs(), protocolVersion); + handshakeState.update(ecdhSrvKeyExchange, resumingSession); + this.serverKeyExchange(ecdhSrvKeyExchange); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } @@ -320,6 +338,7 @@ final class ClientHandshaker extends Handshaker { if (debug != null && Debug.isOn("handshake")) { certRequest.print(System.out); } + handshakeState.update(certRequest, resumingSession); if (protocolVersion.v >= ProtocolVersion.TLS12.v) { Collection peerSignAlgs = @@ -345,33 +364,23 @@ final class ClientHandshaker extends Handshaker { break; case HandshakeMessage.ht_server_hello_done: - this.serverHelloDone(new ServerHelloDone(input)); + ServerHelloDone serverHelloDone = new ServerHelloDone(input); + handshakeState.update(serverHelloDone, resumingSession); + this.serverHelloDone(serverHelloDone); break; case HandshakeMessage.ht_finished: - // A ChangeCipherSpec record must have been received prior to - // reception of the Finished message (RFC 5246, 7.4.9). - if (!receivedChangeCipherSpec()) { - fatalSE(Alerts.alert_handshake_failure, - "Received Finished message before ChangeCipherSpec"); - } + Finished serverFinished = + new Finished(protocolVersion, input, cipherSuite); + handshakeState.update(serverFinished, resumingSession); + this.serverFinished(serverFinished); - this.serverFinished( - new Finished(protocolVersion, input, cipherSuite)); break; default: throw new SSLProtocolException( "Illegal client handshake msg, " + type); } - - // - // Move state machine forward if the message handling - // code didn't already do so - // - if (state < type) { - state = type; - } } /* @@ -389,7 +398,7 @@ final class ClientHandshaker extends Handshaker { // Could be (e.g. at connection setup) that we already // sent the "client hello" but the server's not seen it. // - if (state < HandshakeMessage.ht_client_hello) { + if (!clientHelloDelivered) { if (!secureRenegotiation && !allowUnsafeRenegotiation) { // renegotiation is not allowed. if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { @@ -613,7 +622,6 @@ final class ClientHandshaker extends Handshaker { // looks fine; resume it, and update the state machine. resumingSession = true; - state = HandshakeMessage.ht_finished - 1; calculateConnectionKeys(session.getMasterSecret()); if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); @@ -904,6 +912,7 @@ final class ClientHandshaker extends Handshaker { m1.print(System.out); } m1.write(output); + handshakeState.update(m1, resumingSession); } } @@ -1068,6 +1077,7 @@ final class ClientHandshaker extends Handshaker { } m2.write(output); + handshakeState.update(m2, resumingSession); /* * THIRD, send a "change_cipher_spec" record followed by the @@ -1170,6 +1180,7 @@ final class ClientHandshaker extends Handshaker { m3.print(System.out); } m3.write(output); + handshakeState.update(m3, resumingSession); output.doHashes(); } @@ -1227,6 +1238,8 @@ final class ClientHandshaker extends Handshaker { if (resumingSession) { input.digestNow(); sendChangeCipherAndFinish(true); + } else { + handshakeFinished = true; } session.setLastAccessedTime(System.currentTimeMillis()); @@ -1272,13 +1285,6 @@ final class ClientHandshaker extends Handshaker { if (secureRenegotiation) { clientVerifyData = mesg.getVerifyData(); } - - /* - * Update state machine so server MUST send 'finished' next. - * (In "long" handshake case; in short case, we're responding - * to its message.) - */ - state = HandshakeMessage.ht_finished - 1; } diff --git a/src/share/classes/sun/security/ssl/HandshakeMessage.java b/src/share/classes/sun/security/ssl/HandshakeMessage.java index 976b76e6fed4625b4328133aace279260353a369..f453b650df21207f8a7d8954657c8b502abe5ab1 100644 --- a/src/share/classes/sun/security/ssl/HandshakeMessage.java +++ b/src/share/classes/sun/security/ssl/HandshakeMessage.java @@ -89,6 +89,8 @@ public abstract class HandshakeMessage { static final byte ht_finished = 20; + static final byte ht_not_applicable = -1; // N/A + /* Class and subclass dynamic debugging support */ public static final Debug debug = Debug.getInstance("ssl"); diff --git a/src/share/classes/sun/security/ssl/HandshakeStateManager.java b/src/share/classes/sun/security/ssl/HandshakeStateManager.java new file mode 100644 index 0000000000000000000000000000000000000000..d1dcea020ab8c09ec3890d1295f299319d92c49b --- /dev/null +++ b/src/share/classes/sun/security/ssl/HandshakeStateManager.java @@ -0,0 +1,765 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.Collections; +import java.util.List; +import java.util.LinkedList; +import java.util.HashMap; +import javax.net.ssl.SSLProtocolException; + +import static sun.security.ssl.CipherSuite.KeyExchange; +import static sun.security.ssl.CipherSuite.KeyExchange.*; +import static sun.security.ssl.HandshakeStateManager.HandshakeState.*; +import static sun.security.ssl.HandshakeMessage.*; + +/* + * Handshake state manager. + * + * Messages flow for a full handshake: + * + * - - + * | HelloRequest (No.0, RFC 5246) [*] | + * | <-------------------------------------------- | + * | | + * | ClientHello (No.1, RFC 5246) | + * | --------------------------------------------> | + * | | + * C | ServerHello (No.2, RFC 5246) | S + * L | SupplementalData (No.23, RFC4680) [*] | E + * I | Certificate (No.11, RFC 5246) [*] | R + * E | CertificateStatus (No.22, RFC 6066) [*] | V + * N | ServerKeyExchange (No.12, RFC 5246) [*] | E + * T | CertificateRequest (No.13, RFC 5246) [*] | R + * | ServerHelloDone (No.14, RFC 5246) | + * | <-------------------------------------------- | + * | | + * | SupplementalData (No.23, RFC4680) [*] | + * | Certificate (No.11, RFC 5246) [*] Or | + * | CertificateURL (No.21, RFC6066) [*] | + * | ClientKeyExchange (No.16, RFC 5246) | + * | CertificateVerify (No.15, RFC 5246) [*] | + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | --------------------------------------------> | + * | | + * | NewSessionTicket (No.4, RFC4507) [*] | + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | <-------------------------------------------- | + * - - + * [*] Indicates optional or situation-dependent messages that are not + * always sent. + * + * Message flow for an abbreviated handshake: + * - - + * | ClientHello (No.1, RFC 5246) | + * | --------------------------------------------> | + * | | + * C | ServerHello (No.2, RFC 5246) | S + * L | NewSessionTicket (No.4, RFC4507) [*] | E + * I | [ChangeCipherSpec] (RFC 5246) | R + * E | Finished (No.20, RFC 5246) | V + * N | <-------------------------------------------- | E + * T | | R + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | --------------------------------------------> | + * - - + * + * + * State machine of handshake states: + * + * +--------------+ + * START -----> | HelloRequest | + * | +--------------+ + * | | + * v v + * +---------------------+ --> +---------------------+ + * | ClientHello | | HelloVerifyRequest | + * +---------------------+ <-- +---------------------+ + * | + * | + * ========================================================================= + * | + * v + * +---------------------+ + * | ServerHello | ----------------------------------+------+ + * +---------------------+ --> +-------------------------+ | | + * | | Server SupplementalData | | | + * | +-------------------------+ | | + * | | | | + * v v | | + * +---------------------+ | | + * +---- | Server Certificate | | | + * | +---------------------+ | | + * | | | | + * | | +--------------------+ | | + * | +-> | CertificateStatus | | | + * | | +--------------------+ v | + * | | | | +--------------------+ | + * | v v +--> | ServerKeyExchange | | + * | +---------------------+ | +--------------------+ | + * | | CertificateRequest | | | | + * | +---------------------+ <-+---------+ | + * | | | | | + * v v | | | + * +---------------------+ <-------+ | | + * | ServerHelloDone | <-----------------+ | + * +---------------------+ | + * | | | + * | | | + * | | | + * ========================================================================= + * | | | + * | v | + * | +-------------------------+ | + * | | Client SupplementalData | --------------+ | + * | +-------------------------+ | | + * | | | | + * | v | | + * | +--------------------+ | | + * +-> | Client Certificate | ALT. | | + * | +--------------------+----------------+ | | + * | | CertificateURL | | | + * | +----------------+ | | + * v | | + * +-------------------+ <------------------------+ | + * | ClientKeyExchange | | + * +-------------------+ | + * | | | + * | v | + * | +-------------------+ | + * | | CertificateVerify | | + * | +-------------------+ | + * | | | + * v v | + * +-------------------------+ | + * | Client ChangeCipherSpec | <---------------+ | + * +-------------------------+ | | + * | | | + * v | | + * +-----------------+ (abbreviated) | | + * | Client Finished | -------------> END | | + * +-----------------+ (Abbreviated handshake) | | + * | | | + * | (full) | | + * | | | + * ================================ | | + * | | | + * | ================================ + * | | | + * v | | + * +------------------+ | (abbreviated) | + * | NewSessionTicket | <--------------------------------+ + * +------------------+ | | + * | | | + * v | | + * +-------------------------+ | (abbreviated) | + * | Server ChangeCipherSpec | <-------------------------------------+ + * +-------------------------+ | + * | | + * v | + * +-----------------+ (abbreviated) | + * | Server Finished | -------------------------+ + * +-----------------+ + * | (full) + * v + * END (Full handshake) + * + * + * The scenarios of the use of this class: + * 1. Create an instance of HandshakeStateManager during the initializtion + * handshake. + * 2. If receiving a handshake message, call HandshakeStateManager.check() + * to make sure that the message is of the expected handshake type. And + * then call HandshakeStateManager.update() in case handshake states may + * be impacted by this new incoming handshake message. + * 3. On delivering a handshake message, call HandshakeStateManager.update() + * in case handshake states may by thie new outgoing handshake message. + * 4. On receiving and delivering ChangeCipherSpec message, call + * HandshakeStateManager.changeCipherSpec() to check the present sequence + * of this message, and update the states if necessary. + */ +final class HandshakeStateManager { + // upcoming handshake states. + private LinkedList upcomingStates; + private LinkedList alternatives; + + private static final boolean debugIsOn; + + private static final HashMap handshakeTypes; + + static { + debugIsOn = (Handshaker.debug != null) && + Debug.isOn("handshake") && Debug.isOn("verbose"); + handshakeTypes = new HashMap<>(8); + + handshakeTypes.put(ht_hello_request, "hello_request"); + handshakeTypes.put(ht_client_hello, "client_hello"); + handshakeTypes.put(ht_server_hello, "server_hello"); + handshakeTypes.put(ht_certificate, "certificate"); + handshakeTypes.put(ht_server_key_exchange, "server_key_exchange"); + handshakeTypes.put(ht_server_hello_done, "server_hello_done"); + handshakeTypes.put(ht_certificate_verify, "certificate_verify"); + handshakeTypes.put(ht_client_key_exchange, "client_key_exchange"); + handshakeTypes.put(ht_finished, "finished"); + } + + HandshakeStateManager() { + this.upcomingStates = new LinkedList<>(); + this.alternatives = new LinkedList<>(); + } + + // + // enumation of handshake type + // + static enum HandshakeState { + HS_HELLO_REQUEST( + "hello_request", + HandshakeMessage.ht_hello_request), + HS_CLIENT_HELLO( + "client_hello", + HandshakeMessage.ht_client_hello), + HS_SERVER_HELLO( + "server_hello", + HandshakeMessage.ht_server_hello), + HS_SERVER_CERTIFICATE( + "server certificate", + HandshakeMessage.ht_certificate), + HS_SERVER_KEY_EXCHANGE( + "server_key_exchange", + HandshakeMessage.ht_server_key_exchange, true), + HS_CERTIFICATE_REQUEST( + "certificate_request", + HandshakeMessage.ht_certificate_request, true), + HS_SERVER_HELLO_DONE( + "server_hello_done", + HandshakeMessage.ht_server_hello_done), + HS_CLIENT_CERTIFICATE( + "client certificate", + HandshakeMessage.ht_certificate, true), + HS_CLIENT_KEY_EXCHANGE( + "client_key_exchange", + HandshakeMessage.ht_client_key_exchange), + HS_CERTIFICATE_VERIFY( + "certificate_verify", + HandshakeMessage.ht_certificate_verify, true), + HS_CLIENT_CHANGE_CIPHER_SPEC( + "client change_cipher_spec", + HandshakeMessage.ht_not_applicable), + HS_CLIENT_FINISHED( + "client finished", + HandshakeMessage.ht_finished), + HS_SERVER_CHANGE_CIPHER_SPEC( + "server change_cipher_spec", + HandshakeMessage.ht_not_applicable), + HS_SERVER_FINISHED( + "server finished", + HandshakeMessage.ht_finished); + + final String description; + final byte handshakeType; + final boolean isOptional; + + HandshakeState(String description, byte handshakeType) { + this.description = description; + this.handshakeType = handshakeType; + this.isOptional = false; + } + + HandshakeState(String description, + byte handshakeType, boolean isOptional) { + + this.description = description; + this.handshakeType = handshakeType; + this.isOptional = isOptional; + } + + public String toString() { + return description + "[" + handshakeType + "]" + + (isOptional ? "(optional)" : ""); + } + } + + boolean isEmpty() { + return upcomingStates.isEmpty(); + } + + List check(byte handshakeType) throws SSLProtocolException { + List ignoredOptional = new LinkedList<>(); + String exceptionMsg = + "Handshake message sequence violation, " + handshakeType; + + if (debugIsOn) { + System.out.println( + "check handshake state: " + toString(handshakeType)); + } + + if (upcomingStates.isEmpty()) { + // Is it a kickstart message? + if ((handshakeType != HandshakeMessage.ht_hello_request) && + (handshakeType != HandshakeMessage.ht_client_hello)) { + throw new SSLProtocolException( + "Handshake message sequence violation, " + handshakeType); + } + + // It is a kickstart message. + return Collections.emptyList(); + } + + // Ignore the checking for HelloRequest messages as they + // may be sent by the server at any time. + if (handshakeType == HandshakeMessage.ht_hello_request) { + return Collections.emptyList(); + } + + for (HandshakeState handshakeState : upcomingStates) { + if (handshakeState.handshakeType == handshakeType) { + // It's the expected next handshake type. + return ignoredOptional; + } + + if (handshakeState.isOptional) { + ignoredOptional.add(handshakeState.handshakeType); + continue; + } else { + for (HandshakeState alternative : alternatives) { + if (alternative.handshakeType == handshakeType) { + return ignoredOptional; + } + + if (alternative.isOptional) { + continue; + } else { + throw new SSLProtocolException(exceptionMsg); + } + } + } + throw new SSLProtocolException(exceptionMsg); + } + + // Not an expected Handshake message. + throw new SSLProtocolException( + "Handshake message sequence violation, " + handshakeType); + } + + void update(HandshakeMessage handshakeMessage, + boolean isAbbreviated) throws SSLProtocolException { + + byte handshakeType = (byte)handshakeMessage.messageType(); + String exceptionMsg = + "Handshake message sequence violation, " + handshakeType; + + if (debugIsOn) { + System.out.println( + "update handshake state: " + toString(handshakeType)); + } + + boolean hasPresentState = false; + switch (handshakeType) { + case HandshakeMessage.ht_hello_request: + // + // State machine: + // PRESENT: START + // TO : ClientHello + // + + // No old state to update. + + // Add the upcoming states. + if (!upcomingStates.isEmpty()) { + // A ClientHello message should be followed. + upcomingStates.add(HS_CLIENT_HELLO); + + } // Otherwise, ignore this HelloRequest message. + + break; + + case HandshakeMessage.ht_client_hello: + // + // State machine: + // PRESENT: START + // HS_CLIENT_HELLO + // TO : HS_SERVER_HELLO + // + + // Check and update the present state. + if (!upcomingStates.isEmpty()) { + // The current state should be HS_CLIENT_HELLO. + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState != HS_CLIENT_HELLO) { + throw new SSLProtocolException(exceptionMsg); + } + } + + // Add the upcoming states. + ClientHello clientHello = (ClientHello)handshakeMessage; + upcomingStates.add(HS_SERVER_HELLO); + + break; + + case HandshakeMessage.ht_server_hello: + // + // State machine: + // PRESENT: HS_SERVER_HELLO + // TO : + // Full handshake state stacks + // (ServerHello Flight) + // HS_SERVER_SUPPLEMENTAL_DATA [optional] + // --> HS_SERVER_CERTIFICATE [optional] + // --> HS_CERTIFICATE_STATUS [optional] + // --> HS_SERVER_KEY_EXCHANGE [optional] + // --> HS_CERTIFICATE_REQUEST [optional] + // --> HS_SERVER_HELLO_DONE + // (Client ClientKeyExchange Flight) + // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional] + // --> HS_CLIENT_CERTIFICATE or + // HS_CERTIFICATE_URL + // --> HS_CLIENT_KEY_EXCHANGE + // --> HS_CERTIFICATE_VERIFY [optional] + // --> HS_CLIENT_CHANGE_CIPHER_SPEC + // --> HS_CLIENT_FINISHED + // (Server Finished Flight) + // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional] + // + // Abbreviated handshake state stacks + // (Server Finished Flight) + // HS_NEW_SESSION_TICKET + // --> HS_SERVER_CHANGE_CIPHER_SPEC + // --> HS_SERVER_FINISHED + // (Client Finished Flight) + // --> HS_CLIENT_CHANGE_CIPHER_SPEC + // --> HS_CLIENT_FINISHED + // + // Note that this state may have an alternative option. + + // Check and update the present state. + if (!upcomingStates.isEmpty()) { + // The current state should be HS_SERVER_HELLO + HandshakeState handshakeState = upcomingStates.pop(); + HandshakeState alternative = null; + if (!alternatives.isEmpty()) { + alternative = alternatives.pop(); + } + + if ((handshakeState != HS_SERVER_HELLO) && + (alternative != HS_SERVER_HELLO)) { + throw new SSLProtocolException(exceptionMsg); + } + } else { + // No present state. + throw new SSLProtocolException(exceptionMsg); + } + + // Add the upcoming states. + ServerHello serverHello = (ServerHello)handshakeMessage; + HelloExtensions hes = serverHello.extensions; + + + // Not support SessionTicket extension yet. + // + // boolean hasSessionTicketExt = + // (hes.get(HandshakeMessage.ht_new_session_ticket) != null); + + if (isAbbreviated) { + // Not support SessionTicket extension yet. + // + // // Mandatory NewSessionTicket message + // if (hasSessionTicketExt) { + // upcomingStates.add(HS_NEW_SESSION_TICKET); + // } + + // Mandatory server ChangeCipherSpec and Finished messages + upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_SERVER_FINISHED); + + // Mandatory client ChangeCipherSpec and Finished messages + upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_CLIENT_FINISHED); + } else { + // Not support SupplementalData extension yet. + // + // boolean hasSupplementalDataExt = + // (hes.get(HandshakeMessage.ht_supplemental_data) != null); + + // Not support CertificateURL extension yet. + // + // boolean hasCertificateUrlExt = + // (hes.get(ExtensionType EXT_CLIENT_CERTIFICATE_URL) + // != null); + + // Not support SupplementalData extension yet. + // + // // Optional SupplementalData message + // if (hasSupplementalDataExt) { + // upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA); + // } + + // Need server Certificate message or not? + KeyExchange keyExchange = serverHello.cipherSuite.keyExchange; + if ((keyExchange != K_KRB5) && + (keyExchange != K_KRB5_EXPORT) && + (keyExchange != K_DH_ANON) && + (keyExchange != K_ECDH_ANON)) { + // Mandatory Certificate message + upcomingStates.add(HS_SERVER_CERTIFICATE); + } + + // Need ServerKeyExchange message or not? + if ((keyExchange == K_RSA_EXPORT) || + (keyExchange == K_DHE_RSA) || + (keyExchange == K_DHE_DSS) || + (keyExchange == K_DH_ANON) || + (keyExchange == K_ECDHE_RSA) || + (keyExchange == K_ECDHE_ECDSA) || + (keyExchange == K_ECDH_ANON)) { + // Optional ServerKeyExchange message + upcomingStates.add(HS_SERVER_KEY_EXCHANGE); + } + + // Optional CertificateRequest message + upcomingStates.add(HS_CERTIFICATE_REQUEST); + + // Mandatory ServerHelloDone message + upcomingStates.add(HS_SERVER_HELLO_DONE); + + // Not support SupplementalData extension yet. + // + // // Optional SupplementalData message + // if (hasSupplementalDataExt) { + // upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA); + // } + + // Optional client Certificate message + upcomingStates.add(HS_CLIENT_CERTIFICATE); + + // Not support CertificateURL extension yet. + // + // // Alternative CertificateURL message, optional too. + // // + // // Please put CertificateURL rather than Certificate + // // message in the alternatives list. So that we can + // // simplify the process of this alternative pair later. + // if (hasCertificateUrlExt) { + // alternatives.add(HS_CERTIFICATE_URL); + // } + + // Mandatory ClientKeyExchange message + upcomingStates.add(HS_CLIENT_KEY_EXCHANGE); + + // Optional CertificateVerify message + upcomingStates.add(HS_CERTIFICATE_VERIFY); + + // Mandatory client ChangeCipherSpec and Finished messages + upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_CLIENT_FINISHED); + + // Not support SessionTicket extension yet. + // + // // Mandatory NewSessionTicket message + // if (hasSessionTicketExt) { + // upcomingStates.add(HS_NEW_SESSION_TICKET); + // } + + // Mandatory server ChangeCipherSpec and Finished messages + upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_SERVER_FINISHED); + } + + break; + + case HandshakeMessage.ht_certificate: + // + // State machine: + // PRESENT: HS_CERTIFICATE_URL or + // HS_CLIENT_CERTIFICATE + // TO : HS_CLIENT_KEY_EXCHANGE + // + // Or + // + // PRESENT: HS_SERVER_CERTIFICATE + // TO : HS_CERTIFICATE_STATUS [optional] + // HS_SERVER_KEY_EXCHANGE [optional] + // HS_CERTIFICATE_REQUEST [optional] + // HS_SERVER_HELLO_DONE + // + // Note that this state may have an alternative option. + + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState.handshakeType == handshakeType) { + hasPresentState = true; + + // The current state should be HS_CLIENT_CERTIFICATE or + // HS_SERVER_CERTIFICATE. + // + // Note that we won't put HS_CLIENT_CERTIFICATE into + // the alternative list. + if ((handshakeState != HS_CLIENT_CERTIFICATE) && + (handshakeState != HS_SERVER_CERTIFICATE)) { + throw new SSLProtocolException(exceptionMsg); + } + + // Is it an expected client Certificate message? + boolean isClientMessage = false; + if (!upcomingStates.isEmpty()) { + // If the next expected message is ClientKeyExchange, + // this one should be an expected client Certificate + // message. + HandshakeState nextState = upcomingStates.getFirst(); + if (nextState == HS_CLIENT_KEY_EXCHANGE) { + isClientMessage = true; + } + } + + if (isClientMessage) { + if (handshakeState != HS_CLIENT_CERTIFICATE) { + throw new SSLProtocolException(exceptionMsg); + } + + // Not support CertificateURL extension yet. + /******************************************* + // clear up the alternatives list + if (!alternatives.isEmpty()) { + HandshakeState alternative = alternatives.pop(); + + if (alternative != HS_CERTIFICATE_URL) { + throw new SSLProtocolException(exceptionMsg); + } + } + ********************************************/ + } else { + if ((handshakeState != HS_SERVER_CERTIFICATE)) { + throw new SSLProtocolException(exceptionMsg); + } + } + + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + + break; + + default: + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState.handshakeType == handshakeType) { + hasPresentState = true; + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + } + + if (debugIsOn) { + for (HandshakeState handshakeState : upcomingStates) { + System.out.println( + "upcoming handshake states: " + handshakeState); + } + for (HandshakeState handshakeState : alternatives) { + System.out.println( + "upcoming handshake alternative state: " + handshakeState); + } + } + } + + void changeCipherSpec(boolean isInput, + boolean isClient) throws SSLProtocolException { + + if (debugIsOn) { + System.out.println( + "update handshake state: change_cipher_spec"); + } + + String exceptionMsg = "ChangeCipherSpec message sequence violation"; + + HandshakeState expectedState; + if ((isClient && isInput) || (!isClient && !isInput)) { + expectedState = HS_SERVER_CHANGE_CIPHER_SPEC; + } else { + expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC; + } + + boolean hasPresentState = false; + + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState == expectedState) { + hasPresentState = true; + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + + if (debugIsOn) { + for (HandshakeState handshakeState : upcomingStates) { + System.out.println( + "upcoming handshake states: " + handshakeState); + } + for (HandshakeState handshakeState : alternatives) { + System.out.println( + "upcoming handshake alternative state: " + handshakeState); + } + } + } + + private static String toString(byte handshakeType) { + String s = handshakeTypes.get(handshakeType); + if (s == null) { + s = "unknown"; + } + return (s + "[" + handshakeType + "]"); + } +} diff --git a/src/share/classes/sun/security/ssl/Handshaker.java b/src/share/classes/sun/security/ssl/Handshaker.java index 8e570c404d13db0f4b031b58f0db567a5904033a..beee9fdb97b2e68c89b16c98732010de9a63c2be 100644 --- a/src/share/classes/sun/security/ssl/Handshaker.java +++ b/src/share/classes/sun/security/ssl/Handshaker.java @@ -118,10 +118,14 @@ abstract class Handshaker { HandshakeHash handshakeHash; HandshakeInStream input; HandshakeOutStream output; - int state; SSLContextImpl sslContext; RandomCookie clnt_random, svr_random; SSLSessionImpl session; + HandshakeStateManager handshakeState; + boolean clientHelloDelivered; + boolean serverHelloRequested; + boolean handshakeActivated; + boolean handshakeFinished; // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL CipherSuite cipherSuite; @@ -135,10 +139,6 @@ abstract class Handshaker { // True if it's OK to start a new SSL session boolean enableNewSession; - // True if session keys have been calculated and the caller may receive - // and process a ChangeCipherSpec message - private boolean sessKeysCalculated; - // Whether local cipher suites preference should be honored during // handshaking? // @@ -277,9 +277,13 @@ abstract class Handshaker { this.secureRenegotiation = secureRenegotiation; this.clientVerifyData = clientVerifyData; this.serverVerifyData = serverVerifyData; - enableNewSession = true; - invalidated = false; - sessKeysCalculated = false; + this.enableNewSession = true; + this.invalidated = false; + this.handshakeState = new HandshakeStateManager(); + this.clientHelloDelivered = false; + this.serverHelloRequested = false; + this.handshakeActivated = false; + this.handshakeFinished = false; setCipherSuite(CipherSuite.C_NULL); setEnabledProtocols(enabledProtocols); @@ -289,22 +293,6 @@ abstract class Handshaker { } else { // engine != null algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } - - - // - // In addition to the connection state machine, controlling - // how the connection deals with the different sorts of records - // that get sent (notably handshake transitions!), there's - // also a handshaking state machine that controls message - // sequencing. - // - // It's a convenient artifact of the protocol that this can, - // with only a couple of minor exceptions, be driven by the - // type constant for the last message seen: except for the - // client's cert verify, those constants are in a convenient - // order to drastically simplify state machine checking. - // - state = -2; // initialized but not activated } /* @@ -386,14 +374,6 @@ abstract class Handshaker { } } - final boolean receivedChangeCipherSpec() { - if (conn != null) { - return conn.receivedChangeCipherSpec(); - } else { - return engine.receivedChangeCipherSpec(); - } - } - String getEndpointIdentificationAlgorithmSE() { SSLParameters paras; if (conn != null) { @@ -573,8 +553,7 @@ abstract class Handshaker { engine.outputRecord.setHelloVersion(helloVersion); } - // move state to activated - state = -1; + handshakeActivated = true; } /** @@ -919,10 +898,9 @@ abstract class Handshaker { * this freshly created session can become the current one. */ boolean isDone() { - return state == HandshakeMessage.ht_finished; + return started() && handshakeState.isEmpty() && handshakeFinished; } - /* * Returns the session which was created through this * handshake sequence ... should be called after isDone() @@ -1028,6 +1006,13 @@ abstract class Handshaker { return; } + // Set the flags in the message receiving side. + if (messageType == HandshakeMessage.ht_client_hello) { + clientHelloDelivered = true; + } else if (messageType == HandshakeMessage.ht_hello_request) { + serverHelloRequested = true; + } + /* * Process the message. We require * that processMessage() consumes the entire message. In @@ -1062,15 +1047,14 @@ abstract class Handshaker { * In activated state, the handshaker may not send any messages out. */ boolean activated() { - return state >= -1; + return handshakeActivated; } /** * Returns true iff the handshaker has sent any messages. */ boolean started() { - return state >= 0; // 0: HandshakeMessage.ht_hello_request - // 1: HandshakeMessage.ht_client_hello + return (serverHelloRequested || clientHelloDelivered); } @@ -1080,11 +1064,13 @@ abstract class Handshaker { * the subclass returns. NOP if handshaking's already started. */ void kickstart() throws IOException { - if (state >= 0) { + if ((isClient && clientHelloDelivered) || + (!isClient && serverHelloRequested)) { return; } HandshakeMessage m = getKickstartMessage(); + handshakeState.update(m, resumingSession); if (debug != null && Debug.isOn("handshake")) { m.print(System.out); @@ -1092,7 +1078,14 @@ abstract class Handshaker { m.write(output); output.flush(); - state = m.messageType(); + // Set the flags in the message delivering side. + int handshakeType = m.messageType(); + if (handshakeType == HandshakeMessage.ht_hello_request) { + serverHelloRequested = true; + } else { // HandshakeMessage.ht_client_hello + clientHelloDelivered = true; + } + } /** @@ -1124,16 +1117,6 @@ abstract class Handshaker { output.flush(); // i.e. handshake data - /* - * The write cipher state is protected by the connection write lock - * so we must grab it while making the change. We also - * make sure no writes occur between sending the ChangeCipherSpec - * message, installing the new cipher state, and sending the - * Finished message. - * - * We already hold SSLEngine/SSLSocket "this" by virtue - * of this being called from the readRecord code. - */ OutputRecord r; if (conn != null) { r = new OutputRecord(Record.ct_change_cipher_spec); @@ -1144,14 +1127,27 @@ abstract class Handshaker { r.setVersion(protocolVersion); r.write(1); // single byte of data + /* + * The write cipher state is protected by the connection write lock + * so we must grab it while making the change. We also + * make sure no writes occur between sending the ChangeCipherSpec + * message, installing the new cipher state, and sending the + * Finished message. + * + * We already hold SSLEngine/SSLSocket "this" by virtue + * of this being called from the readRecord code. + */ if (conn != null) { conn.writeLock.lock(); try { + handshakeState.changeCipherSpec(false, isClient); conn.writeRecord(r); conn.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } + + handshakeState.update(mesg, resumingSession); mesg.write(output); output.flush(); } finally { @@ -1159,19 +1155,28 @@ abstract class Handshaker { } } else { synchronized (engine.writeLock) { + handshakeState.changeCipherSpec(false, isClient); engine.writeRecord((EngineOutputRecord)r); engine.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } - mesg.write(output); + handshakeState.update(mesg, resumingSession); + mesg.write(output); if (lastMessage) { output.setFinishedMsg(); } output.flush(); } } + if (lastMessage) { + handshakeFinished = true; + } + } + + void receiveChangeCipherSpec() throws IOException { + handshakeState.changeCipherSpec(true, isClient); } /* @@ -1353,10 +1358,6 @@ abstract class Handshaker { throw new ProviderException(e); } - // Mark a flag that allows outside entities (like SSLSocket/SSLEngine) - // determine if a ChangeCipherSpec message could be processed. - sessKeysCalculated = true; - // // Dump the connection keys as they're generated. // @@ -1411,15 +1412,6 @@ abstract class Handshaker { } } - /** - * Return whether or not the Handshaker has derived session keys for - * this handshake. This is used for determining readiness to process - * an incoming ChangeCipherSpec message. - */ - boolean sessionKeysCalculated() { - return sessKeysCalculated; - } - private static void printHex(HexDumpEncoder dump, byte[] bytes) { if (bytes == null) { System.out.println("(key bytes not available)"); diff --git a/src/share/classes/sun/security/ssl/SSLEngineImpl.java b/src/share/classes/sun/security/ssl/SSLEngineImpl.java index e021d8ee2c27968ad02c5f2376cdb33e10722d9b..376d45d5ffc7026e6bba14b1091586c24a1ee245 100644 --- a/src/share/classes/sun/security/ssl/SSLEngineImpl.java +++ b/src/share/classes/sun/security/ssl/SSLEngineImpl.java @@ -211,11 +211,6 @@ final public class SSLEngineImpl extends SSLEngine { static final byte clauth_requested = 1; static final byte clauth_required = 2; - /* - * Flag indicating that the engine has received a ChangeCipherSpec message. - */ - private boolean receivedCCS; - /* * Flag indicating if the next record we receive MUST be a Finished * message. Temporarily set during the handshake to ensure that @@ -377,7 +372,6 @@ final public class SSLEngineImpl extends SSLEngine { */ roleIsServer = true; connectionState = cs_START; - receivedCCS = false; // default server name indication serverNames = @@ -525,7 +519,6 @@ final public class SSLEngineImpl extends SSLEngine { return HandshakeStatus.NEED_UNWRAP; } // else not handshaking } - return HandshakeStatus.NOT_HANDSHAKING; } } @@ -587,14 +580,6 @@ final public class SSLEngineImpl extends SSLEngine { * Synchronized on "this" from readRecord. */ private void changeReadCiphers() throws SSLException { - if (connectionState != cs_HANDSHAKE - && connectionState != cs_RENEGOTIATE) { - throw new SSLProtocolException( - "State error, change cipher specs"); - } - - // ... create decompressor - CipherBox oldCipher = readCipher; try { @@ -780,6 +765,10 @@ final public class SSLEngineImpl extends SSLEngine { synchronized (unwrapLock) { return readNetRecord(ea); } + } catch (SSLProtocolException spe) { + // may be an unexpected handshake message + fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe); + return null; // make compiler happy } catch (Exception e) { /* * Don't reset position so it looks like we didn't @@ -1004,6 +993,7 @@ final public class SSLEngineImpl extends SSLEngine { * session (new keys exchanged) with just this connection * in it. */ + initHandshaker(); if (!handshaker.activated()) { // prior to handshaking, activate the handshake @@ -1027,7 +1017,6 @@ final public class SSLEngineImpl extends SSLEngine { if (handshaker.invalidated) { handshaker = null; - receivedCCS = false; // if state is cs_RENEGOTIATE, revert it to cs_DATA if (connectionState == cs_RENEGOTIATE) { connectionState = cs_DATA; @@ -1046,8 +1035,6 @@ final public class SSLEngineImpl extends SSLEngine { } handshaker = null; connectionState = cs_DATA; - receivedCCS = false; - // No handshakeListeners here. That's a // SSLSocket thing. } else if (handshaker.taskOutstanding()) { @@ -1085,14 +1072,11 @@ final public class SSLEngineImpl extends SSLEngine { case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE - && connectionState != cs_RENEGOTIATE) - || !handshaker.sessionKeysCalculated() - || receivedCCS) { + && connectionState != cs_RENEGOTIATE)) { // For the CCS message arriving in the wrong state fatal(Alerts.alert_unexpected_message, "illegal change cipher spec msg, conn state = " - + connectionState + ", handshake state = " - + handshaker.state); + + connectionState); } else if (inputRecord.available() != 1 || inputRecord.read() != 1) { // For structural/content issues with the CCS @@ -1100,11 +1084,6 @@ final public class SSLEngineImpl extends SSLEngine { "Malformed change cipher spec msg"); } - // Once we've received CCS, update the flag. - // If the remote endpoint sends it again in this handshake - // we won't process it. - receivedCCS = true; - // // The first message after a change_cipher_spec // record MUST be a "Finished" handshake record, @@ -1112,6 +1091,7 @@ final public class SSLEngineImpl extends SSLEngine { // to be checked by a minor tweak to the state // machine. // + handshaker.receiveChangeCipherSpec(); changeReadCiphers(); // next message MUST be a finished message expectingFinished = true; @@ -1151,7 +1131,6 @@ final public class SSLEngineImpl extends SSLEngine { } } // synchronized (this) } - return hsStatus; } @@ -1185,6 +1164,10 @@ final public class SSLEngineImpl extends SSLEngine { synchronized (wrapLock) { return writeAppRecord(ea); } + } catch (SSLProtocolException spe) { + // may be an unexpected handshake message + fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe); + return null; // make compiler happy } catch (Exception e) { ea.resetPos(); @@ -1212,7 +1195,6 @@ final public class SSLEngineImpl extends SSLEngine { * See if the handshaker needs to report back some SSLException. */ checkTaskThrown(); - /* * short circuit if we're closed/closing. */ @@ -1234,7 +1216,6 @@ final public class SSLEngineImpl extends SSLEngine { * without trying to wrap anything. */ hsStatus = getHSStatus(null); - if (hsStatus == HandshakeStatus.NEED_UNWRAP) { return new SSLEngineResult(Status.OK, hsStatus, 0, 0); } @@ -2140,14 +2121,6 @@ final public class SSLEngineImpl extends SSLEngine { } } - /* - * Returns a boolean indicating whether the ChangeCipherSpec message - * has been received for this handshake. - */ - boolean receivedChangeCipherSpec() { - return receivedCCS; - } - /** * Returns a printable representation of this end of the connection. */ diff --git a/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/share/classes/sun/security/ssl/SSLSocketImpl.java index d7322dd5ddb89b9d2ec089422ba50ccb766c4e1d..d96dd5c3713f9ea83380bb721d3dcff23aa7054e 100644 --- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -174,12 +174,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { */ private volatile int connectionState; - /* - * Flag indicating that the engine's handshaker has done the necessary - * steps so the engine may process a ChangeCipherSpec message. - */ - private boolean receivedCCS; - /* * Flag indicating if the next record we receive MUST be a Finished * message. Temporarily set during the handshake to ensure that @@ -610,7 +604,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { */ roleIsServer = isServer; connectionState = cs_START; - receivedCCS = false; /* * default read and write side cipher and MAC support @@ -940,7 +933,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { readRecord(r, true); } - /* * Clear the pipeline of records from the peer, optionally returning * application data. Caller is responsible for knowing that it's @@ -1074,7 +1066,8 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { if (handshaker.invalidated) { handshaker = null; - receivedCCS = false; + inrec.setHandshakeHash(null); + // if state is cs_RENEGOTIATE, revert it to cs_DATA if (connectionState == cs_RENEGOTIATE) { connectionState = cs_DATA; @@ -1090,7 +1083,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { handshakeSession = null; handshaker = null; connectionState = cs_DATA; - receivedCCS = false; // // Tell folk about handshake completion, but do @@ -1137,25 +1129,17 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE - && connectionState != cs_RENEGOTIATE) - || !handshaker.sessionKeysCalculated() - || receivedCCS) { + && connectionState != cs_RENEGOTIATE)) { // For the CCS message arriving in the wrong state fatal(Alerts.alert_unexpected_message, "illegal change cipher spec msg, conn state = " - + connectionState + ", handshake state = " - + handshaker.state); + + connectionState); } else if (r.available() != 1 || r.read() != 1) { // For structural/content issues with the CCS fatal(Alerts.alert_unexpected_message, "Malformed change cipher spec msg"); } - // Once we've received CCS, update the flag. - // If the remote endpoint sends it again in this handshake - // we won't process it. - receivedCCS = true; - // // The first message after a change_cipher_spec // record MUST be a "Finished" handshake record, @@ -1163,6 +1147,7 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { // to be checked by a minor tweak to the state // machine. // + handshaker.receiveChangeCipherSpec(); changeReadCiphers(); // next message MUST be a finished message expectingFinished = true; @@ -1361,13 +1346,10 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { kickstartHandshake(); /* - * All initial handshaking goes through this - * InputRecord until we have a valid SSL connection. - * Once initial handshaking is finished, AppInputStream's - * InputRecord can handle any future renegotiation. + * All initial handshaking goes through this operation + * until we have a valid SSL connection. * - * Keep this local so that it goes out of scope and is - * eventually GC'd. + * Handle handshake messages only, need no application data. */ if (inrec == null) { inrec = new InputRecord(); @@ -2666,14 +2648,6 @@ final public class SSLSocketImpl extends BaseSSLSocketImpl { } } - /* - * Returns a boolean indicating whether the ChangeCipherSpec message - * has been received for this handshake. - */ - boolean receivedChangeCipherSpec() { - return receivedCCS; - } - // // We allocate a separate thread to deliver handshake completion // events. This ensures that the notifications don't block the diff --git a/src/share/classes/sun/security/ssl/ServerHandshaker.java b/src/share/classes/sun/security/ssl/ServerHandshaker.java index 73c0284af9fda8cf2f665bd00c3d82f9a1168bdc..bc2431e93519c1305f6ffb25ee24735198507abc 100644 --- a/src/share/classes/sun/security/ssl/ServerHandshaker.java +++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java @@ -207,21 +207,14 @@ final class ServerHandshaker extends Handshaker { @Override void processMessage(byte type, int message_len) throws IOException { - // - // In SSLv3 and TLS, messages follow strictly increasing - // numerical order _except_ for one annoying special case. - // - if ((state >= type) - && (state != HandshakeMessage.ht_client_key_exchange - && type != HandshakeMessage.ht_certificate_verify)) { - throw new SSLProtocolException( - "Handshake message sequence violation, state = " + state - + ", type = " + type); - } + + // check the handshake state + handshakeState.check(type); switch (type) { case HandshakeMessage.ht_client_hello: ClientHello ch = new ClientHello(input, message_len); + handshakeState.update(ch, resumingSession); /* * send it off for processing. */ @@ -234,7 +227,9 @@ final class ServerHandshaker extends Handshaker { "client sent unsolicited cert chain"); // NOTREACHED } - this.clientCertificate(new CertificateMsg(input)); + CertificateMsg certificateMsg = new CertificateMsg(input); + handshakeState.update(certificateMsg, resumingSession); + this.clientCertificate(certificateMsg); break; case HandshakeMessage.ht_client_key_exchange: @@ -252,17 +247,20 @@ final class ServerHandshaker extends Handshaker { protocolVersion, clientRequestedVersion, sslContext.getSecureRandom(), input, message_len, privateKey); + handshakeState.update(pms, resumingSession); preMasterSecret = this.clientKeyExchange(pms); break; case K_KRB5: case K_KRB5_EXPORT: - preMasterSecret = this.clientKeyExchange( + KerberosClientKeyExchange kke = new KerberosClientKeyExchange(protocolVersion, clientRequestedVersion, sslContext.getSecureRandom(), input, this.getAccSE(), - serviceCreds)); + serviceCreds); + handshakeState.update(kke, resumingSession); + preMasterSecret = this.clientKeyExchange(kke); break; case K_DHE_RSA: case K_DHE_DSS: @@ -273,16 +271,19 @@ final class ServerHandshaker extends Handshaker { * protocol difference in these five flavors is in how * the ServerKeyExchange message was constructed! */ - preMasterSecret = this.clientKeyExchange( - new DHClientKeyExchange(input)); + DHClientKeyExchange dhcke = new DHClientKeyExchange(input); + handshakeState.update(dhcke, resumingSession); + preMasterSecret = this.clientKeyExchange(dhcke); break; case K_ECDH_RSA: case K_ECDH_ECDSA: case K_ECDHE_RSA: case K_ECDHE_ECDSA: case K_ECDH_ANON: - preMasterSecret = this.clientKeyExchange - (new ECDHClientKeyExchange(input)); + ECDHClientKeyExchange ecdhcke = + new ECDHClientKeyExchange(input); + handshakeState.update(ecdhcke, resumingSession); + preMasterSecret = this.clientKeyExchange(ecdhcke); break; default: throw new SSLProtocolException @@ -302,38 +303,24 @@ final class ServerHandshaker extends Handshaker { break; case HandshakeMessage.ht_certificate_verify: - this.clientCertificateVerify(new CertificateVerify(input, - getLocalSupportedSignAlgs(), protocolVersion)); + CertificateVerify cvm = + new CertificateVerify(input, + getLocalSupportedSignAlgs(), protocolVersion); + handshakeState.update(cvm, resumingSession); + this.clientCertificateVerify(cvm); break; case HandshakeMessage.ht_finished: - // A ChangeCipherSpec record must have been received prior to - // reception of the Finished message (RFC 5246, 7.4.9). - if (!receivedChangeCipherSpec()) { - fatalSE(Alerts.alert_handshake_failure, - "Received Finished message before ChangeCipherSpec"); - } - - this.clientFinished( - new Finished(protocolVersion, input, cipherSuite)); + Finished cfm = + new Finished(protocolVersion, input, cipherSuite); + handshakeState.update(cfm, resumingSession); + this.clientFinished(cfm); break; default: throw new SSLProtocolException( "Illegal server handshake msg, " + type); } - - // - // Move state machine forward if the message handling - // code didn't already do so - // - if (state < type) { - if(type == HandshakeMessage.ht_certificate_verify) { - state = type + 2; // an annoying special case - } else { - state = type; - } - } } @@ -364,7 +351,7 @@ final class ServerHandshaker extends Handshaker { // // This will not have any impact on server initiated renegotiation. if (rejectClientInitiatedRenego && !isInitialHandshake && - state != HandshakeMessage.ht_hello_request) { + !serverHelloRequested) { fatalSE(Alerts.alert_handshake_failure, "Client initiated renegotiation is not allowed"); } @@ -876,6 +863,7 @@ final class ServerHandshaker extends Handshaker { System.out.println("Cipher suite: " + session.getSuite()); } m1.write(output); + handshakeState.update(m1, resumingSession); // // If we are resuming a session, we finish writing handshake @@ -915,6 +903,7 @@ final class ServerHandshaker extends Handshaker { m2.print(System.out); } m2.write(output); + handshakeState.update(m2, resumingSession); // XXX has some side effects with OS TCP buffering, // leave it out for now @@ -1010,6 +999,7 @@ final class ServerHandshaker extends Handshaker { m3.print(System.out); } m3.write(output); + handshakeState.update(m3, resumingSession); } // @@ -1059,6 +1049,7 @@ final class ServerHandshaker extends Handshaker { m4.print(System.out); } m4.write(output); + handshakeState.update(m4, resumingSession); } /* @@ -1070,6 +1061,7 @@ final class ServerHandshaker extends Handshaker { m5.print(System.out); } m5.write(output); + handshakeState.update(m5, resumingSession); /* * Flush any buffered messages so the client will see them. @@ -1817,6 +1809,8 @@ final class ServerHandshaker extends Handshaker { if (!resumingSession) { input.digestNow(); sendChangeCipherAndFinish(true); + } else { + handshakeFinished = true; } /* @@ -1864,16 +1858,6 @@ final class ServerHandshaker extends Handshaker { if (secureRenegotiation) { serverVerifyData = mesg.getVerifyData(); } - - /* - * Update state machine so client MUST send 'finished' next - * The update should only take place if it is not in the fast - * handshake mode since the server has to wait for a finished - * message from the client. - */ - if (finishedTag) { - state = HandshakeMessage.ht_finished; - } } diff --git a/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/LengthCheckTest.java b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/LengthCheckTest.java index 38122d7480b91c7ff946dea577ea9a2db7019328..f4e71a60298d5ed266738963df41791d1d03081e 100644 --- a/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/LengthCheckTest.java +++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/ClientHandshaker/LengthCheckTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8044860 + * @bug 8044860 8074462 * @summary Vectors and fixed length fields should be verified * for allowed sizes. * @run main/othervm LengthCheckTest @@ -231,7 +231,7 @@ public class LengthCheckTest { // sent back to the server. if (gotException == false || !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, - TLS_ALERT_INTERNAL_ERROR)) { + TLS_ALERT_UNEXPECTED_MSG)) { throw new SSLException( "Client failed to throw Alert:fatal:internal_error"); } @@ -283,7 +283,7 @@ public class LengthCheckTest { // sent back to the client. if (gotException == false || !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, - TLS_ALERT_INTERNAL_ERROR)) { + TLS_ALERT_UNEXPECTED_MSG)) { throw new SSLException( "Server failed to throw Alert:fatal:internal_error"); }