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 Load more
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(); } }