2493 lines · 91.5 KB · Java
RawDownload
        init(context, false);    }      /**     * Layer SSL traffic over an existing connection, rather than creating     * a new connection.  The existing connection may be used only for SSL     * traffic (using this SSLSocket) until the SSLSocket.close() call     * returns. However, if a protocol error is detected, that existing     * connection is automatically closed.     *     * <P> This particular constructor always uses the socket in the     * role of an SSL client. It may be useful in cases which start     * using SSL after some initial data transfers, for example in some     * SSL tunneling applications or as part of some kinds of application     * protocols which negotiate use of a SSL based security.     *     * @param sock the existing connection     * @param context the authentication context to use     */    SSLSocketImpl(SSLContextImpl context, Socket sock, String host,            int port, boolean autoClose) throws IOException {        super(sock);        // We always layer over a connected socket        if (!sock.isConnected()) {            throw new SocketException("Underlying socket is not connected");        }        this.host = host;        this.rawHostname = host;        init(context, false);        this.autoClose = autoClose;        doneConnect();    }     /**     * Initializes the client socket.     */    private void init(SSLContextImpl context, boolean isServer) {        sslContext = context;        sess = SSLSessionImpl.nullSession;        handshakeSession = null;         /*         * role is as specified, state is START until after         * the low level connection's established.         */        roleIsServer = isServer;        connectionState = cs_START;         /*         * default read and write side cipher and MAC support         *         * Note:  compression support would go here too         */        readCipher = CipherBox.NULL;        readMAC = MAC.NULL;        writeCipher = CipherBox.NULL;        writeMAC = MAC.NULL;         // initial security parameters for secure renegotiation        secureRenegotiation = false;        clientVerifyData = new byte[0];        serverVerifyData = new byte[0];         enabledCipherSuites =                sslContext.getDefaultCipherSuiteList(roleIsServer);        enabledProtocols =                sslContext.getDefaultProtocolList(roleIsServer);         inrec = null;         // save the acc        acc = AccessController.getContext();         input = new AppInputStream(this);        output = new AppOutputStream(this);    }     /**     * Connects this socket to the server with a specified timeout     * value.     *     * This method is either called on an unconnected SSLSocketImpl by the     * application, or it is called in the constructor of a regular     * SSLSocketImpl. If we are layering on top on another socket, then     * this method should not be called, because we assume that the     * underlying socket is already connected by the time it is passed to     * us.     *     * @param   endpoint the <code>SocketAddress</code>     * @param   timeout  the timeout value to be used, 0 is no timeout     * @throws  IOException if an error occurs during the connection     * @throws  SocketTimeoutException if timeout expires before connecting     */    public void connect(SocketAddress endpoint, int timeout)            throws IOException {         if (self != this) {            throw new SocketException("Already connected");        }         if (!(endpoint instanceof InetSocketAddress)) {            throw new SocketException(                                  "Cannot handle non-Inet socket addresses.");        }         super.connect(endpoint, timeout);        doneConnect();    }     /**     * Initialize the handshaker and socket streams.     *     * Called by connect, the layered constructor, and SSLServerSocket.     */    void doneConnect() throws IOException {        /*         * Save the input and output streams.  May be done only after         * java.net actually connects using the socket "self", else         * we get some pretty bizarre failure modes.         */        if (self == this) {            sockInput = super.getInputStream();            sockOutput = super.getOutputStream();        } else {            sockInput = self.getInputStream();            sockOutput = self.getOutputStream();        }         /*         * Move to handshaking state, with pending session initialized         * to defaults and the appropriate kind of handshaker set up.         */        initHandshaker();    }     synchronized private int getConnectionState() {        return connectionState;    }     synchronized private void setConnectionState(int state) {        connectionState = state;    }     AccessControlContext getAcc() {        return acc;    }     //    // READING AND WRITING RECORDS    //     /*     * Record Output. Application data can't be sent until the first     * handshake establishes a session.     *     * NOTE:  we let empty records be written as a hook to force some     * TCP-level activity, notably handshaking, to occur.     */    void writeRecord(OutputRecord r) throws IOException {        /*         * The loop is in case of HANDSHAKE --> ERROR transitions, etc         */    loop:        while (r.contentType() == Record.ct_application_data) {            /*             * Not all states support passing application data.  We             * synchronize access to the connection state, so that             * synchronous handshakes can complete cleanly.             */            switch (getConnectionState()) {             /*             * We've deferred the initial handshaking till just now,             * when presumably a thread's decided it's OK to block for             * longish periods of time for I/O purposes (as well as             * configured the cipher suites it wants to use).             */            case cs_HANDSHAKE:                performInitialHandshake();                break;             case cs_DATA:            case cs_RENEGOTIATE:                break loop;             case cs_ERROR:                fatal(Alerts.alert_close_notify,                    "error while writing to socket");                break; // dummy             case cs_SENT_CLOSE:            case cs_CLOSED:            case cs_APP_CLOSED:                // we should never get here (check in AppOutputStream)                // this is just a fallback                if (closeReason != null) {                    throw closeReason;                } else {                    throw new SocketException("Socket closed");                }             /*             * Else something's goofy in this state machine's use.             */            default:                throw new SSLProtocolException("State error, send app data");            }        }         //        // Don't bother to really write empty records.  We went this        // far to drive the handshake machinery, for correctness; not        // writing empty records improves performance by cutting CPU        // time and network resource usage.  However, some protocol        // implementations are fragile and don't like to see empty        // records, so this also increases robustness.        //        if (!r.isEmpty()) {             // If the record is a close notify alert, we need to honor            // socket option SO_LINGER. Note that we will try to send            // the close notify even if the SO_LINGER set to zero.            if (r.isAlert(Alerts.alert_close_notify) && getSoLinger() >= 0) {                 // keep and clear the current thread interruption status.                boolean interrupted = Thread.interrupted();                try {                    if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {                        try {                            writeRecordInternal(r);                        } finally {                            writeLock.unlock();                        }                    } else {                        SSLException ssle = new SSLException(                                "SO_LINGER timeout," +                                " close_notify message cannot be sent.");                          // For layered, non-autoclose sockets, we are not                        // able to bring them into a usable state, so we                        // treat it as fatal error.                        if (self != this && !autoClose) {                            // Note that the alert description is                            // specified as -1, so no message will be send                            // to peer anymore.                            fatal((byte)(-1), ssle);                        } else if ((debug != null) && Debug.isOn("ssl")) {                            System.out.println(threadName() +                                ", received Exception: " + ssle);                        }                         // RFC2246 requires that the session becomes                        // unresumable if any connection is terminated                        // without proper close_notify messages with                        // level equal to warning.                        //                        // RFC4346 no longer requires that a session not be                        // resumed if failure to properly close a connection.                        //                        // We choose to make the session unresumable if                        // failed to send the close_notify message.                        //                        sess.invalidate();                    }                } catch (InterruptedException ie) {                    // keep interrupted status                    interrupted = true;                }                 // restore the interrupted status                if (interrupted) {                    Thread.currentThread().interrupt();                }            } else {                writeLock.lock();                try {                    writeRecordInternal(r);                } finally {                    writeLock.unlock();                }            }        }    }     private void writeRecordInternal(OutputRecord r) throws IOException {        // r.compress(c);        r.addMAC(writeMAC);        r.encrypt(writeCipher);        r.write(sockOutput);         /*         * 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) {            checkSequenceNumber(writeMAC, r.contentType());        }    }      /*     * Read an application data record.  Alerts and handshake     * messages are handled directly.     */    void readDataRecord(InputRecord r) throws IOException {        if (getConnectionState() == cs_HANDSHAKE) {            performInitialHandshake();        }        readRecord(r, true);    }      /*     * Clear the pipeline of records from the peer, optionally returning     * application data.   Caller is responsible for knowing that it's     * possible to do this kind of clearing, if they don't want app     * data -- e.g. since it's the initial SSL handshake.     *     * Don't synchronize (this) during a blocking read() since it     * protects data which is accessed on the write side as well.     */    private void readRecord(InputRecord r, boolean needAppData)            throws IOException {        int state;         // readLock protects reading and processing of an InputRecord.        // It keeps the reading from sockInput and processing of the record        // atomic so that no two threads can be blocked on the        // read from the same input stream at the same time.        // This is required for example when a reader thread is        // blocked on the read and another thread is trying to        // close the socket. For a non-autoclose, layered socket,        // the thread performing the close needs to read the close_notify.        //        // Use readLock instead of 'this' for locking because        // 'this' also protects data accessed during writing.      synchronized (readLock) {        /*         * Read and handle records ... return application data         * ONLY if it's needed.         */         while (((state = getConnectionState()) != cs_CLOSED) &&                (state != cs_ERROR) && (state != cs_APP_CLOSED)) {            /*             * Read a record ... maybe emitting an alert if we get a             * comprehensible but unsupported "hello" message during             * format checking (e.g. V2).             */            try {                r.setAppDataValid(false);                r.read(sockInput, sockOutput);            } catch (SSLProtocolException e) {                try {                    fatal(Alerts.alert_unexpected_message, e);                } catch (IOException x) {                    // discard this exception                }                throw e;            } catch (EOFException eof) {                boolean handshaking = (getConnectionState() <= cs_HANDSHAKE);                boolean rethrow = requireCloseNotify || handshaking;                if ((debug != null) && Debug.isOn("ssl")) {                    System.out.println(threadName() +                        ", received EOFException: "                        + (rethrow ? "error" : "ignored"));                }                if (rethrow) {                    SSLException e;                    if (handshaking) {                        e = new SSLHandshakeException                            ("Remote host closed connection during handshake");                    } else {                        e = new SSLProtocolException                            ("Remote host closed connection incorrectly");                    }                    e.initCause(eof);                    throw e;                } else {                    // treat as if we had received a close_notify                    closeInternal(false);                    continue;                }            }              /*             * 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 {                r.decrypt(readCipher);            } 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.                r.checkMAC(readMAC);                // use the same alert types as for MAC failure below                byte alertType = (r.contentType() == Record.ct_handshake)                                        ? Alerts.alert_handshake_failure                                        : Alerts.alert_bad_record_mac;                fatal(alertType, "Invalid padding", e);            }            if (!r.checkMAC(readMAC)) {                if (r.contentType() == Record.ct_handshake) {                    fatal(Alerts.alert_handshake_failure,                        "bad handshake record MAC");                } else {                    fatal(Alerts.alert_bad_record_mac, "bad record MAC");                }            }              // if (!r.decompress(c))            //     fatal(Alerts.alert_decompression_failure,            //         "decompression failure");             /*             * Process the record.             */            synchronized (this) {              switch (r.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                        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(r, 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;                        handshaker = null;                        connectionState = cs_DATA;                         //                        // Tell folk about handshake completion, but do                        // it in a separate thread.                        //                        if (handshakeListeners != null) {                            HandshakeCompletedEvent event =                                new HandshakeCompletedEvent(this, sess);                             Thread t = new NotifyHandshakeThread(                                handshakeListeners.entrySet(), event);                            t.start();                        }                    } 

Lines 501–1000 of 2493. Previous lines