| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.util.ArrayList;  | 
 | 
import javax.crypto.BadPaddingException;  | 
 | 
import javax.net.ssl.SSLException;  | 
 | 
import javax.net.ssl.SSLHandshakeException;  | 
 | 
import javax.net.ssl.SSLProtocolException;  | 
 | 
import sun.security.ssl.SSLCipher.SSLReadCipher;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class SSLEngineInputRecord extends InputRecord implements SSLRecord { | 
 | 
    private boolean formatVerified = false;       | 
 | 
 | 
 | 
      | 
 | 
    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 { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (packet.remaining() < 5) { | 
 | 
            return -1;  | 
 | 
        }  | 
 | 
 | 
 | 
        int pos = packet.position();  | 
 | 
        byte byteZero = packet.get(pos);  | 
 | 
 | 
 | 
        int len = 0;  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (formatVerified ||  | 
 | 
                (byteZero == ContentType.HANDSHAKE.id) ||  | 
 | 
                (byteZero == ContentType.ALERT.id)) { | 
 | 
              | 
 | 
 | 
 | 
             */  | 
 | 
            byte majorVersion = packet.get(pos + 1);  | 
 | 
            byte minorVersion = packet.get(pos + 2);  | 
 | 
            if (!ProtocolVersion.isNegotiable(  | 
 | 
                    majorVersion, minorVersion, false)) { | 
 | 
                throw new SSLException("Unrecognized record version " + | 
 | 
                        ProtocolVersion.nameOf(majorVersion, minorVersion) +  | 
 | 
                        " , plaintext connection?");  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            formatVerified = true;  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
             */  | 
 | 
            len = ((packet.get(pos + 3) & 0xFF) << 8) +  | 
 | 
                   (packet.get(pos + 4) & 0xFF) + headerSize;  | 
 | 
 | 
 | 
        } else { | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            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)) { | 
 | 
                    throw new SSLException("Unrecognized record version " + | 
 | 
                            ProtocolVersion.nameOf(majorVersion, minorVersion) +  | 
 | 
                            " , plaintext connection?");  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
 | 
 | 
                 */  | 
 | 
                int mask = (isShort ? 0x7F : 0x3F);  | 
 | 
                len = ((byteZero & mask) << 8) +  | 
 | 
                        (packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);  | 
 | 
 | 
 | 
            } else { | 
 | 
                  | 
 | 
                throw new SSLException(  | 
 | 
                        "Unrecognized SSL message, plaintext connection?");  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        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); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (!formatVerified) { | 
 | 
            formatVerified = true;  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            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();                     | 
 | 
        byte majorVersion = packet.get();                    | 
 | 
        byte minorVersion = packet.get();                    | 
 | 
        int contentLen = Record.getInt16(packet);            | 
 | 
 | 
 | 
        if (SSLLogger.isOn && SSLLogger.isOn("record")) { | 
 | 
            SSLLogger.fine(  | 
 | 
                    "READ: " +  | 
 | 
                    ProtocolVersion.nameOf(majorVersion, minorVersion) +  | 
 | 
                    " " + ContentType.nameOf(contentType) + ", length = " +  | 
 | 
                    contentLen);  | 
 | 
        }  | 
 | 
 | 
 | 
        //  | 
 | 
        // Check for upper bound.  | 
 | 
        //  | 
 | 
          | 
 | 
        if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) { | 
 | 
            throw new SSLProtocolException(  | 
 | 
                "Bad input record size, TLSCiphertext.length = " + contentLen);  | 
 | 
        }  | 
 | 
 | 
 | 
        //  | 
 | 
        // Decrypt the fragment  | 
 | 
          | 
 | 
        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 { | 
 | 
              | 
 | 
            packet.limit(srcLim);  | 
 | 
            packet.position(recLim);  | 
 | 
        }  | 
 | 
 | 
 | 
        //  | 
 | 
        // check for handshake fragment  | 
 | 
          | 
 | 
        if (contentType != ContentType.HANDSHAKE.id &&  | 
 | 
                handshakeBuffer != null && handshakeBuffer.hasRemaining()) { | 
 | 
            throw new SSLProtocolException(  | 
 | 
                    "Expecting a handshake fragment, but received " +  | 
 | 
                    ContentType.nameOf(contentType));  | 
 | 
        }  | 
 | 
 | 
 | 
        //  | 
 | 
        // 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 = (ByteBuffer)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();  | 
 | 
                  | 
 | 
                byte handshakeType = handshakeFrag.get();  | 
 | 
                int handshakeBodyLen = Record.getInt24(handshakeFrag);  | 
 | 
                if (handshakeBodyLen > SSLConfiguration.maxHandshakeMessageSize) { | 
 | 
                    throw new SSLProtocolException(  | 
 | 
                            "The size of the handshake message (" | 
 | 
                            + handshakeBodyLen  | 
 | 
                            + ") exceeds the maximum allowed size (" | 
 | 
                            + SSLConfiguration.maxHandshakeMessageSize  | 
 | 
                            + ")");  | 
 | 
                }  | 
 | 
 | 
 | 
                handshakeFrag.reset();  | 
 | 
                int handshakeMessageLen =  | 
 | 
                        handshakeHeaderSize + handshakeBodyLen;  | 
 | 
                if (remaining < handshakeMessageLen) { | 
 | 
                    handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);  | 
 | 
                    handshakeBuffer.put(handshakeFrag);  | 
 | 
                    handshakeBuffer.rewind();  | 
 | 
                    break;  | 
 | 
                } else 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();  | 
 | 
 | 
 | 
        byte firstByte = packet.get(srcPos);  | 
 | 
        byte thirdByte = packet.get(srcPos + 2);  | 
 | 
 | 
 | 
          | 
 | 
        if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { | 
 | 
              | 
 | 
 | 
 | 
             */  | 
 | 
            if (helloVersion != ProtocolVersion.SSL20Hello) { | 
 | 
                throw new SSLHandshakeException("SSLv2Hello is not enabled"); | 
 | 
            }  | 
 | 
 | 
 | 
            byte majorVersion = packet.get(srcPos + 3);  | 
 | 
            byte minorVersion = packet.get(srcPos + 4);  | 
 | 
 | 
 | 
            if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&  | 
 | 
                (minorVersion == ProtocolVersion.SSL20Hello.minor)) { | 
 | 
 | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("record")) { | 
 | 
                   SSLLogger.fine(  | 
 | 
                            "Requested to negotiate unsupported SSLv2!");  | 
 | 
                }  | 
 | 
 | 
 | 
                // hack code, the exception is caught in SSLEngineImpl  | 
 | 
                  | 
 | 
                throw new UnsupportedOperationException(          | 
 | 
                        "Unsupported SSL v2.0 ClientHello");  | 
 | 
            }  | 
 | 
 | 
 | 
            /*  | 
 | 
             * 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);          | 
 | 
            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"); | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |