jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java
changeset 7039 6464c8e62a18
parent 6856 533f4ad71f88
child 7043 5e2d1edeb2c7
--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Fri Oct 29 12:35:07 2010 +0200
+++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Sat Oct 30 18:39:17 2010 +0800
@@ -194,12 +194,14 @@
      */
     private byte                doClientAuth;
     private boolean             roleIsServer;
-    private CipherSuiteList     enabledCipherSuites;
     private boolean             enableSessionCreation = true;
     private String              host;
     private boolean             autoClose = true;
     private AccessControlContext acc;
 
+    // The cipher suites enabled for use on this connection.
+    private CipherSuiteList     enabledCipherSuites;
+
     // hostname identification algorithm, the hostname identification is
     // disabled by default.
     private String              identificationAlg = null;
@@ -341,11 +343,11 @@
     private AppOutputStream     output;
 
     /*
-     * The protocols we support are SSL Version 3.0) and
-     * TLS (version 3.1).
-     * In addition we support a pseudo protocol called
-     * SSLv2Hello which when set will result in an SSL v2 Hello
-     * being sent with SSLv3 or TLSv1 version info.
+     * The protocol versions enabled for use on this connection.
+     *
+     * Note: we support a pseudo protocol called SSLv2Hello which when
+     * set will result in an SSL v2 Hello being sent with SSL (version 3.0)
+     * or TLS (version 3.1, 3.2, etc.) version info.
      */
     private ProtocolList enabledProtocols;
 
@@ -541,7 +543,7 @@
         serverVerifyData = new byte[0];
 
         enabledCipherSuites = CipherSuiteList.getDefault();
-        enabledProtocols = ProtocolList.getDefault();
+        enabledProtocols = ProtocolList.getDefault(roleIsServer);
         inrec = null;
 
         // save the acc
@@ -764,6 +766,21 @@
         r.addMAC(writeMAC);
         r.encrypt(writeCipher);
         r.write(sockOutput);
+
+        /*
+         * Check the sequence number state
+         *
+         * Note that in order to maintain the connection I/O
+         * properly, we check the sequence number after the last
+         * record writing process. As we request renegotiation
+         * or close the connection for wrapped sequence number
+         * when there is enough sequence number space left to
+         * handle a few more records, so the sequence number
+         * of the last record cannot be wrapped.
+         */
+        if (connectionState < cs_ERROR) {
+            checkSequenceNumber(writeMAC, r.contentType());
+        }
     }
 
 
@@ -883,6 +900,7 @@
                 }
             }
 
+
             // if (!r.decompress(c))
             //     fatal(Alerts.alert_decompression_failure,
             //         "decompression failure");
@@ -905,6 +923,15 @@
                      * in it.
                      */
                     initHandshaker();
+                    if (!handshaker.activated()) {
+                        // prior to handshaking, activate the handshake
+                        if (connectionState == cs_RENEGOTIATE) {
+                            // don't use SSLv2Hello when renegotiating
+                            handshaker.activate(protocolVersion);
+                        } else {
+                            handshaker.activate(null);
+                        }
+                    }
 
                     /*
                      * process the handshake record ... may contain just
@@ -949,9 +976,8 @@
 
                     if (needAppData || connectionState != cs_DATA) {
                         continue;
-                    } else {
-                        return;
                     }
+                    break;
 
                 case Record.ct_application_data:
                     // Pass this right back up to the application.
@@ -971,7 +997,7 @@
                     }
 
                     r.setAppDataValid(true);
-                    return;
+                    break;
 
                 case Record.ct_alert:
                     recvAlert(r);
@@ -1010,6 +1036,23 @@
                     }
                     continue;
               } // switch
+
+              /*
+               * Check the sequence number state
+               *
+               * Note that in order to maintain the connection I/O
+               * properly, we check the sequence number after the last
+               * record reading process. As we request renegotiation
+               * or close the connection for wrapped sequence number
+               * when there is enough sequence number space left to
+               * handle a few more records, so the sequence number
+               * of the last record cannot be wrapped.
+               */
+              if (connectionState < cs_ERROR) {
+                  checkSequenceNumber(readMAC, r.contentType());
+              }
+
+              return;
             } // synchronized (this)
         }
 
@@ -1021,6 +1064,61 @@
       }  // synchronized (readLock)
     }
 
