8046656: Update protocol support
authorwetmore
Tue, 26 Aug 2014 17:09:05 -0700
changeset 28543 31afdc0e77af
parent 28542 d50a7783fe02
child 28544 a132e8e301e5
8046656: Update protocol support Reviewed-by: xuelei, wetmore, igerasim, mullan, asmotrak Contributed-by: jamil.nimeh@oracle.com
jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java	Wed Jan 21 12:49:53 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java	Tue Aug 26 17:09:05 2014 -0700
@@ -95,8 +95,6 @@
     Collection<SignatureAndHashAlgorithm> peerSupportedSignAlgs;
 
     /*
-
-    /*
      * List of active protocols
      *
      * Active protocols is a subset of enabled protocols, and will
@@ -114,10 +112,8 @@
     private CipherSuiteList    activeCipherSuites;
 
     // The server name indication and matchers
-    List<SNIServerName>         serverNames =
-                                    Collections.<SNIServerName>emptyList();
-    Collection<SNIMatcher>      sniMatchers =
-                                    Collections.<SNIMatcher>emptyList();
+    List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
+    Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList();
 
     private boolean             isClient;
     private boolean             needCertVerify;
@@ -139,12 +135,16 @@
     // current key exchange. Never null, initially K_NULL
     KeyExchange         keyExchange;
 
-    /* True if this session is being resumed (fast handshake) */
+    // True if this session is being resumed (fast handshake)
     boolean             resumingSession;
 
-    /* True if it's OK to start a new SSL session */
+    // 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?
     //
@@ -253,6 +253,7 @@
         this.serverVerifyData = serverVerifyData;
         enableNewSession = true;
         invalidated = false;
+        sessKeysCalculated = false;
 
         setCipherSuite(CipherSuite.C_NULL);
         setEnabledProtocols(enabledProtocols);
@@ -1081,7 +1082,6 @@
         calculateConnectionKeys(master);
     }
 
-
     /*
      * Calculate the master secret from its various components.  This is
      * used for key exchange by all cipher suites.
@@ -1226,6 +1226,10 @@
             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.
         //
@@ -1280,6 +1284,15 @@
         }
     }
 
+    /**
+     * 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)");
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Wed Jan 21 12:49:53 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Tue Aug 26 17:09:05 2014 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -212,6 +212,11 @@
     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
      * a change cipher spec message is followed by a finished message.
@@ -372,6 +377,7 @@
          */
         roleIsServer = true;
         connectionState = cs_START;
+        receivedCCS = false;
 
         // default server name indication
         serverNames =
@@ -1021,6 +1027,7 @@
 
                     if (handshaker.invalidated) {
                         handshaker = null;
+                        receivedCCS = false;
                         // if state is cs_RENEGOTIATE, revert it to cs_DATA
                         if (connectionState == cs_RENEGOTIATE) {
                             connectionState = cs_DATA;
@@ -1039,6 +1046,7 @@
                         }
                         handshaker = null;
                         connectionState = cs_DATA;
+                        receivedCCS = false;
 
                         // No handshakeListeners here.  That's a
                         // SSLSocket thing.
@@ -1078,13 +1086,25 @@
                 case Record.ct_change_cipher_spec:
                     if ((connectionState != cs_HANDSHAKE
                                 && connectionState != cs_RENEGOTIATE)
-                            || inputRecord.available() != 1
-                            || inputRecord.read() != 1) {
+                            || !handshaker.sessionKeysCalculated()
+                            || receivedCCS) {
+                        // For the CCS message arriving in the wrong state
                         fatal(Alerts.alert_unexpected_message,
-                            "illegal change cipher spec msg, state = "
-                            + connectionState);
+                                "illegal change cipher spec msg, conn state = "
+                                + connectionState + ", handshake state = "
+                                + handshaker.state);
+                    } else if (inputRecord.available() != 1
+                            || inputRecord.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,
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Wed Jan 21 12:49:53 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Tue Aug 26 17:09:05 2014 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2014, 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
@@ -172,6 +172,12 @@
     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
      * a change cipher spec message is followed by a finished message.
@@ -587,6 +593,7 @@
          */
         roleIsServer = isServer;
         connectionState = cs_START;
+        receivedCCS = false;
 
         /*
          * default read and write side cipher and MAC support
@@ -1045,6 +1052,7 @@
 
                     if (handshaker.invalidated) {
                         handshaker = null;
+                        receivedCCS = false;
                         // if state is cs_RENEGOTIATE, revert it to cs_DATA
                         if (connectionState == cs_RENEGOTIATE) {
                             connectionState = cs_DATA;
@@ -1060,6 +1068,7 @@
                         handshakeSession = null;
                         handshaker = null;
                         connectionState = cs_DATA;
+                        receivedCCS = false;
 
                         //
                         // Tell folk about handshake completion, but do
@@ -1107,13 +1116,24 @@
                 case Record.ct_change_cipher_spec:
                     if ((connectionState != cs_HANDSHAKE
                                 && connectionState != cs_RENEGOTIATE)
-                            || r.available() != 1
-                            || r.read() != 1) {
+                            || !handshaker.sessionKeysCalculated()
+                            || receivedCCS) {
+                        // For the CCS message arriving in the wrong state
                         fatal(Alerts.alert_unexpected_message,
-                            "illegal change cipher spec msg, state = "
-                            + connectionState);
+                                "illegal change cipher spec msg, conn state = "
+                                + connectionState + ", handshake state = "
+                                + handshaker.state);
+                    } 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,