< prev index next >

src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java

Print this page

        

*** 1,7 **** /* ! * 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 * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1996, 2018, 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
*** 25,41 **** package sun.security.ssl; import java.io.*; import java.nio.*; ! import javax.crypto.BadPaddingException; - import javax.net.ssl.*; ! ! import sun.security.util.HexDumpEncoder; ! /** * {@code InputRecord} implementation for {@code SSLEngine}. */ final class SSLEngineInputRecord extends InputRecord implements SSLRecord { --- 25,39 ---- package sun.security.ssl; import java.io.*; import java.nio.*; ! import java.security.GeneralSecurityException; ! import java.util.ArrayList; import javax.crypto.BadPaddingException; import javax.net.ssl.*; ! import sun.security.ssl.SSLCipher.SSLReadCipher; /** * {@code InputRecord} implementation for {@code SSLEngine}. */ final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
*** 44,74 **** private int hsMsgOff = 0; private int hsMsgLen = 0; private boolean formatVerified = false; // SSLv2 ruled out? ! SSLEngineInputRecord() { ! this.readAuthenticator = MAC.TLS_NULL; } @Override int estimateFragmentSize(int packetSize) { - int macLen = 0; - if (readAuthenticator instanceof MAC) { - macLen = ((MAC)readAuthenticator).MAClen(); - } - if (packetSize > 0) { ! return readCipher.estimateFragmentSize( ! packetSize, macLen, headerSize); } else { return Record.maxDataSize; } } @Override ! int bytesInCompletePacket(ByteBuffer packet) throws SSLException { /* * SSLv2 length field is in bytes 0/1 * SSLv3/TLS length field is in bytes 3/4 */ if (packet.remaining() < 5) { --- 42,75 ---- private int hsMsgOff = 0; private int hsMsgLen = 0; private boolean formatVerified = false; // SSLv2 ruled out? ! // Cache for incomplete handshake messages. ! private ByteBuffer handshakeBuffer = null; ! ! SSLEngineInputRecord(HandshakeHash handshakeHash) { ! super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); } @Override int estimateFragmentSize(int packetSize) { if (packetSize > 0) { ! return readCipher.estimateFragmentSize(packetSize, headerSize); } else { return Record.maxDataSize; } } @Override ! int bytesInCompletePacket( ! ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException { ! ! return bytesInCompletePacket(srcs[srcsOffset]); ! } ! ! private int bytesInCompletePacket(ByteBuffer packet) throws SSLException { /* * SSLv2 length field is in bytes 0/1 * SSLv3/TLS length field is in bytes 3/4 */ if (packet.remaining() < 5) {
*** 85,103 **** * ignore the verifications steps, and jump right to the * determination. Otherwise, try one last hueristic to * see if it's SSL/TLS. */ if (formatVerified || ! (byteZero == ct_handshake) || (byteZero == ct_alert)) { /* * Last sanity check that it's not a wild record */ ! ProtocolVersion recordVersion = ProtocolVersion.valueOf( ! packet.get(pos + 1), packet.get(pos + 2)); ! ! // check the record version ! checkRecordVersion(recordVersion, false); /* * Reasonably sure this is a V3, disable further checks. * We can't do the same in the v2 check below, because * read still needs to parse/handle the v2 clientHello. --- 86,108 ---- * ignore the verifications steps, and jump right to the * determination. Otherwise, try one last hueristic to * see if it's SSL/TLS. */ if (formatVerified || ! (byteZero == ContentType.HANDSHAKE.id) || ! (byteZero == ContentType.ALERT.id)) { /* * Last sanity check that it's not a wild record */ ! byte majorVersion = packet.get(pos + 1); ! byte minorVersion = packet.get(pos + 2); ! if (!ProtocolVersion.isNegotiable( ! majorVersion, minorVersion, false, false)) { ! throw new SSLException("Unrecognized record version " + ! ProtocolVersion.nameOf(majorVersion, minorVersion) + ! " , plaintext connection?"); ! } /* * Reasonably sure this is a V3, disable further checks. * We can't do the same in the v2 check below, because * read still needs to parse/handle the v2 clientHello.
*** 121,135 **** boolean isShort = ((byteZero & 0x80) != 0); if (isShort && ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) { ! ProtocolVersion recordVersion = ProtocolVersion.valueOf( ! packet.get(pos + 3), packet.get(pos + 4)); ! ! // check the record version ! checkRecordVersion(recordVersion, true); /* * Client or Server Hello */ int mask = (isShort ? 0x7F : 0x3F); --- 126,143 ---- boolean isShort = ((byteZero & 0x80) != 0); if (isShort && ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) { ! byte majorVersion = packet.get(pos + 3); ! byte minorVersion = packet.get(pos + 4); ! if (!ProtocolVersion.isNegotiable( ! majorVersion, minorVersion, false, false)) { ! throw new SSLException("Unrecognized record version " + ! ProtocolVersion.nameOf(majorVersion, minorVersion) + ! " , plaintext connection?"); ! } /* * Client or Server Hello */ int mask = (isShort ? 0x7F : 0x3F);
*** 145,185 **** return len; } @Override ! void checkRecordVersion(ProtocolVersion recordVersion, ! boolean allowSSL20Hello) throws SSLException { ! ! if (recordVersion.maybeDTLSProtocol()) { ! throw new SSLException( ! "Unrecognized record version " + recordVersion + ! " , DTLS packet?"); ! } ! // Check if the record version is too old. ! if ((recordVersion.v < ProtocolVersion.MIN.v)) { ! // if it's not SSLv2, we're out of here. ! if (!allowSSL20Hello || ! (recordVersion.v != ProtocolVersion.SSL20Hello.v)) { ! throw new SSLException( ! "Unsupported record version " + recordVersion); ! } } } ! @Override ! Plaintext decode(ByteBuffer packet) throws IOException, BadPaddingException { if (isClosed) { return null; } ! if (debug != null && Debug.isOn("packet")) { ! Debug.printHex( ! "[Raw read]: length = " + packet.remaining(), packet); } // The caller should have validated the record. if (!formatVerified) { formatVerified = true; --- 153,185 ---- return len; } @Override ! Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, ! int srcsLength) throws IOException, BadPaddingException { ! if (srcs == null || srcs.length == 0 || srcsLength == 0) { ! return new Plaintext[0]; ! } else if (srcsLength == 1) { ! return decode(srcs[srcsOffset]); ! } else { ! ByteBuffer packet = extract(srcs, ! srcsOffset, srcsLength, SSLRecord.headerSize); ! return decode(packet); } } ! private Plaintext[] decode(ByteBuffer packet) throws IOException, BadPaddingException { if (isClosed) { return null; } ! if (SSLLogger.isOn && SSLLogger.isOn("packet")) { ! SSLLogger.fine("Raw read", packet); } // The caller should have validated the record. if (!formatVerified) { formatVerified = true;
*** 189,227 **** * alert message. If it's not, it is either invalid or an * SSLv2 message. */ int pos = packet.position(); byte byteZero = packet.get(pos); ! if (byteZero != ct_handshake && byteZero != ct_alert) { return handleUnknownRecord(packet); } } return decodeInputRecord(packet); } ! private Plaintext decodeInputRecord(ByteBuffer packet) throws IOException, BadPaddingException { - // // The packet should be a complete record, or more. // - int srcPos = packet.position(); int srcLim = packet.limit(); byte contentType = packet.get(); // pos: 0 byte majorVersion = packet.get(); // pos: 1 byte minorVersion = packet.get(); // pos: 2 ! int contentLen = ((packet.get() & 0xFF) << 8) + ! (packet.get() & 0xFF); // pos: 3, 4 ! if (debug != null && Debug.isOn("record")) { ! System.out.println(Thread.currentThread().getName() + ! ", READ: " + ! ProtocolVersion.valueOf(majorVersion, minorVersion) + ! " " + Record.contentName(contentType) + ", length = " + contentLen); } // // Check for upper bound. --- 189,225 ---- * alert message. If it's not, it is either invalid or an * SSLv2 message. */ int pos = packet.position(); byte byteZero = packet.get(pos); ! if (byteZero != ContentType.HANDSHAKE.id && ! byteZero != ContentType.ALERT.id) { return handleUnknownRecord(packet); } } return decodeInputRecord(packet); } ! private Plaintext[] decodeInputRecord(ByteBuffer packet) throws IOException, BadPaddingException { // // The packet should be a complete record, or more. // int srcPos = packet.position(); int srcLim = packet.limit(); byte contentType = packet.get(); // pos: 0 byte majorVersion = packet.get(); // pos: 1 byte minorVersion = packet.get(); // pos: 2 ! int contentLen = Record.getInt16(packet); // pos: 3, 4 ! if (SSLLogger.isOn && SSLLogger.isOn("record")) { ! SSLLogger.fine( ! "READ: " + ! ProtocolVersion.nameOf(majorVersion, minorVersion) + ! " " + ContentType.nameOf(contentType) + ", length = " + contentLen); } // // Check for upper bound.
*** 233,243 **** } // // check for handshake fragment // ! if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) { throw new SSLProtocolException( "Expected to get a handshake fragment"); } // --- 231,241 ---- } // // check for handshake fragment // ! if (contentType != ContentType.HANDSHAKE.id && hsMsgOff != hsMsgLen) { throw new SSLProtocolException( "Expected to get a handshake fragment"); } //
*** 245,340 **** // int recLim = srcPos + SSLRecord.headerSize + contentLen; packet.limit(recLim); packet.position(srcPos + SSLRecord.headerSize); ! ByteBuffer plaintext; try { ! plaintext = ! decrypt(readAuthenticator, readCipher, contentType, packet); } finally { // comsume a complete record packet.limit(srcLim); packet.position(recLim); } // ! // handshake hashing // ! if (contentType == ct_handshake) { ! int pltPos = plaintext.position(); ! int pltLim = plaintext.limit(); ! int frgPos = pltPos; ! for (int remains = plaintext.remaining(); remains > 0;) { ! int howmuch; ! byte handshakeType; ! if (hsMsgOff < hsMsgLen) { ! // a fragment of the handshake message ! howmuch = Math.min((hsMsgLen - hsMsgOff), remains); ! handshakeType = prevType; ! ! hsMsgOff += howmuch; ! if (hsMsgOff == hsMsgLen) { ! // Now is a complete handshake message. ! hsMsgOff = 0; ! hsMsgLen = 0; ! } ! } else { // hsMsgOff == hsMsgLen, a new handshake message ! handshakeType = plaintext.get(); ! int handshakeLen = ((plaintext.get() & 0xFF) << 16) | ! ((plaintext.get() & 0xFF) << 8) | ! (plaintext.get() & 0xFF); ! plaintext.position(frgPos); ! if (remains < (handshakeLen + 4)) { // 4: handshake header ! // This handshake message is fragmented. ! prevType = handshakeType; ! hsMsgOff = remains - 4; // 4: handshake header ! hsMsgLen = handshakeLen; ! } ! ! howmuch = Math.min(handshakeLen + 4, remains); ! } ! ! plaintext.limit(frgPos + howmuch); ! ! if (handshakeType == HandshakeMessage.ht_hello_request) { ! // omitted from handshake hash computation ! } else if ((handshakeType != HandshakeMessage.ht_finished) && ! (handshakeType != HandshakeMessage.ht_certificate_verify)) { ! ! if (handshakeHash == null) { ! // used for cache only ! handshakeHash = new HandshakeHash(false); ! } ! handshakeHash.update(plaintext); } else { ! // Reserve until this handshake message has been processed. ! if (handshakeHash == null) { ! // used for cache only ! handshakeHash = new HandshakeHash(false); ! } ! handshakeHash.reserve(plaintext); } ! plaintext.position(frgPos + howmuch); ! plaintext.limit(pltLim); ! frgPos += howmuch; ! remains -= howmuch; } ! plaintext.position(pltPos); } ! return new Plaintext(contentType, ! majorVersion, minorVersion, -1, -1L, plaintext); ! // recordEpoch, recordSeq, plaintext); } ! private Plaintext handleUnknownRecord(ByteBuffer packet) throws IOException, BadPaddingException { - // // The packet should be a complete record. // int srcPos = packet.position(); int srcLim = packet.limit(); --- 243,347 ---- // int recLim = srcPos + SSLRecord.headerSize + contentLen; packet.limit(recLim); packet.position(srcPos + SSLRecord.headerSize); ! ByteBuffer fragment; try { ! Plaintext plaintext = ! readCipher.decrypt(contentType, packet, null); ! fragment = plaintext.fragment; ! contentType = plaintext.contentType; ! } catch (BadPaddingException bpe) { ! throw bpe; ! } catch (GeneralSecurityException gse) { ! throw (SSLProtocolException)(new SSLProtocolException( ! "Unexpected exception")).initCause(gse); } finally { // comsume a complete record packet.limit(srcLim); packet.position(recLim); } // ! // parse handshake messages // ! if (contentType == ContentType.HANDSHAKE.id) { ! ByteBuffer handshakeFrag = fragment; ! if ((handshakeBuffer != null) && ! (handshakeBuffer.remaining() != 0)) { ! ByteBuffer bb = ByteBuffer.wrap(new byte[ ! handshakeBuffer.remaining() + fragment.remaining()]); ! bb.put(handshakeBuffer); ! bb.put(fragment); ! handshakeFrag = bb.rewind(); ! handshakeBuffer = null; ! } ! ! ArrayList<Plaintext> plaintexts = new ArrayList<>(5); ! while (handshakeFrag.hasRemaining()) { ! int remaining = handshakeFrag.remaining(); ! if (remaining < handshakeHeaderSize) { ! handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); ! handshakeBuffer.put(handshakeFrag); ! handshakeBuffer.rewind(); ! break; ! } ! ! handshakeFrag.mark(); ! // skip the first byte: handshake type ! byte handshakeType = handshakeFrag.get(); ! int handshakeBodyLen = Record.getInt24(handshakeFrag); ! handshakeFrag.reset(); ! int handshakeMessageLen = ! handshakeHeaderSize + handshakeBodyLen; ! if (remaining < handshakeMessageLen) { ! handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); ! handshakeBuffer.put(handshakeFrag); ! handshakeBuffer.rewind(); ! break; ! } if (remaining == handshakeMessageLen) { ! if (handshakeHash.isHashable(handshakeType)) { ! handshakeHash.receive(handshakeFrag); ! } ! ! plaintexts.add( ! new Plaintext(contentType, ! majorVersion, minorVersion, -1, -1L, handshakeFrag) ! ); ! break; } else { ! int fragPos = handshakeFrag.position(); ! int fragLim = handshakeFrag.limit(); ! int nextPos = fragPos + handshakeMessageLen; ! handshakeFrag.limit(nextPos); ! ! if (handshakeHash.isHashable(handshakeType)) { ! handshakeHash.receive(handshakeFrag); } ! plaintexts.add( ! new Plaintext(contentType, majorVersion, minorVersion, ! -1, -1L, handshakeFrag.slice()) ! ); ! handshakeFrag.position(nextPos); ! handshakeFrag.limit(fragLim); ! } } ! return plaintexts.toArray(new Plaintext[0]); } ! return new Plaintext[] { ! new Plaintext(contentType, ! majorVersion, minorVersion, -1, -1L, fragment) ! }; } ! private Plaintext[] handleUnknownRecord(ByteBuffer packet) throws IOException, BadPaddingException { // // The packet should be a complete record. // int srcPos = packet.position(); int srcLim = packet.limit();
*** 361,372 **** * Looks like a V2 client hello, but not one saying * "let's talk SSLv3". So we need to send an SSLv2 * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ ! if (debug != null && Debug.isOn("record")) { ! System.out.println(Thread.currentThread().getName() + "Requested to negotiate unsupported SSLv2!"); } // hack code, the exception is caught in SSLEngineImpl // so that SSLv2 error message can be delivered properly. --- 368,379 ---- * Looks like a V2 client hello, but not one saying * "let's talk SSLv3". So we need to send an SSLv2 * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ ! if (SSLLogger.isOn && SSLLogger.isOn("record")) { ! SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); } // hack code, the exception is caught in SSLEngineImpl // so that SSLv2 error message can be delivered properly.
*** 378,409 **** * If we can map this into a V3 ClientHello, read and * hash the rest of the V2 handshake, turn it into a * V3 ClientHello message, and pass it up. */ packet.position(srcPos + 2); // exclude the header ! ! if (handshakeHash == null) { ! // used for cache only ! handshakeHash = new HandshakeHash(false); ! } ! handshakeHash.update(packet); packet.position(srcPos); ByteBuffer converted = convertToClientHello(packet); ! if (debug != null && Debug.isOn("packet")) { ! Debug.printHex( "[Converted] ClientHello", converted); } ! return new Plaintext(ct_handshake, ! majorVersion, minorVersion, -1, -1L, converted); } else { if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { throw new SSLException("SSL V2.0 servers are not supported."); } throw new SSLException("Unsupported or unrecognized SSL message"); } } - } --- 385,412 ---- * If we can map this into a V3 ClientHello, read and * hash the rest of the V2 handshake, turn it into a * V3 ClientHello message, and pass it up. */ packet.position(srcPos + 2); // exclude the header ! handshakeHash.receive(packet); packet.position(srcPos); ByteBuffer converted = convertToClientHello(packet); ! if (SSLLogger.isOn && SSLLogger.isOn("packet")) { ! SSLLogger.fine( "[Converted] ClientHello", converted); } ! return new Plaintext[] { ! new Plaintext(ContentType.HANDSHAKE.id, ! majorVersion, minorVersion, -1, -1L, converted) ! }; } else { if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { throw new SSLException("SSL V2.0 servers are not supported."); } throw new SSLException("Unsupported or unrecognized SSL message"); } } }
< prev index next >