+    /**
+     * Check the sequence number state
+     *
+     * RFC 4346 states that, "Sequence numbers are of type uint64 and
+     * may not exceed 2^64-1.  Sequence numbers do not wrap. If a TLS
+     * implementation would need to wrap a sequence number, it must
+     * renegotiate instead."
+     */
+    private void checkSequenceNumber(MAC mac, byte type)
+            throws IOException {
+
+        /*
+         * Don't bother to check the sequence number for error or
+         * closed connections, or NULL MAC.
+         */
+        if (connectionState >= cs_ERROR || mac == MAC.NULL) {
+            return;
+        }
+
+        /*
+         * Conservatively, close the connection immediately when the
+         * sequence number is close to overflow
+         */
+        if (mac.seqNumOverflow()) {
+            /*
+             * TLS protocols do not define a error alert for sequence
+             * number overflow. We use handshake_failure error alert
+             * for handshaking and bad_record_mac for other records.
+             */
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(threadName() +
+                    ", sequence number extremely close to overflow " +
+                    "(2^64-1 packets). Closing connection.");
+
+            }
+
+            fatal(Alerts.alert_handshake_failure, "sequence number overflow");
+        }
+
+        /*
+         * Ask for renegotiation when need to renew sequence number.
+         *
+         * Don't bother to kickstart the renegotiation when the local is
+         * asking for it.
+         */
+        if ((type != Record.ct_handshake) && mac.seqNumIsHuge()) {
+            if (debug != null && Debug.isOn("ssl")) {
+                System.out.println(threadName() + ", request renegotiation " +
+                        "to avoid sequence number overflow");
+            }
+
+            startHandshake();
+        }
+    }
+
     //
     // HANDSHAKE RELATED CODE
     //
@@ -1033,28 +1131,10 @@
     }
 
     /**
-     * Initialize and get the server handshaker. Used by SSLServerSocketImpl
-     * for the ciphersuite availability test *only*.
+     * Return the AppOutputStream. For use by Handshaker only.
      */
