703 lines · 22.4 KB · Java
RawDownload
/* * Copyright (c) 1996, 2008, 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */  package sun.security.ssl; import java.io.*;import java.nio.*;import java.net.SocketException;import java.net.SocketTimeoutException; import javax.crypto.BadPaddingException; import javax.net.ssl.*; import sun.misc.HexDumpEncoder;  /** * SSL 3.0 records, as pulled off a TCP stream.  Input records are * basically buffers tied to a particular input stream ... a layer * above this must map these records into the model of a continuous * stream of data. * * Since this returns SSL 3.0 records, it's the layer that needs to * map SSL 2.0 style handshake records into SSL 3.0 ones for those * "old" clients that interop with both V2 and V3 servers.  Not as * pretty as might be desired. * * NOTE:  During handshaking, each message must be hashed to support * verification that the handshake process wasn't compromised. * * @author David Brownell */class InputRecord extends ByteArrayInputStream implements Record {     private HandshakeHash       handshakeHash;    private int                 lastHashed;    boolean                     formatVerified = true;  // SSLv2 ruled out?    private boolean             isClosed;    private boolean             appDataValid;     // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello    // and the first message we read is a ClientHello in V2 format, we convert    // it to V3. Otherwise we throw an exception when encountering a V2 hello.    private ProtocolVersion     helloVersion;     /* Class and subclass dynamic debugging support */    static final Debug debug = Debug.getInstance("ssl");     /* The existing record length */    private int exlen;     /* V2 handshake message */    private byte v2Buf[];     /*     * Construct the record to hold the maximum sized input record.     * Data will be filled in separately.     */    InputRecord() {        super(new byte[maxRecordSize]);        setHelloVersion(ProtocolVersion.DEFAULT_HELLO);        pos = headerSize;        count = headerSize;        lastHashed = count;        exlen = 0;        v2Buf = null;    }     void setHelloVersion(ProtocolVersion helloVersion) {        this.helloVersion = helloVersion;    }     ProtocolVersion getHelloVersion() {        return helloVersion;    }     /*     * Enable format checks if initial handshaking hasn't completed     */    void enableFormatChecks() {        formatVerified = false;    }     // return whether the data in this record is valid, decrypted data    boolean isAppDataValid() {        return appDataValid;    }     void setAppDataValid(boolean value) {        appDataValid = value;    }     /*     * Return the content type of the record.     */    byte contentType() {        return buf[0];    }     /*     * For handshaking, we need to be able to hash every byte above the     * record marking layer.  This is where we're guaranteed to see those     * bytes, so this is where we can hash them ... especially in the     * case of hashing the initial V2 message!     */    void setHandshakeHash(HandshakeHash handshakeHash) {        this.handshakeHash = handshakeHash;    }     HandshakeHash getHandshakeHash() {        return handshakeHash;    }     /*     * Verify and remove the MAC ... used for all records.     */    boolean checkMAC(MAC signer) {        int len = signer.MAClen();        if (len == 0) { // no mac            return true;        }         int offset = count - len;         if (offset < headerSize) {            // data length would be negative, something is wrong            return false;        }         byte[] mac = signer.compute(contentType(), buf,            headerSize, offset - headerSize);         if (len != mac.length) {            throw new RuntimeException("Internal MAC error");        }         for (int i = 0; i < len; i++) {            if (buf[offset + i] != mac[i]) {                return false;            }        }        count -= len;        return true;    }     void decrypt(CipherBox box) throws BadPaddingException {        int len = count - headerSize;        count = headerSize + box.decrypt(buf, headerSize, len);    }      /*     * Well ... hello_request messages are _never_ hashed since we can't     * know when they'd appear in the sequence.     */    void ignore(int bytes) {        if (bytes > 0) {            pos += bytes;            lastHashed = pos;        }    }     /*     * We hash the (plaintext) we've processed, but only on demand.     *     * There is one place where we want to access the hash in the middle     * of a record:  client cert message gets hashed, and part of the     * same record is the client cert verify message which uses that hash.     * So we track how much we've read and hashed.     */    void doHashes() {        int len = pos - lastHashed;         if (len > 0) {            hashInternal(buf, lastHashed, len);            lastHashed = pos;        }    }     /*     * Need a helper function so we can hash the V2 hello correctly     */    private void hashInternal(byte databuf [], int offset, int len) {        if (debug != null && Debug.isOn("data")) {            try {                HexDumpEncoder hd = new HexDumpEncoder();                 System.out.println("[read] MD5 and SHA1 hashes:  len = "                    + len);                hd.encodeBuffer(new ByteArrayInputStream(databuf, offset, len),                    System.out);            } catch (IOException e) { }        }        handshakeHash.update(databuf, offset, len);    }      /*     * Handshake messages may cross record boundaries.  We "queue"     * these in big buffers if we need to cope with this problem.     * This is not anticipated to be a common case; if this turns     * out to be wrong, this can readily be sped up.     */    void queueHandshake(InputRecord r) throws IOException {        int len;         /*         * Hash any data that's read but unhashed.         */        doHashes();         /*         * Move any unread data to the front of the buffer,         * flagging it all as unhashed.         */        if (pos > headerSize) {            len = count - pos;            if (len != 0) {                System.arraycopy(buf, pos, buf, headerSize, len);            }            pos = headerSize;            lastHashed = pos;            count = headerSize + len;        }         /*         * Grow "buf" if needed         */        len = r.available() + count;        if (buf.length < len) {            byte        newbuf [];             newbuf = new byte [len];            System.arraycopy(buf, 0, newbuf, 0, count);            buf = newbuf;        }         /*         * Append the new buffer to this one.         */        System.arraycopy(r.buf, r.pos, buf, count, len - count);        count = len;         /*         * Adjust lastHashed; important for now with clients which         * send SSL V2 client hellos.  This will go away eventually,         * by buffer code cleanup.         */        len = r.lastHashed - r.pos;        if (pos == headerSize) {            lastHashed += len;        } else {            throw new SSLProtocolException("?? confused buffer hashing ??");        }        // we've read the record, advance the pointers        r.pos = r.count;    }      /**     * Prevent any more data from being read into this record,     * and flag the record as holding no data.     */    public void close() {        appDataValid = false;        isClosed = true;        mark = 0;        pos = 0;        count = 0;    }      /*     * We may need to send this SSL v2 "No Cipher" message back, if we     * are faced with an SSLv2 "hello" that's not saying "I talk v3".     * It's the only one documented in the V2 spec as a fatal error.     */    private static final byte[] v2NoCipher = {        (byte)0x80, (byte)0x03, // unpadded 3 byte record        (byte)0x00,             // ... error message        (byte)0x00, (byte)0x01  // ... NO_CIPHER error    };     private int readFully(InputStream s, byte b[], int off, int len)            throws IOException {        int n = 0;        while (n < len) {            int readLen = s.read(b, off + n, len - n);            if (readLen < 0) {                return readLen;            }             if (debug != null && Debug.isOn("packet")) {                try {                    HexDumpEncoder hd = new HexDumpEncoder();                    ByteBuffer bb = ByteBuffer.wrap(b, off + n, readLen);                     System.out.println("[Raw read]: length = " +                        bb.remaining());                    hd.encodeBuffer(bb, System.out);                } catch (IOException e) { }            }             n += readLen;            exlen += readLen;        }         return n;    }     /*     * Read the SSL V3 record ... first time around, check to see if it     * really IS a V3 record.  Handle SSL V2 clients which can talk V3.0,     * as well as real V3 record format; otherwise report an error.     */    void read(InputStream s, OutputStream o) throws IOException {        if (isClosed) {            return;        }         /*         * For SSL it really _is_ an error if the other end went away         * so ungracefully as to not shut down cleanly.         */        if(exlen < headerSize) {            int really = readFully(s, buf, exlen, headerSize - exlen);            if (really < 0) {                throw new EOFException("SSL peer shut down incorrectly");            }             pos = headerSize;            count = headerSize;            lastHashed = pos;        }         /*         * The first record might use some other record marking convention,         * typically SSL v2 header.  (PCT could also be detected here.)         * This case is currently common -- Navigator 3.0 usually works         * this way, as do IE 3.0 and other products.         */        if (!formatVerified) {            formatVerified = true;            /*             * The first record must either be a handshake record or an             * alert message. If it's not, it is either invalid or an             * SSLv2 message.             */            if (buf[0] != ct_handshake && buf[0] != ct_alert) {                handleUnknownRecord(s, o);            } else {                readV3Record(s, o);            }        } else { // formatVerified == true            readV3Record(s, o);        }    }     /**     * Read a SSL/TLS record. Throw an IOException if the format is invalid.     */    private void readV3Record(InputStream s, OutputStream o)            throws IOException {        ProtocolVersion recordVersion = ProtocolVersion.valueOf(buf[1], buf[2]);        // Check if too old (currently not possible)        // or if the major version does not match.        // The actual version negotiation is in the handshaker classes        if ((recordVersion.v < ProtocolVersion.MIN.v)                || (recordVersion.major > ProtocolVersion.MAX.major)) {            throw new SSLException(                "Unsupported record version " + recordVersion);        }         /*         * Get and check length, then the data.         */        int contentLen = ((buf[3] & 0x0ff) << 8) + (buf[4] & 0xff);         /*         * Check for upper bound.         */        if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {            throw new SSLProtocolException("Bad InputRecord size"                + ", count = " + contentLen                + ", buf.length = " + buf.length);        }         /*         * Grow "buf" if needed. Since buf is maxRecordSize by default,         * this only occurs when we receive records which violate the         * SSL specification. This is a workaround for a Microsoft SSL bug.         */        if (contentLen > buf.length - headerSize) {            byte[] newbuf = new byte[contentLen + headerSize];            System.arraycopy(buf, 0, newbuf, 0, headerSize);            buf = newbuf;        }         if (exlen < contentLen + headerSize) {            int really = readFully(                s, buf, exlen, contentLen + headerSize - exlen);            if (really < 0) {                throw new SSLException("SSL peer shut down incorrectly");            }        }         // now we've got a complete record.        count = contentLen + headerSize;        exlen = 0;         if (debug != null && Debug.isOn("record")) {            if (count < 0 || count > (maxRecordSize - headerSize)) {                System.out.println(Thread.currentThread().getName()                    + ", Bad InputRecord size" + ", count = " + count);            }            System.out.println(Thread.currentThread().getName()                + ", READ: " + recordVersion + " "                + contentName(contentType()) + ", length = " + available());        }        /*         * then caller decrypts, verifies, and uncompresses         */    }     /**     * Deal with unknown records. Called if the first data we read on this     * connection does not look like an SSL/TLS record. It could a SSLv2     * message, or just garbage.     */    private void handleUnknownRecord(InputStream s, OutputStream o)            throws IOException {        /*         * No?  Oh well; does it look like a V2 "ClientHello"?         * That'd be an unpadded handshake message; we don't         * bother checking length just now.         */        if (((buf[0] & 0x080) != 0) && buf[2] == 1) {            /*             * if the user has disabled SSLv2Hello (using             * setEnabledProtocol) then throw an             * exception             */            if (helloVersion != ProtocolVersion.SSL20Hello) {                throw new SSLHandshakeException("SSLv2Hello is disabled");            }             ProtocolVersion recordVersion =                                ProtocolVersion.valueOf(buf[3], buf[4]);             if (recordVersion == ProtocolVersion.SSL20Hello) {                /*                 * Looks like a V2 client hello, but not one saying                 * "let's talk SSLv3".  So we send an SSLv2 error                 * message, one that's treated as fatal by clients.                 * (Otherwise we'll hang.)                 */                try {                    writeBuffer(o, v2NoCipher, 0, v2NoCipher.length);                } catch (Exception e) {                    /* NOTHING */                }                throw new SSLException("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.             */            int len = ((buf[0] & 0x7f) << 8) +                (buf[1] & 0xff) - 3;            if (v2Buf == null) {                v2Buf = new byte[len];            }            if (exlen < len + headerSize) {                int really = readFully(

Lines 1–500 of 703.