if (connectionState == cs_RENEGOTIATE) { // don't use SSLv2Hello when renegotiating handshaker.activate(protocolVersion); } else { handshaker.activate(null); } } /* * process the handshake record ... may contain just * a partial handshake message or multiple messages. * * The handshaker state machine will ensure that it's * a finished message. */ handshaker.process_record(inputRecord, expectingFinished); expectingFinished = false; if (handshaker.invalidated) { handshaker = null; // if state is cs_RENEGOTIATE, revert it to cs_DATA if (connectionState == cs_RENEGOTIATE) { connectionState = cs_DATA; } } else if (handshaker.isDone()) { // reset the parameters for secure renegotiation. secureRenegotiation = handshaker.isSecureRenegotiation(); clientVerifyData = handshaker.getClientVerifyData(); serverVerifyData = handshaker.getServerVerifyData(); sess = handshaker.getSession(); handshakeSession = null; if (!writer.hasOutboundData()) { hsStatus = HandshakeStatus.FINISHED; } handshaker = null; connectionState = cs_DATA; // No handshakeListeners here. That's a // SSLSocket thing. } else if (handshaker.taskOutstanding()) { hsStatus = HandshakeStatus.NEED_TASK; } break; case Record.ct_application_data: // Pass this right back up to the application. if ((connectionState != cs_DATA) && (connectionState != cs_RENEGOTIATE) && (connectionState != cs_CLOSED)) { throw new SSLProtocolException( "Data received in non-data state: " + connectionState); } if (expectingFinished) { throw new SSLProtocolException ("Expecting finished message, received data"); } /* * Don't return data once the inbound side is * closed. */ if (!inboundDone) { ea.scatter(decryptedBB.slice()); } break; case Record.ct_alert: recvAlert(); break; case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE && connectionState != cs_RENEGOTIATE) || inputRecord.available() != 1 || inputRecord.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; break; default: // // TLS requires that unrecognized records be ignored. // if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", Received record type: " + inputRecord.contentType()); } break; } // switch /* * We only need to check the sequence number state for * non-handshaking record. * * Note that in order to maintain the handshake status * 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 && !isInboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { if (checkSequenceNumber(readMAC, inputRecord.contentType())) { hsStatus = getHSStatus(null); } } } // synchronized (this) } return hsStatus; } // // write/wrap side // /** * Wraps a buffer. Does a variety of checks before grabbing * the wrapLock, which blocks multiple wraps from occuring. */ public SSLEngineResult wrap(ByteBuffer [] appData, int offset, int length, ByteBuffer netData) throws SSLException { EngineArgs ea = new EngineArgs(appData, offset, length, netData); /* * We can be smarter about using smaller buffer sizes later. * For now, force it to be large enough to handle any * valid SSL/TLS record. */ if (netData.remaining() < outputRecord.maxRecordSize) { return new SSLEngineResult( Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0); } try { synchronized (wrapLock) { return writeAppRecord(ea); } } catch (Exception e) { ea.resetPos(); fatal(Alerts.alert_internal_error, "problem unwrapping net record", e); return null; // make compiler happy } finally { /* * Just in case something didn't reset limits properly. */ ea.resetLim(); } } /* * Makes additional checks for unwrap, but this time more * specific to this packet and the current state of the machine. */ private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException { Status status = null; HandshakeStatus hsStatus = null; /* * See if the handshaker needs to report back some SSLException. */ checkTaskThrown(); /* * short circuit if we're closed/closing. */ if (writer.isOutboundDone()) { return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); } /* * If we're still in cs_HANDSHAKE, make sure it's been * started. */ synchronized (this) { if ((connectionState == cs_HANDSHAKE) || (connectionState == cs_START)) { kickstartHandshake(); /* * If there's no HS data available to write, we can return * without trying to wrap anything. */ hsStatus = getHSStatus(null); if (hsStatus == HandshakeStatus.NEED_UNWRAP) { return new SSLEngineResult(Status.OK, hsStatus, 0, 0); } } } /* * Grab a copy of this if it doesn't already exist, * and we can use it several places before anything major * happens on this side. Races aren't critical * here. */ if (hsStatus == null) { hsStatus = getHSStatus(null); } /* * If we have a task outstanding, this *MUST* be done before * doing any more wrapping, because we could be in the middle * of receiving a handshake message, for example, a finished * message which would change the ciphers. */ if (hsStatus == HandshakeStatus.NEED_TASK) { return new SSLEngineResult( Status.OK, hsStatus, 0, 0); } /* * This will obtain any waiting outbound data, or will * process the outbound appData. */ try { synchronized (writeLock) { hsStatus = writeRecord(outputRecord, ea); } } catch (SSLException e) { throw e; } catch (IOException e) { SSLException ex = new SSLException("Write problems"); ex.initCause(e); throw ex; } /* * writeRecord might have reported some status. * Now check for the remaining cases. * * status above should cover: NEED_WRAP/FINISHED */ status = (isOutboundDone() ? Status.CLOSED : Status.OK); hsStatus = getHSStatus(hsStatus); return new SSLEngineResult(status, hsStatus, ea.deltaApp(), ea.deltaNet()); } /* * Central point to write/get all of the outgoing data. */ private HandshakeStatus writeRecord(EngineOutputRecord eor, EngineArgs ea) throws IOException { // eventually compress as well. HandshakeStatus hsStatus = writer.writeRecord(eor, ea, writeMAC, writeCipher); /* * We only need to check the sequence number state for * non-handshaking record. * * Note that in order to maintain the handshake status * 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 && !isOutboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { if (checkSequenceNumber(writeMAC, eor.contentType())) { hsStatus = getHSStatus(null); } } return hsStatus; } /* * Non-application OutputRecords go through here. */ void writeRecord(EngineOutputRecord eor) throws IOException { // eventually compress as well. writer.writeRecord(eor, writeMAC, writeCipher); /* * 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) && !isOutboundDone()) { checkSequenceNumber(writeMAC, eor.contentType()); } } // // Close code // /** * 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." * * Return true if the handshake status may be changed. */ private boolean 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 false; } /* * 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"); return true; // make the compiler happy } /* * 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"); } beginHandshake(); return true; } return false; } /** * Signals that no more outbound application data will be sent * on this <code>SSLEngine</code>. */ private void closeOutboundInternal() { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", closeOutboundInternal()"); } /* * Already closed, ignore */ if (writer.isOutboundDone()) { return; } switch (connectionState) { /* * If we haven't even started yet, don't bother reading inbound. */ case cs_START: writer.closeOutbound(); inboundDone = true; break; case cs_ERROR: case cs_CLOSED: break; /* * Otherwise we indicate clean termination. */ // case cs_HANDSHAKE: // case cs_DATA: // case cs_RENEGOTIATE: default: warning(Alerts.alert_close_notify); writer.closeOutbound(); break; } // See comment in changeReadCiphers() writeCipher.dispose(); connectionState = cs_CLOSED; } synchronized public void closeOutbound() { /* * Dump out a close_notify to the remote side */ if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeOutbound()"); } closeOutboundInternal(); } /** * Returns the outbound application data closure state */ public boolean isOutboundDone() { return writer.isOutboundDone(); } /** * Signals that no more inbound network data will be sent * to this <code>SSLEngine</code>. */ private void closeInboundInternal() { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", closeInboundInternal()"); } /* * Already closed, ignore */ if (inboundDone) { return; } closeOutboundInternal(); inboundDone = true; // See comment in changeReadCiphers() readCipher.dispose(); connectionState = cs_CLOSED; } /* * Close the inbound side of the connection. We grab the * lock here, and do the real work in the internal verison. * We do check for truncation attacks. */ synchronized public void closeInbound() throws SSLException { /* * Currently closes the outbound side as well. The IETF TLS * working group has expressed the opinion that 1/2 open * connections are not allowed by the spec. May change * someday in the future. */ if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeInbound()"); } /* * No need to throw an Exception if we haven't even started yet. */Lines 1001–1500 of 2073. Previous lines Load more
if (connectionState == cs_RENEGOTIATE) { // don't use SSLv2Hello when renegotiating handshaker.activate(protocolVersion); } else { handshaker.activate(null); } } /* * process the handshake record ... may contain just * a partial handshake message or multiple messages. * * The handshaker state machine will ensure that it's * a finished message. */ handshaker.process_record(inputRecord, expectingFinished); expectingFinished = false; if (handshaker.invalidated) { handshaker = null; // if state is cs_RENEGOTIATE, revert it to cs_DATA if (connectionState == cs_RENEGOTIATE) { connectionState = cs_DATA; } } else if (handshaker.isDone()) { // reset the parameters for secure renegotiation. secureRenegotiation = handshaker.isSecureRenegotiation(); clientVerifyData = handshaker.getClientVerifyData(); serverVerifyData = handshaker.getServerVerifyData(); sess = handshaker.getSession(); handshakeSession = null; if (!writer.hasOutboundData()) { hsStatus = HandshakeStatus.FINISHED; } handshaker = null; connectionState = cs_DATA; // No handshakeListeners here. That's a // SSLSocket thing. } else if (handshaker.taskOutstanding()) { hsStatus = HandshakeStatus.NEED_TASK; } break; case Record.ct_application_data: // Pass this right back up to the application. if ((connectionState != cs_DATA) && (connectionState != cs_RENEGOTIATE) && (connectionState != cs_CLOSED)) { throw new SSLProtocolException( "Data received in non-data state: " + connectionState); } if (expectingFinished) { throw new SSLProtocolException ("Expecting finished message, received data"); } /* * Don't return data once the inbound side is * closed. */ if (!inboundDone) { ea.scatter(decryptedBB.slice()); } break; case Record.ct_alert: recvAlert(); break; case Record.ct_change_cipher_spec: if ((connectionState != cs_HANDSHAKE && connectionState != cs_RENEGOTIATE) || inputRecord.available() != 1 || inputRecord.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; break; default: // // TLS requires that unrecognized records be ignored. // if (debug != null && Debug.isOn("ssl")) { System.out.println(threadName() + ", Received record type: " + inputRecord.contentType()); } break; } // switch /* * We only need to check the sequence number state for * non-handshaking record. * * Note that in order to maintain the handshake status * 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 && !isInboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { if (checkSequenceNumber(readMAC, inputRecord.contentType())) { hsStatus = getHSStatus(null); } } } // synchronized (this) } return hsStatus; } // // write/wrap side // /** * Wraps a buffer. Does a variety of checks before grabbing * the wrapLock, which blocks multiple wraps from occuring. */ public SSLEngineResult wrap(ByteBuffer [] appData, int offset, int length, ByteBuffer netData) throws SSLException { EngineArgs ea = new EngineArgs(appData, offset, length, netData); /* * We can be smarter about using smaller buffer sizes later. * For now, force it to be large enough to handle any * valid SSL/TLS record. */ if (netData.remaining() < outputRecord.maxRecordSize) { return new SSLEngineResult( Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0); } try { synchronized (wrapLock) { return writeAppRecord(ea); } } catch (Exception e) { ea.resetPos(); fatal(Alerts.alert_internal_error, "problem unwrapping net record", e); return null; // make compiler happy } finally { /* * Just in case something didn't reset limits properly. */ ea.resetLim(); } } /* * Makes additional checks for unwrap, but this time more * specific to this packet and the current state of the machine. */ private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException { Status status = null; HandshakeStatus hsStatus = null; /* * See if the handshaker needs to report back some SSLException. */ checkTaskThrown(); /* * short circuit if we're closed/closing. */ if (writer.isOutboundDone()) { return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); } /* * If we're still in cs_HANDSHAKE, make sure it's been * started. */ synchronized (this) { if ((connectionState == cs_HANDSHAKE) || (connectionState == cs_START)) { kickstartHandshake(); /* * If there's no HS data available to write, we can return * without trying to wrap anything. */ hsStatus = getHSStatus(null); if (hsStatus == HandshakeStatus.NEED_UNWRAP) { return new SSLEngineResult(Status.OK, hsStatus, 0, 0); } } } /* * Grab a copy of this if it doesn't already exist, * and we can use it several places before anything major * happens on this side. Races aren't critical * here. */ if (hsStatus == null) { hsStatus = getHSStatus(null); } /* * If we have a task outstanding, this *MUST* be done before * doing any more wrapping, because we could be in the middle * of receiving a handshake message, for example, a finished * message which would change the ciphers. */ if (hsStatus == HandshakeStatus.NEED_TASK) { return new SSLEngineResult( Status.OK, hsStatus, 0, 0); } /* * This will obtain any waiting outbound data, or will * process the outbound appData. */ try { synchronized (writeLock) { hsStatus = writeRecord(outputRecord, ea); } } catch (SSLException e) { throw e; } catch (IOException e) { SSLException ex = new SSLException("Write problems"); ex.initCause(e); throw ex; } /* * writeRecord might have reported some status. * Now check for the remaining cases. * * status above should cover: NEED_WRAP/FINISHED */ status = (isOutboundDone() ? Status.CLOSED : Status.OK); hsStatus = getHSStatus(hsStatus); return new SSLEngineResult(status, hsStatus, ea.deltaApp(), ea.deltaNet()); } /* * Central point to write/get all of the outgoing data. */ private HandshakeStatus writeRecord(EngineOutputRecord eor, EngineArgs ea) throws IOException { // eventually compress as well. HandshakeStatus hsStatus = writer.writeRecord(eor, ea, writeMAC, writeCipher); /* * We only need to check the sequence number state for * non-handshaking record. * * Note that in order to maintain the handshake status * 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 && !isOutboundDone() && (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { if (checkSequenceNumber(writeMAC, eor.contentType())) { hsStatus = getHSStatus(null); } } return hsStatus; } /* * Non-application OutputRecords go through here. */ void writeRecord(EngineOutputRecord eor) throws IOException { // eventually compress as well. writer.writeRecord(eor, writeMAC, writeCipher); /* * 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) && !isOutboundDone()) { checkSequenceNumber(writeMAC, eor.contentType()); } } // // Close code // /** * 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." * * Return true if the handshake status may be changed. */ private boolean 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 false; } /* * 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"); return true; // make the compiler happy } /* * 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"); } beginHandshake(); return true; } return false; } /** * Signals that no more outbound application data will be sent * on this <code>SSLEngine</code>. */ private void closeOutboundInternal() { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", closeOutboundInternal()"); } /* * Already closed, ignore */ if (writer.isOutboundDone()) { return; } switch (connectionState) { /* * If we haven't even started yet, don't bother reading inbound. */ case cs_START: writer.closeOutbound(); inboundDone = true; break; case cs_ERROR: case cs_CLOSED: break; /* * Otherwise we indicate clean termination. */ // case cs_HANDSHAKE: // case cs_DATA: // case cs_RENEGOTIATE: default: warning(Alerts.alert_close_notify); writer.closeOutbound(); break; } // See comment in changeReadCiphers() writeCipher.dispose(); connectionState = cs_CLOSED; } synchronized public void closeOutbound() { /* * Dump out a close_notify to the remote side */ if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeOutbound()"); } closeOutboundInternal(); } /** * Returns the outbound application data closure state */ public boolean isOutboundDone() { return writer.isOutboundDone(); } /** * Signals that no more inbound network data will be sent * to this <code>SSLEngine</code>. */ private void closeInboundInternal() { if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", closeInboundInternal()"); } /* * Already closed, ignore */ if (inboundDone) { return; } closeOutboundInternal(); inboundDone = true; // See comment in changeReadCiphers() readCipher.dispose(); connectionState = cs_CLOSED; } /* * Close the inbound side of the connection. We grab the * lock here, and do the real work in the internal verison. * We do check for truncation attacks. */ synchronized public void closeInbound() throws SSLException { /* * Currently closes the outbound side as well. The IETF TLS * working group has expressed the opinion that 1/2 open * connections are not allowed by the spec. May change * someday in the future. */ if ((debug != null) && Debug.isOn("ssl")) { System.out.println(threadName() + ", called closeInbound()"); } /* * No need to throw an Exception if we haven't even started yet. */