-    ServerHandshaker getServerHandshaker() throws SSLException {
-        initHandshaker();
-
-         // The connection state would have been set to cs_HANDSHAKE during the
-         // handshaking initializing, however the caller may not have the
-         // the low level connection's established, which is not consistent with
-         // the HANDSHAKE state. As if it is unconnected, we need to reset the
-         // connection state to cs_START.
-         if (!isConnected()) {
-             connectionState = cs_START;
-         }
-
-         // Make sure that we get a ServerHandshaker.
-         // This should never happen.
-         if (!(handshaker instanceof ServerHandshaker)) {
-             throw new SSLProtocolException("unexpected handshaker instance");
-         }
-
-        return (ServerHandshaker)handshaker;
+    AppOutputStream getAppOutputStream() {
+        return output;
     }
 
     /**
@@ -1066,8 +1146,8 @@
      *  . if the socket is already closed, throw an Exception (internal error)
      *
      *  . otherwise (cs_START or cs_DATA), create the appropriate handshaker
-     *    object, initialize it, and advance the connection state (to
-     *    cs_HANDSHAKE or cs_RENEGOTIATE, respectively).
+     *    object, and advance the connection state (to cs_HANDSHAKE or
+     *    cs_RENEGOTIATE, respectively).
      *
      * This method is called right after a new socket is created, when
      * starting renegotiation, or when changing client/ server mode of the
@@ -1115,12 +1195,8 @@
                     protocolVersion, connectionState == cs_HANDSHAKE,
                     secureRenegotiation, clientVerifyData, serverVerifyData);
         }
-        handshaker.enabledCipherSuites = enabledCipherSuites;
+        handshaker.setEnabledCipherSuites(enabledCipherSuites);
         handshaker.setEnableSessionCreation(enableSessionCreation);
-        if (connectionState == cs_RENEGOTIATE) {
-            // don't use SSLv2Hello when renegotiating
-            handshaker.output.r.setHelloVersion(protocolVersion);
-        }
     }
 
     /**
@@ -1135,6 +1211,8 @@
         // one thread performs the handshake
         synchronized (handshakeLock) {
             if (getConnectionState() == cs_HANDSHAKE) {
+                kickstartHandshake();
+
                 /*
                  * All initial handshaking goes through this
                  * InputRecord until we have a valid SSL connection.
@@ -1157,7 +1235,6 @@
                     inrec.enableFormatChecks();
                 }
 
-                kickstartHandshake();
                 readRecord(inrec, false);
                 inrec = null;
             }
@@ -1211,6 +1288,7 @@
      *    on servers when renegotiating).
      */
     private synchronized void kickstartHandshake() throws IOException {
+
         switch (connectionState) {
 
         case cs_HANDSHAKE:
@@ -1257,7 +1335,15 @@
         // to its HandshakeOutStream, which calls back into
         // SSLSocketImpl.writeRecord() to send it.
         //
-        if (!handshaker.started()) {
+        if (!handshaker.activated()) {
+             // prior to handshaking, activate the handshake
+            if (connectionState == cs_RENEGOTIATE) {
+                // don't use SSLv2Hello when renegotiating
+                handshaker.activate(protocolVersion);
+            } else {
+                handshaker.activate(null);
+            }
+
             if (handshaker instanceof ClientHandshaker) {
                 // send client hello
                 handshaker.kickstart();
@@ -1752,10 +1838,18 @@
      * Emit alerts.  Caller must have synchronized with "this".
      */
     private void sendAlert(byte level, byte description) {
+        // the connectionState cannot be cs_START
         if (connectionState >= cs_SENT_CLOSE) {
             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;
+        }
+
         OutputRecord r = new OutputRecord(Record.ct_alert);
         r.setVersion(protocolVersion);
 
@@ -1962,7 +2056,7 @@
     synchronized public void setEnableSessionCreation(boolean flag) {
         enableSessionCreation = flag;
 
-        if ((handshaker != null) && !handshaker.started()) {
+        if ((handshaker != null) && !handshaker.activated()) {
             handshaker.setEnableSessionCreation(enableSessionCreation);
         }
     }
@@ -1990,7 +2084,7 @@
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
-                !handshaker.started()) {
+                !handshaker.activated()) {
             ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
         }
     }
@@ -2013,7 +2107,7 @@
 
         if ((handshaker != null) &&
                 (handshaker instanceof ServerHandshaker) &&
-                !handshaker.started()) {
+                !handshaker.activated()) {
             ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
         }
     }
@@ -2032,6 +2126,15 @@
         switch (connectionState) {
 
         case cs_START:
+            /*
+             * If we need to change the socket mode and the enabled
+             * protocols haven't specifically been set by the user,
+             * change them to the corresponding default ones.
+             */
+            if (roleIsServer != (!flag) &&
+                    ProtocolList.isDefaultProtocolList(enabledProtocols)) {
+                enabledProtocols = ProtocolList.getDefault(!flag);
+            }
             roleIsServer = !flag;
             break;
 
@@ -2044,7 +2147,16 @@
              * have the streams.
              */
             assert(handshaker != null);
-            if (!handshaker.started()) {
+            if (!handshaker.activated()) {
+                /*
+                 * If we need to change the socket mode and the enabled
+                 * protocols haven't specifically been set by the user,
+                 * change them to the corresponding default ones.
+                 */
+                if (roleIsServer != (!flag) &&
+                        ProtocolList.isDefaultProtocolList(enabledProtocols)) {
+                    enabledProtocols = ProtocolList.getDefault(!flag);
+                }
                 roleIsServer = !flag;
                 connectionState = cs_START;
                 initHandshaker();
@@ -2095,8 +2207,8 @@
      */
     synchronized public void setEnabledCipherSuites(String[] suites) {
         enabledCipherSuites = new CipherSuiteList(suites);
-        if ((handshaker != null) && !handshaker.started()) {
-            handshaker.enabledCipherSuites = enabledCipherSuites;
+        if ((handshaker != null) && !handshaker.activated()) {
+            handshaker.setEnabledCipherSuites(enabledCipherSuites);
         }
     }
 
@@ -2135,7 +2247,7 @@
      */
     synchronized public void setEnabledProtocols(String[] protocols) {
         enabledProtocols = new ProtocolList(protocols);
-        if ((handshaker != null) && !handshaker.started()) {
+        if ((handshaker != null) && !handshaker.activated()) {
             handshaker.setEnabledProtocols(enabledProtocols);
         }
     }