if (needAppData || connectionState != cs_DATA) { continue; } break; case Record.ct_application_data: // Pass this right back up to the application. if (connectionState != cs_DATA && connectionState != cs_RENEGOTIATE && connectionState != cs_SENT_CLOSE) { throw new SSLProtocolException( "Data received in non-data state: " + connectionState); } if (expectingFinished) { throw new SSLProtocolException ("Expecting finished message, received data"); } if (!needAppData) { throw new SSLException("Discarding app data"); } r.setAppDataValid(true); break; case Record.ct_alert: recvAlert(r); continue; case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE && connectionState != cs_RENEGOTIATE) || r.available() != 1 || r.read() != 1) { fatal(Alerts.alert_unexpected_message, "illegal change cipher spec msg, state = " + connectionState); } // // The first message after a change_cipher_spec // record MUST be a "Finished" handshake record, // else it's a protocol violation. We force this // to be checked by a minor tweak to the state // machine. // changeReadCiphers(); // next message MUST be a finished message expectingFinished = true; continue; default: // // TLS requires that unrecognized records be ignored. // if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", Received record type: " + r.contentType()); } 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) } // // couldn't read, due to some kind of error // r.close(); return; } // 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 // /** * Return the AppInputStream. For use by Handshaker only. */ AppInputStream getAppInputStream() { return input; } /** * Return the AppOutputStream. For use by Handshaker only. */ AppOutputStream getAppOutputStream() { return output; } /** * Initialize the handshaker object. This means: * * . if a handshake is already in progress (state is cs_HANDSHAKE * or cs_RENEGOTIATE), do nothing and return * * . if the socket is already closed, throw an Exception (internal error) * * . otherwise (cs_START or cs_DATA), create the appropriate handshaker * 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 * socket. */ private void initHandshaker() { switch (connectionState) { // // Starting a new handshake. // case cs_START: case cs_DATA: break; // // We're already in the middle of a handshake. // case cs_HANDSHAKE: case cs_RENEGOTIATE: return; // // Anyone allowed to call this routine is required to // do so ONLY if the connection state is reasonable... // default: throw new IllegalStateException("Internal error"); } // state is either cs_START or cs_DATA if (connectionState == cs_START) { connectionState = cs_HANDSHAKE; } else { // cs_DATA connectionState = cs_RENEGOTIATE; } if (roleIsServer) { handshaker = new ServerHandshaker(this, sslContext, enabledProtocols, doClientAuth, protocolVersion, connectionState == cs_HANDSHAKE, secureRenegotiation, clientVerifyData, serverVerifyData); } else { handshaker = new ClientHandshaker(this, sslContext, enabledProtocols, protocolVersion, connectionState == cs_HANDSHAKE, secureRenegotiation, clientVerifyData, serverVerifyData); } handshaker.setEnabledCipherSuites(enabledCipherSuites); handshaker.setEnableSessionCreation(enableSessionCreation); } /** * Synchronously perform the initial handshake. * * If the handshake is already in progress, this method blocks until it * is completed. If the initial handshake has already been completed, * it returns immediately. */ private void performInitialHandshake() throws IOException { // use handshakeLock and the state check to make sure only // 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. * Once initial handshaking is finished, AppInputStream's * InputRecord can handle any future renegotiation. * * Keep this local so that it goes out of scope and is * eventually GC'd. */ if (inrec == null) { inrec = new InputRecord(); /* * Grab the characteristics already assigned to * AppInputStream's InputRecord. Enable checking for * SSLv2 hellos on this first handshake. */ inrec.setHandshakeHash(input.r.getHandshakeHash()); inrec.setHelloVersion(input.r.getHelloVersion()); inrec.enableFormatChecks(); } readRecord(inrec, false); inrec = null; } } } /** * Starts an SSL handshake on this connection. */ public void startHandshake() throws IOException { // start an ssl handshake that could be resumed from timeout exception startHandshake(true); } /** * Starts an ssl handshake on this connection. * * @param resumable indicates the handshake process is resumable from a * certain exception. If <code>resumable</code>, the socket will * be reserved for exceptions like timeout; otherwise, the socket * will be closed, no further communications could be done. */ private void startHandshake(boolean resumable) throws IOException { checkWrite(); try { if (getConnectionState() == cs_HANDSHAKE) { // do initial handshake performInitialHandshake(); } else { // start renegotiation kickstartHandshake(); } } catch (Exception e) { // shutdown and rethrow (wrapped) exception as appropriate handleException(e, resumable); } } /** * Kickstart the handshake if it is not already in progress. * This means: * * . if handshaking is already underway, do nothing and return * * . if the socket is not connected or already closed, throw an * Exception. * * . otherwise, call initHandshake() to initialize the handshaker * object and progress the state. Then, send the initial * handshaking message if appropriate (always on clients and * on servers when renegotiating). */ private synchronized void kickstartHandshake() throws IOException { switch (connectionState) { case cs_HANDSHAKE: // handshaker already setup, proceed break; case cs_DATA: if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { throw new SSLHandshakeException( "Insecure renegotiation is not allowed"); } if (!secureRenegotiation) { if (debug != null && Debug.isOn("handshake")) { System.out.println( "Warning: Using insecure renegotiation"); } } // initialize the handshaker, move to cs_RENEGOTIATE initHandshaker(); break; case cs_RENEGOTIATE: // handshaking already in progress, return return; /* * The only way to get a socket in the state is when * you have an unconnected socket. */ case cs_START: throw new SocketException( "handshaking attempted on unconnected socket"); default: throw new SocketException("connection is closed"); } // // Kickstart handshake state machine if we need to ... // // Note that handshaker.kickstart() writes the message // to its HandshakeOutStream, which calls back into // SSLSocketImpl.writeRecord() to send it. // 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(); } else { if (connectionState == cs_HANDSHAKE) { // initial handshake, no kickstart message to send } else { // we want to renegotiate, send hello request handshaker.kickstart(); // hello request is not included in the handshake // hashes, reset them handshaker.handshakeHash.reset(); } } } } // // CLOSURE RELATED CALLS // /** * Return whether the socket has been explicitly closed by the application. */ public boolean isClosed() { return getConnectionState() == cs_APP_CLOSED; } /** * Return whether we have reached end-of-file. * * If the socket is not connected, has been shutdown because of an error * or has been closed, throw an Exception. */ boolean checkEOF() throws IOException { switch (getConnectionState()) { case cs_START: throw new SocketException("Socket is not connected"); case cs_HANDSHAKE: case cs_DATA: case cs_RENEGOTIATE: case cs_SENT_CLOSE: return false; case cs_APP_CLOSED: throw new SocketException("Socket is closed"); case cs_ERROR: case cs_CLOSED: default: // either closed because of error, or normal EOF if (closeReason == null) { return true; } IOException e = new SSLException ("Connection has been shutdown: " + closeReason); e.initCause(closeReason); throw e; } } /** * Check if we can write data to this socket. If not, throw an IOException. */ void checkWrite() throws IOException { if (checkEOF() || (getConnectionState() == cs_SENT_CLOSE)) { // we are at EOF, write must throw Exception throw new SocketException("Connection closed by remote host"); } } protected void closeSocket() throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeSocket()"); } if (self == this) { super.close(); } else { self.close(); } } private void closeSocket(boolean selfInitiated) throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeSocket(selfInitiated)"); } if (self == this) { super.close(); } else if (autoClose) { self.close(); } else if (selfInitiated) { // layered && non-autoclose // read close_notify alert to clear input stream waitForClose(false); } } /* * Closing the connection is tricky ... we can't officially close the * connection until we know the other end is ready to go away too, * and if ever the connection gets aborted we must forget session * state (it becomes invalid). */ /** * Closes the SSL connection. SSL includes an application level * shutdown handshake; you should close SSL sockets explicitly * rather than leaving it for finalization, so that your remote * peer does not experience a protocol error. */ public void close() throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called close()"); } closeInternal(true); // caller is initiating close setConnectionState(cs_APP_CLOSED); } /** * Don't synchronize the whole method because waitForClose() * (which calls readRecord()) might be called. * * @param selfInitiated Indicates which party initiated the close. * If selfInitiated, this side is initiating a close; for layered and * non-autoclose socket, wait for close_notify response. * If !selfInitiated, peer sent close_notify; we reciprocate but * no need to wait for response.Lines 1001–1500 of 2493. Previous lines Load more
if (needAppData || connectionState != cs_DATA) { continue; } break; case Record.ct_application_data: // Pass this right back up to the application. if (connectionState != cs_DATA && connectionState != cs_RENEGOTIATE && connectionState != cs_SENT_CLOSE) { throw new SSLProtocolException( "Data received in non-data state: " + connectionState); } if (expectingFinished) { throw new SSLProtocolException ("Expecting finished message, received data"); } if (!needAppData) { throw new SSLException("Discarding app data"); } r.setAppDataValid(true); break; case Record.ct_alert: recvAlert(r); continue; case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE && connectionState != cs_RENEGOTIATE) || r.available() != 1 || r.read() != 1) { fatal(Alerts.alert_unexpected_message, "illegal change cipher spec msg, state = " + connectionState); } // // The first message after a change_cipher_spec // record MUST be a "Finished" handshake record, // else it's a protocol violation. We force this // to be checked by a minor tweak to the state // machine. // changeReadCiphers(); // next message MUST be a finished message expectingFinished = true; continue; default: // // TLS requires that unrecognized records be ignored. // if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", Received record type: " + r.contentType()); } 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) } // // couldn't read, due to some kind of error // r.close(); return; } // 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 // /** * Return the AppInputStream. For use by Handshaker only. */ AppInputStream getAppInputStream() { return input; } /** * Return the AppOutputStream. For use by Handshaker only. */ AppOutputStream getAppOutputStream() { return output; } /** * Initialize the handshaker object. This means: * * . if a handshake is already in progress (state is cs_HANDSHAKE * or cs_RENEGOTIATE), do nothing and return * * . if the socket is already closed, throw an Exception (internal error) * * . otherwise (cs_START or cs_DATA), create the appropriate handshaker * 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 * socket. */ private void initHandshaker() { switch (connectionState) { // // Starting a new handshake. // case cs_START: case cs_DATA: break; // // We're already in the middle of a handshake. // case cs_HANDSHAKE: case cs_RENEGOTIATE: return; // // Anyone allowed to call this routine is required to // do so ONLY if the connection state is reasonable... // default: throw new IllegalStateException("Internal error"); } // state is either cs_START or cs_DATA if (connectionState == cs_START) { connectionState = cs_HANDSHAKE; } else { // cs_DATA connectionState = cs_RENEGOTIATE; } if (roleIsServer) { handshaker = new ServerHandshaker(this, sslContext, enabledProtocols, doClientAuth, protocolVersion, connectionState == cs_HANDSHAKE, secureRenegotiation, clientVerifyData, serverVerifyData); } else { handshaker = new ClientHandshaker(this, sslContext, enabledProtocols, protocolVersion, connectionState == cs_HANDSHAKE, secureRenegotiation, clientVerifyData, serverVerifyData); } handshaker.setEnabledCipherSuites(enabledCipherSuites); handshaker.setEnableSessionCreation(enableSessionCreation); } /** * Synchronously perform the initial handshake. * * If the handshake is already in progress, this method blocks until it * is completed. If the initial handshake has already been completed, * it returns immediately. */ private void performInitialHandshake() throws IOException { // use handshakeLock and the state check to make sure only // 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. * Once initial handshaking is finished, AppInputStream's * InputRecord can handle any future renegotiation. * * Keep this local so that it goes out of scope and is * eventually GC'd. */ if (inrec == null) { inrec = new InputRecord(); /* * Grab the characteristics already assigned to * AppInputStream's InputRecord. Enable checking for * SSLv2 hellos on this first handshake. */ inrec.setHandshakeHash(input.r.getHandshakeHash()); inrec.setHelloVersion(input.r.getHelloVersion()); inrec.enableFormatChecks(); } readRecord(inrec, false); inrec = null; } } } /** * Starts an SSL handshake on this connection. */ public void startHandshake() throws IOException { // start an ssl handshake that could be resumed from timeout exception startHandshake(true); } /** * Starts an ssl handshake on this connection. * * @param resumable indicates the handshake process is resumable from a * certain exception. If <code>resumable</code>, the socket will * be reserved for exceptions like timeout; otherwise, the socket * will be closed, no further communications could be done. */ private void startHandshake(boolean resumable) throws IOException { checkWrite(); try { if (getConnectionState() == cs_HANDSHAKE) { // do initial handshake performInitialHandshake(); } else { // start renegotiation kickstartHandshake(); } } catch (Exception e) { // shutdown and rethrow (wrapped) exception as appropriate handleException(e, resumable); } } /** * Kickstart the handshake if it is not already in progress. * This means: * * . if handshaking is already underway, do nothing and return * * . if the socket is not connected or already closed, throw an * Exception. * * . otherwise, call initHandshake() to initialize the handshaker * object and progress the state. Then, send the initial * handshaking message if appropriate (always on clients and * on servers when renegotiating). */ private synchronized void kickstartHandshake() throws IOException { switch (connectionState) { case cs_HANDSHAKE: // handshaker already setup, proceed break; case cs_DATA: if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { throw new SSLHandshakeException( "Insecure renegotiation is not allowed"); } if (!secureRenegotiation) { if (debug != null && Debug.isOn("handshake")) { System.out.println( "Warning: Using insecure renegotiation"); } } // initialize the handshaker, move to cs_RENEGOTIATE initHandshaker(); break; case cs_RENEGOTIATE: // handshaking already in progress, return return; /* * The only way to get a socket in the state is when * you have an unconnected socket. */ case cs_START: throw new SocketException( "handshaking attempted on unconnected socket"); default: throw new SocketException("connection is closed"); } // // Kickstart handshake state machine if we need to ... // // Note that handshaker.kickstart() writes the message // to its HandshakeOutStream, which calls back into // SSLSocketImpl.writeRecord() to send it. // 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(); } else { if (connectionState == cs_HANDSHAKE) { // initial handshake, no kickstart message to send } else { // we want to renegotiate, send hello request handshaker.kickstart(); // hello request is not included in the handshake // hashes, reset them handshaker.handshakeHash.reset(); } } } } // // CLOSURE RELATED CALLS // /** * Return whether the socket has been explicitly closed by the application. */ public boolean isClosed() { return getConnectionState() == cs_APP_CLOSED; } /** * Return whether we have reached end-of-file. * * If the socket is not connected, has been shutdown because of an error * or has been closed, throw an Exception. */ boolean checkEOF() throws IOException { switch (getConnectionState()) { case cs_START: throw new SocketException("Socket is not connected"); case cs_HANDSHAKE: case cs_DATA: case cs_RENEGOTIATE: case cs_SENT_CLOSE: return false; case cs_APP_CLOSED: throw new SocketException("Socket is closed"); case cs_ERROR: case cs_CLOSED: default: // either closed because of error, or normal EOF if (closeReason == null) { return true; } IOException e = new SSLException ("Connection has been shutdown: " + closeReason); e.initCause(closeReason); throw e; } } /** * Check if we can write data to this socket. If not, throw an IOException. */ void checkWrite() throws IOException { if (checkEOF() || (getConnectionState() == cs_SENT_CLOSE)) { // we are at EOF, write must throw Exception throw new SocketException("Connection closed by remote host"); } } protected void closeSocket() throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeSocket()"); } if (self == this) { super.close(); } else { self.close(); } } private void closeSocket(boolean selfInitiated) throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeSocket(selfInitiated)"); } if (self == this) { super.close(); } else if (autoClose) { self.close(); } else if (selfInitiated) { // layered && non-autoclose // read close_notify alert to clear input stream waitForClose(false); } } /* * Closing the connection is tricky ... we can't officially close the * connection until we know the other end is ready to go away too, * and if ever the connection gets aborted we must forget session * state (it becomes invalid). */ /** * Closes the SSL connection. SSL includes an application level * shutdown handshake; you should close SSL sockets explicitly * rather than leaving it for finalization, so that your remote * peer does not experience a protocol error. */ public void close() throws IOException { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called close()"); } closeInternal(true); // caller is initiating close setConnectionState(cs_APP_CLOSED); } /** * Don't synchronize the whole method because waitForClose() * (which calls readRecord()) might be called. * * @param selfInitiated Indicates which party initiated the close. * If selfInitiated, this side is initiating a close; for layered and * non-autoclose socket, wait for close_notify response. * If !selfInitiated, peer sent close_notify; we reciprocate but * no need to wait for response.