2073 lines · 71.5 KB · Java
RawDownload
             return HandshakeStatus.NOT_HANDSHAKING;        }    }     synchronized private void checkTaskThrown() throws SSLException {        if (handshaker != null) {            handshaker.checkThrown();        }    }     //    // Handshaking and connection state code    //     /*     * Provides "this" synchronization for connection state.     * Otherwise, you can access it directly.     */    synchronized private int getConnectionState() {        return connectionState;    }     synchronized private void setConnectionState(int state) {        connectionState = state;    }     /*     * Get the Access Control Context.     *     * Used for a known context to     * run tasks in, and for determining which credentials     * to use for Subject-based (JAAS) decisions.     */    AccessControlContext getAcc() {        return acc;    }     /*     * Is a handshake currently underway?     */    public SSLEngineResult.HandshakeStatus getHandshakeStatus() {        return getHSStatus(null);    }     /*     * When a connection finishes handshaking by enabling use of a newly     * negotiated session, each end learns about it in two halves (read,     * and write).  When both read and write ciphers have changed, and the     * last handshake message has been read, the connection has joined     * (rejoined) the new session.     *     * NOTE:  The SSLv3 spec is rather unclear on the concepts here.     * Sessions don't change once they're established (including cipher     * suite and master secret) but connections can join them (and leave     * them).  They're created by handshaking, though sometime handshaking     * causes connections to join up with pre-established sessions.     *     * Synchronized on "this" from readRecord.     */    private void changeReadCiphers() throws SSLException {        if (connectionState != cs_HANDSHAKE                && connectionState != cs_RENEGOTIATE) {            throw new SSLProtocolException(                "State error, change cipher specs");        }         // ... create decompressor         CipherBox oldCipher = readCipher;         try {            readCipher = handshaker.newReadCipher();            readMAC = handshaker.newReadMAC();        } catch (GeneralSecurityException e) {            // "can't happen"            throw (SSLException)new SSLException                                ("Algorithm missing:  ").initCause(e);        }         /*         * Dispose of any intermediate state in the underlying cipher.         * For PKCS11 ciphers, this will release any attached sessions,         * and thus make finalization faster.         *         * Since MAC's doFinal() is called for every SSL/TLS packet, it's         * not necessary to do the same with MAC's.         */        oldCipher.dispose();    }     /*     * used by Handshaker to change the active write cipher, follows     * the output of the CCS message.     *     * Also synchronized on "this" from readRecord/delegatedTask.     */    void changeWriteCiphers() throws SSLException {        if (connectionState != cs_HANDSHAKE                && connectionState != cs_RENEGOTIATE) {            throw new SSLProtocolException(                "State error, change cipher specs");        }         // ... create compressor         CipherBox oldCipher = writeCipher;         try {            writeCipher = handshaker.newWriteCipher();            writeMAC = handshaker.newWriteMAC();        } catch (GeneralSecurityException e) {            // "can't happen"            throw (SSLException)new SSLException                                ("Algorithm missing:  ").initCause(e);        }         // See comment above.        oldCipher.dispose();    }     /*     * Updates the SSL version associated with this connection.     * Called from Handshaker once it has determined the negotiated version.     */    synchronized void setVersion(ProtocolVersion protocolVersion) {        this.protocolVersion = protocolVersion;        outputRecord.setVersion(protocolVersion);    }      /**     * Kickstart the handshake if it is not already in progress.     * This means:     *     *  . if handshaking is already underway, do nothing and return     *     *  . if the engine 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_START:            if (!serverModeSet) {                throw new IllegalStateException(                    "Client/Server mode not yet set.");            }            initHandshaker();            break;         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;         default:            // cs_ERROR/cs_CLOSED            throw new SSLException("SSLEngine is closing/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 {    // instanceof ServerHandshaker                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();                }            }        }    }     /*     * Start a SSLEngine handshake     */    public void beginHandshake() throws SSLException {        try {            kickstartHandshake();        } catch (Exception e) {            fatal(Alerts.alert_handshake_failure,                "Couldn't kickstart handshaking", e);        }    }      //    // Read/unwrap side    //      /**     * Unwraps a buffer.  Does a variety of checks before grabbing     * the unwrapLock, which blocks multiple unwraps from occuring.     */    public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer [] appData,            int offset, int length) throws SSLException {         EngineArgs ea = new EngineArgs(netData, appData, offset, length);         try {            synchronized (unwrapLock) {                return readNetRecord(ea);            }        } catch (Exception e) {            /*             * Don't reset position so it looks like we didn't             * consume anything.  We did consume something, and it             * got us into this situation, so report that much back.             * Our days of consuming are now over anyway.             */            fatal(Alerts.alert_internal_error,                "problem unwrapping net record", e);            return null;  // make compiler happy        } finally {            /*             * Just in case something failed to 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 readNetRecord(EngineArgs ea) throws IOException {         Status status = null;        HandshakeStatus hsStatus = null;         /*         * See if the handshaker needs to report back some SSLException.         */        checkTaskThrown();         /*         * Check if we are closing/closed.         */        if (isInboundDone()) {            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 still outbound data to flush, we                 * can return without trying to unwrap anything.                 */                hsStatus = getHSStatus(null);                 if (hsStatus == HandshakeStatus.NEED_WRAP) {                    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 unwrapping, 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);        }         /*         * Check the packet to make sure enough is here.         * This will also indirectly check for 0 len packets.         */        int packetLen = inputRecord.bytesInCompletePacket(ea.netData);         // Is this packet bigger than SSL/TLS normally allows?        if (packetLen > sess.getPacketBufferSize()) {            if (packetLen > Record.maxLargeRecordSize) {                throw new SSLProtocolException(                    "Input SSL/TLS record too big: max = " +                    Record.maxLargeRecordSize +                    " len = " + packetLen);            } else {                // Expand the expected maximum packet/application buffer                // sizes.                sess.expandBufferSizes();            }        }         /*         * Check for OVERFLOW.         *         * To be considered: We could delay enforcing the application buffer         * free space requirement until after the initial handshaking.         */        if ((packetLen - Record.headerSize) > ea.getAppRemaining()) {            return new SSLEngineResult(Status.BUFFER_OVERFLOW, hsStatus, 0, 0);        }         // check for UNDERFLOW.        if ((packetLen == -1) || (ea.netData.remaining() < packetLen)) {            return new SSLEngineResult(                Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);        }         /*         * We're now ready to actually do the read.         * The only result code we really need to be exactly         * right is the HS finished, for signaling to         * HandshakeCompletedListeners.         */        try {            hsStatus = readRecord(ea);        } catch (SSLException e) {            throw e;        } catch (IOException e) {            SSLException ex = new SSLException("readRecord");            ex.initCause(e);            throw ex;        }         /*         * Check the various condition that we could be reporting.         *         * It's *possible* something might have happened between the         * above and now, but it was better to minimally lock "this"         * during the read process.  We'll return the current         * status, which is more representative of the current state.         *         * status above should cover:  FINISHED, NEED_TASK         */        status = (isInboundDone() ? Status.CLOSED : Status.OK);        hsStatus = getHSStatus(hsStatus);         return new SSLEngineResult(status, hsStatus,            ea.deltaNet(), ea.deltaApp());    }     /*     * Actually do the read record processing.     *     * Returns a Status if it can make specific determinations     * of the engine state.  In particular, we need to signal     * that a handshake just completed.     *     * It would be nice to be symmetrical with the write side and move     * the majority of this to EngineInputRecord, but there's too much     * SSLEngine state to do that cleanly.  It must still live here.     */    private HandshakeStatus readRecord(EngineArgs ea) throws IOException {         HandshakeStatus hsStatus = null;         /*         * The various operations will return new sliced BB's,         * this will avoid having to worry about positions and         * limits in the netBB.         */        ByteBuffer readBB = null;        ByteBuffer decryptedBB = null;         if (getConnectionState() != cs_ERROR) {             /*             * Read a record ... maybe emitting an alert if we get a             * comprehensible but unsupported "hello" message during             * format checking (e.g. V2).             */            try {                readBB = inputRecord.read(ea.netData);            } catch (IOException e) {                fatal(Alerts.alert_unexpected_message, e);            }             /*             * The basic SSLv3 record protection involves (optional)             * encryption for privacy, and an integrity check ensuring             * data origin authentication.  We do them both here, and             * throw a fatal alert if the integrity check fails.             */            try {                decryptedBB = inputRecord.decrypt(readCipher, readBB);            } catch (BadPaddingException e) {                // RFC 2246 states that decryption_failed should be used                // for this purpose. However, that allows certain attacks,                // so we just send bad record MAC. We also need to make                // sure to always check the MAC to avoid a timing attack                // for the same issue. See paper by Vaudenay et al.                //                // rewind the BB if necessary.                readBB.rewind();                 inputRecord.checkMAC(readMAC, readBB);                 // use the same alert types as for MAC failure below                byte alertType = (inputRecord.contentType() ==                    Record.ct_handshake) ?                        Alerts.alert_handshake_failure :                        Alerts.alert_bad_record_mac;                fatal(alertType, "Invalid padding", e);            }             if (!inputRecord.checkMAC(readMAC, decryptedBB)) {                if (inputRecord.contentType() == Record.ct_handshake) {                    fatal(Alerts.alert_handshake_failure,                        "bad handshake record MAC");                } else {                    fatal(Alerts.alert_bad_record_mac, "bad record MAC");                }            }             // if (!inputRecord.decompress(c))            //     fatal(Alerts.alert_decompression_failure,            //     "decompression failure");              /*             * Process the record.             */             synchronized (this) {                switch (inputRecord.contentType()) {                case Record.ct_handshake:                    /*                     * Handshake messages always go to a pending session                     * handshaker ... if there isn't one, create one.  This                     * must work asynchronously, for renegotiation.                     *                     * NOTE that handshaking will either resume a session                     * which was in the cache (and which might have other                     * connections in it already), or else will start a new                     * session (new keys exchanged) with just this connection                     * in it.                     */                    initHandshaker();                    if (!handshaker.activated()) {                        // prior to handshaking, activate the handshake

Lines 501–1000 of 2073. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t