OpenSSLSocketImpl.java revision df349b3eaf4d1fa0643ab722173bc3bf20a266f5
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.apache.harmony.xnet.provider.jsse;
18
19import dalvik.system.BlockGuard;
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.OutputStream;
23import java.net.InetAddress;
24import java.net.Socket;
25import java.net.SocketException;
26import java.security.PrivateKey;
27import java.security.SecureRandom;
28import java.security.cert.CertificateEncodingException;
29import java.security.cert.CertificateException;
30import java.security.cert.X509Certificate;
31import java.util.ArrayList;
32import java.util.concurrent.atomic.AtomicInteger;
33import java.util.logging.Logger;
34import javax.net.ssl.HandshakeCompletedEvent;
35import javax.net.ssl.HandshakeCompletedListener;
36import javax.net.ssl.SSLException;
37import javax.net.ssl.SSLPeerUnverifiedException;
38import javax.net.ssl.SSLSession;
39import javax.security.auth.x500.X500Principal;
40import org.apache.harmony.security.provider.cert.X509CertImpl;
41
42/**
43 * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
44 * <p>
45 * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
46 * later, for example in the package.html or a separate reference document.
47 * <p>
48 * Extensions to SSLSocket include:
49 * <ul>
50 * <li>handshake timeout
51 * <li>compression methods
52 * <li>session tickets
53 * <li>Server Name Indication
54 * </ul>
55 */
56public class OpenSSLSocketImpl
57        extends javax.net.ssl.SSLSocket
58        implements NativeCrypto.SSLHandshakeCallbacks {
59    private int sslNativePointer;
60    private InputStream is;
61    private OutputStream os;
62    private final Object handshakeLock = new Object();
63    private final Object readLock = new Object();
64    private final Object writeLock = new Object();
65    private SSLParametersImpl sslParameters;
66    private String[] enabledProtocols;
67    private String[] enabledCipherSuites;
68    private String[] enabledCompressionMethods;
69    private boolean useSessionTickets;
70    private String hostname;
71    private OpenSSLSessionImpl sslSession;
72    private Socket socket;
73    private boolean autoClose;
74    private boolean handshakeStarted = false;
75
76    /**
77     * Not set to true until the update from native that tells us the
78     * full handshake is complete, since SSL_do_handshake can return
79     * before the handshake is completely done due to
80     * handshake_cutthrough support.
81     */
82    private boolean handshakeCompleted = false;
83
84    private ArrayList<HandshakeCompletedListener> listeners;
85    private int timeout = 0;
86    // BEGIN android-added
87    private int handshakeTimeout = -1;  // -1 = same as timeout; 0 = infinite
88    // END android-added
89    private String wrappedHost;
90    private int wrappedPort;
91
92    private static final AtomicInteger instanceCount = new AtomicInteger(0);
93
94    public static int getInstanceCount() {
95        return instanceCount.get();
96    }
97
98    private static void updateInstanceCount(int amount) {
99        instanceCount.addAndGet(amount);
100    }
101
102    /**
103     * Class constructor with 1 parameter
104     *
105     * @param sslParameters Parameters for the SSL
106     *            context
107     * @throws IOException if network fails
108     */
109    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
110        super();
111        init(sslParameters);
112    }
113
114    /**
115     * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl
116     *
117     * @param sslParameters Parameters for the SSL
118     *            context
119     * @throws IOException if network fails
120     */
121    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
122                                String[] enabledProtocols,
123                                String[] enabledCipherSuites,
124                                String[] enabledCompressionMethods) throws IOException {
125        super();
126        init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods);
127    }
128
129    /**
130     * Class constructor with 3 parameters
131     *
132     * @throws IOException if network fails
133     * @throws java.net.UnknownHostException host not defined
134     */
135    protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
136        throws IOException {
137        super(host, port);
138        init(sslParameters);
139    }
140
141    /**
142     * Class constructor with 3 parameters: 1st is InetAddress
143     *
144     * @throws IOException if network fails
145     * @throws java.net.UnknownHostException host not defined
146     */
147    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
148        throws IOException {
149        super(address, port);
150        init(sslParameters);
151    }
152
153
154    /**
155     * Class constructor with 5 parameters: 1st is host
156     *
157     * @throws IOException if network fails
158     * @throws java.net.UnknownHostException host not defined
159     */
160    protected OpenSSLSocketImpl(String host, int port,
161                                InetAddress clientAddress, int clientPort,
162                                SSLParametersImpl sslParameters)
163        throws IOException {
164        super(host, port, clientAddress, clientPort);
165        init(sslParameters);
166    }
167
168    /**
169     * Class constructor with 5 parameters: 1st is InetAddress
170     *
171     * @throws IOException if network fails
172     * @throws java.net.UnknownHostException host not defined
173     */
174    protected OpenSSLSocketImpl(InetAddress address, int port,
175                                InetAddress clientAddress, int clientPort,
176                                SSLParametersImpl sslParameters)
177        throws IOException {
178        super(address, port, clientAddress, clientPort);
179        init(sslParameters);
180    }
181
182    /**
183     * Constructor with 5 parameters: 1st is socket. Enhances an existing socket
184     * with SSL functionality. Invoked via OpenSSLSocketImplWrapper constructor.
185     *
186     * @throws IOException if network fails
187     */
188    protected OpenSSLSocketImpl(Socket socket, String host, int port,
189            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
190        super();
191        this.socket = socket;
192        this.timeout = socket.getSoTimeout();
193        this.wrappedHost = host;
194        this.wrappedPort = port;
195        this.autoClose = autoClose;
196        init(sslParameters);
197    }
198
199    /**
200     * Initialize the SSL socket and set the certificates for the
201     * future handshaking.
202     */
203    private void init(SSLParametersImpl sslParameters) throws IOException {
204        init(sslParameters,
205             NativeCrypto.getSupportedProtocols(),
206             NativeCrypto.getDefaultCipherSuites(),
207             NativeCrypto.getDefaultCompressionMethods());
208    }
209
210    /**
211     * Initialize the SSL socket and set the certificates for the
212     * future handshaking.
213     */
214    private void init(SSLParametersImpl sslParameters,
215                      String[] enabledProtocols,
216                      String[] enabledCipherSuites,
217                      String[] enabledCompressionMethods) throws IOException {
218        this.sslParameters = sslParameters;
219        this.enabledProtocols = enabledProtocols;
220        this.enabledCipherSuites = enabledCipherSuites;
221        this.enabledCompressionMethods = enabledCompressionMethods;
222        updateInstanceCount(1);
223    }
224
225    /**
226     * Gets the suitable session reference from the session cache container.
227     *
228     * @return OpenSSLSessionImpl
229     */
230    private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
231        if (super.getInetAddress() == null ||
232                super.getInetAddress().getHostAddress() == null ||
233                super.getInetAddress().getHostName() == null) {
234            return null;
235        }
236        OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(
237                super.getInetAddress().getHostName(),
238                super.getPort());
239        if (session == null) {
240            return null;
241        }
242
243        String protocol = session.getProtocol();
244        boolean protocolFound = false;
245        for (String enabledProtocol : enabledProtocols) {
246            if (protocol.equals(enabledProtocol)) {
247                protocolFound = true;
248                break;
249            }
250        }
251        if (!protocolFound) {
252            return null;
253        }
254
255        String cipherSuite = session.getCipherSuite();
256        boolean cipherSuiteFound = false;
257        for (String enabledCipherSuite : enabledCipherSuites) {
258            if (cipherSuite.equals(enabledCipherSuite)) {
259                cipherSuiteFound = true;
260                break;
261            }
262        }
263        if (!cipherSuiteFound) {
264            return null;
265        }
266
267        String compressionMethod = session.getCompressionMethod();
268        boolean compressionMethodFound = false;
269        for (String enabledCompressionMethod : enabledCompressionMethods) {
270            if (compressionMethod.equals(enabledCompressionMethod)) {
271                compressionMethodFound = true;
272                break;
273            }
274        }
275        if (!compressionMethodFound) {
276            return null;
277        }
278
279        return session;
280    }
281
282    /**
283     * Ensures that logger is lazily loaded. The outer class seems to load
284     * before logging is ready.
285     */
286    static class LoggerHolder {
287        static final Logger logger = Logger.getLogger(OpenSSLSocketImpl.class.getName());
288    }
289
290    /**
291     * Starts a TLS/SSL handshake on this connection using some native methods
292     * from the OpenSSL library. It can negotiate new encryption keys, change
293     * cipher suites, or initiate a new session. The certificate chain is
294     * verified if the correspondent property in java.Security is set. All
295     * listeners are notified at the end of the TLS/SSL handshake.
296     *
297     * @throws <code>IOException</code> if network fails
298     */
299    @Override
300    public void startHandshake() throws IOException {
301        startHandshake(true);
302    }
303
304    /**
305     * Checks whether the socket is closed, and throws an exception.
306     *
307     * @throws SocketException
308     *             if the socket is closed.
309     */
310    private void checkOpen() throws SocketException {
311        if (isClosed()) {
312            throw new SocketException("Socket is closed");
313        }
314    }
315
316    /**
317     * Perform the handshake
318     * @param full If true, disable handshake cutthrough for a fully synchronous handshake
319     */
320    public synchronized void startHandshake(boolean full) throws IOException {
321        checkOpen();
322        synchronized (handshakeLock) {
323            if (!handshakeStarted) {
324                handshakeStarted = true;
325            } else {
326                return;
327            }
328        }
329
330        // note that this modifies the global seed, not something specific to the connection
331        final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
332        final SecureRandom secureRandom = sslParameters.getSecureRandomMember();
333        if (secureRandom == null) {
334            NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
335        } else {
336            NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes));
337        }
338
339        final boolean client = sslParameters.getUseClientMode();
340
341        final int sslCtxNativePointer = (client) ?
342            sslParameters.getClientSessionContext().sslCtxNativePointer :
343            sslParameters.getServerSessionContext().sslCtxNativePointer;
344
345        this.sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
346
347        // setup server certificates and private keys.
348        // clients will receive a call back to request certificates.
349        if (!client) {
350            for (String keyType : NativeCrypto.KEY_TYPES) {
351                try {
352                    setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
353                                                                                   null,
354                                                                                   this));
355                } catch (CertificateEncodingException e) {
356                    throw new IOException(e);
357                }
358            }
359        }
360
361        NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
362        NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
363        if (enabledCompressionMethods.length != 0) {
364            NativeCrypto.setEnabledCompressionMethods(sslNativePointer, enabledCompressionMethods);
365        }
366        if (useSessionTickets) {
367            NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
368        }
369        if (hostname != null) {
370            NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname);
371        }
372
373        boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
374        if (!enableSessionCreation) {
375            NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
376                                                          enableSessionCreation);
377        }
378
379        AbstractSessionContext sessionContext;
380        OpenSSLSessionImpl session;
381        if (client) {
382            // look for client session to reuse
383            ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
384            sessionContext = clientSessionContext;
385            session = getCachedClientSession(clientSessionContext);
386            if (session != null) {
387                NativeCrypto.SSL_set_session(sslNativePointer,  session.sslSessionNativePointer);
388            }
389        } else {
390            sessionContext = sslParameters.getServerSessionContext();
391            session = null;
392        }
393
394        // setup peer certificate verification
395        if (client) {
396            // TODO support for anonymous cipher would require us to
397            // conditionally use SSL_VERIFY_NONE
398        } else {
399            // needing client auth takes priority...
400            boolean certRequested = false;
401            if (sslParameters.getNeedClientAuth()) {
402                NativeCrypto.SSL_set_verify(sslNativePointer,
403                                            NativeCrypto.SSL_VERIFY_PEER
404                                            | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
405                certRequested = true;
406            // ... over just wanting it...
407            } else if (sslParameters.getWantClientAuth()) {
408                NativeCrypto.SSL_set_verify(sslNativePointer,
409                                            NativeCrypto.SSL_VERIFY_PEER);
410                certRequested = true;
411            // ... and it defaults properly so we don't need call SSL_set_verify in the common case.
412            } else {
413                certRequested = false;
414            }
415
416            if (certRequested) {
417                X509Certificate[] issuers = sslParameters.getTrustManager().getAcceptedIssuers();
418                if (issuers != null) {
419                    byte[][] issuersBytes;
420                    try {
421                        issuersBytes = NativeCrypto.encodeIssuerX509Principals(issuers);
422                    } catch (CertificateEncodingException e) {
423                        throw new IOException("Problem encoding principals", e);
424                    }
425                    NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
426                }
427            }
428        }
429
430        if (client && full) {
431            // we want to do a full synchronous handshake, so turn off cutthrough
432            NativeCrypto.SSL_clear_mode(sslNativePointer,
433                                        NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
434        }
435
436        // BEGIN android-added
437        // Temporarily use a different timeout for the handshake process
438        int savedTimeout = timeout;
439        if (handshakeTimeout >= 0) {
440            setSoTimeout(handshakeTimeout);
441        }
442        // END android-added
443
444
445        Socket socket = this.socket != null ? this.socket : this;
446        int sslSessionNativePointer;
447        try {
448            sslSessionNativePointer
449                = NativeCrypto.SSL_do_handshake(sslNativePointer, socket, this, timeout, client);
450        } catch (CertificateException e) {
451            throw new SSLPeerUnverifiedException(e.getMessage());
452        }
453        byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
454        sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId);
455        if (sslSession != null) {
456            sslSession.lastAccessedTime = System.currentTimeMillis();
457            LoggerHolder.logger.fine("Reused cached session for "
458                                     + getInetAddress() + ".");
459            NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
460        } else {
461            if (!enableSessionCreation) {
462                // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
463                throw new IllegalStateException("SSL Session may not be created");
464            }
465            X509Certificate[] localCertificates
466                    = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
467            X509Certificate[] peerCertificates
468                    = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
469            if (wrappedHost == null) {
470                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
471                                                    localCertificates, peerCertificates,
472                                                    super.getInetAddress().getHostName(),
473                                                    super.getPort(), sessionContext);
474            } else  {
475                sslSession = new OpenSSLSessionImpl(sslSessionNativePointer,
476                                                    localCertificates, peerCertificates,
477                                                    wrappedHost, wrappedPort,
478                                                    sessionContext);
479            }
480            // if not, putSession later in handshakeCompleted() callback
481            if (handshakeCompleted) {
482                sessionContext.putSession(sslSession);
483            }
484            LoggerHolder.logger.fine("Created new session for "
485                                     + getInetAddress().getHostName() + ".");
486        }
487
488        // BEGIN android-added
489        // Restore the original timeout now that the handshake is complete
490        if (handshakeTimeout >= 0) {
491            setSoTimeout(savedTimeout);
492        }
493        // END android-added
494
495        // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
496        if (handshakeCompleted) {
497            notifyHandshakeCompletedListeners();
498        }
499    }
500
501    /**
502     * Return a possibly null array of X509Certificates given the
503     * possibly null array of DER encoded bytes.
504     */
505    private static final X509Certificate[] createCertChain(byte[][] certificatesBytes) {
506        if (certificatesBytes == null) {
507            return null;
508        }
509        X509Certificate[] certificates = new X509Certificate[certificatesBytes.length];
510        for (int i = 0; i < certificatesBytes.length; i++) {
511            try {
512                certificates[i] = new X509CertImpl(certificatesBytes[i]);
513            } catch (IOException e) {
514                return null;
515            }
516        }
517        return certificates;
518    }
519
520    private void setCertificate(String alias) throws CertificateEncodingException, SSLException {
521        if (alias == null) {
522            return;
523        }
524
525        PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
526        byte[] privateKeyBytes = privateKey.getEncoded();
527        NativeCrypto.SSL_use_PrivateKey(sslNativePointer, privateKeyBytes);
528
529        X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
530        byte[][] certificateBytes = NativeCrypto.encodeCertificates(certificates);
531        NativeCrypto.SSL_use_certificate(sslNativePointer, certificateBytes);
532
533        // checks the last installed private key and certificate,
534        // so need to do this once per loop iteration
535        NativeCrypto.SSL_check_private_key(sslNativePointer);
536    }
537
538    /**
539     * Implementation of NativeCrypto.SSLHandshakeCallbacks
540     * invoked via JNI from client_cert_cb
541     */
542    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
543            throws CertificateEncodingException, SSLException {
544
545        String[] keyTypes = new String[keyTypeBytes.length];
546        for (int i = 0; i < keyTypeBytes.length; i++) {
547            keyTypes[i] = NativeCrypto.keyType(keyTypeBytes[i]);
548        }
549
550        X500Principal[] issuers;
551        if (asn1DerEncodedPrincipals == null) {
552            issuers = null;
553        } else {
554            issuers = new X500Principal[asn1DerEncodedPrincipals.length];
555            for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
556                issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
557            }
558        }
559        setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this));
560    }
561
562    /**
563     * Implementation of NativeCrypto.SSLHandshakeCallbacks
564     * invoked via JNI from info_callback
565     */
566    public void handshakeCompleted() {
567        handshakeCompleted = true;
568
569        // If sslSession is null, the handshake was completed during
570        // the call to NativeCrypto.SSL_do_handshake and not during a
571        // later read operation. That means we do not need to fixup
572        // the SSLSession and session cache or notify
573        // HandshakeCompletedListeners, it will be done in
574        // startHandshake.
575        if (sslSession == null) {
576            return;
577        }
578
579        // reset session id from the native pointer and update the
580        // appropriate cache.
581        sslSession.resetId();
582        AbstractSessionContext sessionContext =
583            (sslParameters.getUseClientMode())
584            ? sslParameters.getClientSessionContext()
585                : sslParameters.getServerSessionContext();
586        sessionContext.putSession(sslSession);
587
588        // let listeners know we are finally done
589        notifyHandshakeCompletedListeners();
590    }
591
592    private void notifyHandshakeCompletedListeners() {
593        if (listeners != null && !listeners.isEmpty()) {
594            // notify the listeners
595            HandshakeCompletedEvent event =
596                new HandshakeCompletedEvent(this, sslSession);
597            for (HandshakeCompletedListener listener : listeners) {
598                try {
599                    listener.handshakeCompleted(event);
600                } catch (RuntimeException e) {
601                    // The RI runs the handlers in a separate thread,
602                    // which we do not. But we try to preserve their
603                    // behavior of logging a problem and not killing
604                    // the handshaking thread just because a listener
605                    // has a problem.
606                    Thread thread = Thread.currentThread();
607                    thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
608                }
609            }
610        }
611    }
612
613    /**
614     * Implementation of NativeCrypto.SSLHandshakeCallbacks
615     *
616     * @param bytes An array of ASN.1 DER encoded certficates
617     * @param authMethod auth algorithm name
618     *
619     * @throws CertificateException if the certificate is untrusted
620     */
621    @SuppressWarnings("unused")
622    public void verifyCertificateChain(byte[][] bytes, String authMethod)
623            throws CertificateException {
624        try {
625            if (bytes == null || bytes.length == 0) {
626                throw new SSLException("Peer sent no certificate");
627            }
628            X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length];
629            for (int i = 0; i < bytes.length; i++) {
630                peerCertificateChain[i] =
631                    new X509CertImpl(
632                        javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded());
633            }
634            boolean client = sslParameters.getUseClientMode();
635            if (client) {
636                sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain,
637                                                                   authMethod);
638            } else {
639                sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain,
640                                                                   authMethod);
641            }
642
643        } catch (CertificateException e) {
644            throw e;
645        } catch (Exception e) {
646            throw new RuntimeException(e);
647        }
648    }
649
650    /**
651     * Returns an input stream for this SSL socket using native calls to the
652     * OpenSSL library.
653     *
654     * @return: an input stream for reading bytes from this socket.
655     * @throws: <code>IOException</code> if an I/O error occurs when creating
656     *          the input stream, the socket is closed, the socket is not
657     *          connected, or the socket input has been shutdown.
658     */
659    @Override
660    public InputStream getInputStream() throws IOException {
661        checkOpen();
662        synchronized (this) {
663            if (is == null) {
664                is = new SSLInputStream();
665            }
666
667            return is;
668        }
669    }
670
671    /**
672     * Returns an output stream for this SSL socket using native calls to the
673     * OpenSSL library.
674     *
675     * @return an output stream for writing bytes to this socket.
676     * @throws <code>IOException</code> if an I/O error occurs when creating
677     *             the output stream, or no connection to the socket exists.
678     */
679    @Override
680    public OutputStream getOutputStream() throws IOException {
681        checkOpen();
682        synchronized (this) {
683            if (os == null) {
684                os = new SSLOutputStream();
685            }
686
687            return os;
688        }
689    }
690
691    /**
692     * This method is not supported for this SSLSocket implementation
693     * because reading from an SSLSocket may involve writing to the
694     * network.
695     */
696    @Override
697    public void shutdownInput() throws IOException {
698        throw new UnsupportedOperationException();
699    }
700
701    /**
702     * This method is not supported for this SSLSocket implementation
703     * because writing to an SSLSocket may involve reading from the
704     * network.
705     */
706    @Override
707    public void shutdownOutput() throws IOException {
708        throw new UnsupportedOperationException();
709    }
710
711    /**
712     * This inner class provides input data stream functionality
713     * for the OpenSSL native implementation. It is used to
714     * read data received via SSL protocol.
715     */
716    private class SSLInputStream extends InputStream {
717        SSLInputStream() throws IOException {
718            /**
719            /* Note: When startHandshake() throws an exception, no
720             * SSLInputStream object will be created.
721             */
722            OpenSSLSocketImpl.this.startHandshake(false);
723        }
724
725        /**
726         * Reads one byte. If there is no data in the underlying buffer,
727         * this operation can block until the data will be
728         * available.
729         * @return read value.
730         * @throws <code>IOException</code>
731         */
732        @Override
733        public int read() throws IOException {
734            checkOpen();
735            BlockGuard.getThreadPolicy().onNetwork();
736            synchronized (readLock) {
737                return NativeCrypto.SSL_read_byte(sslNativePointer, timeout);
738            }
739        }
740
741        /**
742         * Method acts as described in spec for superclass.
743         * @see java.io.InputStream#read(byte[],int,int)
744         */
745        @Override
746        public int read(byte[] b, int off, int len) throws IOException {
747            checkOpen();
748            BlockGuard.getThreadPolicy().onNetwork();
749            if (b == null) {
750                throw new NullPointerException("b == null");
751            }
752            if ((len | off) < 0 || len > b.length - off) {
753                throw new IndexOutOfBoundsException();
754            }
755            if (0 == len) {
756                return 0;
757            }
758            synchronized (readLock) {
759                return NativeCrypto.SSL_read(sslNativePointer, b, off, len, timeout);
760            }
761        }
762    }
763
764    /**
765     * This inner class provides output data stream functionality
766     * for the OpenSSL native implementation. It is used to
767     * write data according to the encryption parameters given in SSL context.
768     */
769    private class SSLOutputStream extends OutputStream {
770        SSLOutputStream() throws IOException {
771            /**
772            /* Note: When startHandshake() throws an exception, no
773             * SSLOutputStream object will be created.
774             */
775            OpenSSLSocketImpl.this.startHandshake(false);
776        }
777
778        /**
779         * Method acts as described in spec for superclass.
780         * @see java.io.OutputStream#write(int)
781         */
782        @Override
783        public void write(int b) throws IOException {
784            checkOpen();
785            BlockGuard.getThreadPolicy().onNetwork();
786            synchronized (writeLock) {
787                NativeCrypto.SSL_write_byte(sslNativePointer, b);
788            }
789        }
790
791        /**
792         * Method acts as described in spec for superclass.
793         * @see java.io.OutputStream#write(byte[],int,int)
794         */
795        @Override
796        public void write(byte[] b, int start, int len) throws IOException {
797            checkOpen();
798            BlockGuard.getThreadPolicy().onNetwork();
799            if (b == null) {
800                throw new NullPointerException("b == null");
801            }
802            if ((len | start) < 0 || len > b.length - start) {
803                throw new IndexOutOfBoundsException();
804            }
805            if (len == 0) {
806                return;
807            }
808            synchronized (writeLock) {
809                NativeCrypto.SSL_write(sslNativePointer, b, start, len);
810            }
811        }
812    }
813
814
815    /**
816     * The SSL session used by this connection is returned. The SSL session
817     * determines which cipher suite should be used by all connections within
818     * that session and which identities have the session's client and server.
819     * This method starts the SSL handshake.
820     * @return the SSLSession.
821     * @throws <code>IOException</code> if the handshake fails
822     */
823    @Override
824    public SSLSession getSession() {
825        if (sslSession == null) {
826            try {
827                startHandshake(true);
828            } catch (IOException e) {
829
830                // return an invalid session with
831                // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
832                return SSLSessionImpl.NULL_SESSION;
833            }
834        }
835        return sslSession;
836    }
837
838    /**
839     * Registers a listener to be notified that a SSL handshake
840     * was successfully completed on this connection.
841     * @throws <code>IllegalArgumentException</code> if listener is null.
842     */
843    @Override
844    public void addHandshakeCompletedListener(
845            HandshakeCompletedListener listener) {
846        if (listener == null) {
847            throw new IllegalArgumentException("Provided listener is null");
848        }
849        if (listeners == null) {
850            listeners = new ArrayList();
851        }
852        listeners.add(listener);
853    }
854
855    /**
856     * The method removes a registered listener.
857     * @throws IllegalArgumentException if listener is null or not registered
858     */
859    @Override
860    public void removeHandshakeCompletedListener(
861            HandshakeCompletedListener listener) {
862        if (listener == null) {
863            throw new IllegalArgumentException("Provided listener is null");
864        }
865        if (listeners == null) {
866            throw new IllegalArgumentException(
867                    "Provided listener is not registered");
868        }
869        if (!listeners.remove(listener)) {
870            throw new IllegalArgumentException(
871                    "Provided listener is not registered");
872        }
873    }
874
875    /**
876     * Returns true if new SSL sessions may be established by this socket.
877     *
878     * @return true if the session may be created; false if a session already
879     *         exists and must be resumed.
880     */
881    @Override
882    public boolean getEnableSessionCreation() {
883        return sslParameters.getEnableSessionCreation();
884    }
885
886    /**
887     * Set a flag for the socket to inhibit or to allow the creation of a new
888     * SSL sessions. If the flag is set to false, and there are no actual
889     * sessions to resume, then there will be no successful handshaking.
890     *
891     * @param flag true if session may be created; false
892     *            if a session already exists and must be resumed.
893     */
894    @Override
895    public void setEnableSessionCreation(boolean flag) {
896        sslParameters.setEnableSessionCreation(flag);
897    }
898
899    /**
900     * The names of the cipher suites which could be used by the SSL connection
901     * are returned.
902     * @return an array of cipher suite names
903     */
904    @Override
905    public String[] getSupportedCipherSuites() {
906        return NativeCrypto.getSupportedCipherSuites();
907    }
908
909    /**
910     * The names of the cipher suites that are in use in the actual the SSL
911     * connection are returned.
912     *
913     * @return an array of cipher suite names
914     */
915    @Override
916    public String[] getEnabledCipherSuites() {
917        return enabledCipherSuites.clone();
918    }
919
920    /**
921     * This method enables the cipher suites listed by
922     * getSupportedCipherSuites().
923     *
924     * @param suites names of all the cipher suites to
925     *            put on use
926     * @throws IllegalArgumentException when one or more of the
927     *             ciphers in array suites are not supported, or when the array
928     *             is null.
929     */
930    @Override
931    public void setEnabledCipherSuites(String[] suites) {
932        enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
933    }
934
935    /**
936     * The names of the protocols' versions that may be used on this SSL
937     * connection.
938     * @return an array of protocols names
939     */
940    @Override
941    public String[] getSupportedProtocols() {
942        return NativeCrypto.getSupportedProtocols();
943    }
944
945    /**
946     * The names of the protocols' versions that are in use on this SSL
947     * connection.
948     *
949     * @return an array of protocols names
950     */
951    @Override
952    public String[] getEnabledProtocols() {
953        return enabledProtocols.clone();
954    }
955
956    /**
957     * This method enables the protocols' versions listed by
958     * getSupportedProtocols().
959     *
960     * @param protocols The names of all the protocols to allow
961     *
962     * @throws IllegalArgumentException when one or more of the names in the
963     *             array are not supported, or when the array is null.
964     */
965    @Override
966    public void setEnabledProtocols(String[] protocols) {
967        enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
968    }
969
970    /**
971     * The names of the compression methods that may be used on this SSL
972     * connection.
973     * @return an array of compression methods
974     */
975    public String[] getSupportedCompressionMethods() {
976        return NativeCrypto.getSupportedCompressionMethods();
977    }
978
979    /**
980     * The names of the compression methods versions that are in use
981     * on this SSL connection.
982     *
983     * @return an array of compression methods
984     */
985    public String[] getEnabledCompressionMethods() {
986        return enabledCompressionMethods.clone();
987    }
988
989    /**
990     * This method enables the compression method listed by
991     * getSupportedCompressionMethods().
992     *
993     * @param methods The names of all the compression methods to allow
994     *
995     * @throws IllegalArgumentException when one or more of the names in the
996     *             array are not supported, or when the array is null.
997     */
998    public void setEnabledCompressionMethods (String[] methods) {
999        enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods);
1000    }
1001
1002    /**
1003     * This method enables session ticket support.
1004     *
1005     * @param useSessionTickets True to enable session tickets
1006     */
1007    public void setUseSessionTickets(boolean useSessionTickets) {
1008        this.useSessionTickets = useSessionTickets;
1009    }
1010
1011    /**
1012     * This method gives true back if the SSL socket is set to client mode.
1013     *
1014     * @return true if the socket should do the handshaking as client.
1015     */
1016    public boolean getUseSessionTickets() {
1017        return useSessionTickets;
1018    }
1019
1020    /**
1021     * This method enables Server Name Indication
1022     *
1023     * @param hostname the desired SNI hostname, or null to disable
1024     */
1025    public void setHostname(String hostname) {
1026        this.hostname = hostname;
1027    }
1028
1029    /**
1030     * This method returns the current SNI hostname
1031     *
1032     * @return a host name if SNI is enabled, or null otherwise
1033     */
1034    public String getHostname() {
1035        return hostname;
1036    }
1037
1038    /**
1039     * This method gives true back if the SSL socket is set to client mode.
1040     *
1041     * @return true if the socket should do the handshaking as client.
1042     */
1043    public boolean getUseClientMode() {
1044        return sslParameters.getUseClientMode();
1045    }
1046
1047    /**
1048     * This method set the actual SSL socket to client mode.
1049     *
1050     * @param mode true if the socket starts in client
1051     *            mode
1052     * @throws IllegalArgumentException if mode changes during
1053     *             handshake.
1054     */
1055    @Override
1056    public void setUseClientMode(boolean mode) {
1057        if (handshakeStarted) {
1058            throw new IllegalArgumentException(
1059            "Could not change the mode after the initial handshake has begun.");
1060        }
1061        sslParameters.setUseClientMode(mode);
1062    }
1063
1064    /**
1065     * Returns true if the SSL socket requests client's authentication. Relevant
1066     * only for server sockets!
1067     *
1068     * @return true if client authentication is desired, false if not.
1069     */
1070    @Override
1071    public boolean getWantClientAuth() {
1072        return sslParameters.getWantClientAuth();
1073    }
1074
1075    /**
1076     * Returns true if the SSL socket needs client's authentication. Relevant
1077     * only for server sockets!
1078     *
1079     * @return true if client authentication is desired, false if not.
1080     */
1081    @Override
1082    public boolean getNeedClientAuth() {
1083        return sslParameters.getNeedClientAuth();
1084    }
1085
1086    /**
1087     * Sets the SSL socket to use client's authentication. Relevant only for
1088     * server sockets!
1089     *
1090     * @param need true if client authentication is
1091     *            desired, false if not.
1092     */
1093    @Override
1094    public void setNeedClientAuth(boolean need) {
1095        sslParameters.setNeedClientAuth(need);
1096    }
1097
1098    /**
1099     * Sets the SSL socket to use client's authentication. Relevant only for
1100     * server sockets! Notice that in contrast to setNeedClientAuth(..) this
1101     * method will continue the negotiation if the client decide not to send
1102     * authentication credentials.
1103     *
1104     * @param want true if client authentication is
1105     *            desired, false if not.
1106     */
1107    @Override
1108    public void setWantClientAuth(boolean want) {
1109        sslParameters.setWantClientAuth(want);
1110    }
1111
1112    /**
1113     * This method is not supported for SSLSocket implementation.
1114     */
1115    @Override
1116    public void sendUrgentData(int data) throws IOException {
1117        throw new SocketException(
1118                "Method sendUrgentData() is not supported.");
1119    }
1120
1121    /**
1122     * This method is not supported for SSLSocket implementation.
1123     */
1124    @Override
1125    public void setOOBInline(boolean on) throws SocketException {
1126        throw new SocketException(
1127                "Methods sendUrgentData, setOOBInline are not supported.");
1128    }
1129
1130    /**
1131     * Set the read timeout on this socket. The SO_TIMEOUT option, is specified
1132     * in milliseconds. The read operation will block indefinitely for a zero
1133     * value.
1134     *
1135     * @param timeout the read timeout value
1136     * @throws SocketException if an error occurs setting the option
1137     */
1138    @Override
1139    public void setSoTimeout(int timeout) throws SocketException {
1140        super.setSoTimeout(timeout);
1141        this.timeout = timeout;
1142    }
1143
1144    // BEGIN android-added
1145    /**
1146     * Set the handshake timeout on this socket.  This timeout is specified in
1147     * milliseconds and will be used only during the handshake process.
1148     *
1149     * @param timeout the handshake timeout value
1150     */
1151    public void setHandshakeTimeout(int timeout) throws SocketException {
1152        this.handshakeTimeout = timeout;
1153    }
1154    // END android-added
1155
1156    /**
1157     * Closes the SSL socket. Once closed, a socket is not available for further
1158     * use anymore under any circumstance. A new socket must be created.
1159     *
1160     * @throws <code>IOException</code> if an I/O error happens during the
1161     *             socket's closure.
1162     */
1163    @Override
1164    public void close() throws IOException {
1165        // TODO: Close SSL sockets using a background thread so they close
1166        // gracefully.
1167
1168        synchronized (handshakeLock) {
1169            if (!handshakeStarted) {
1170                // prevent further attemps to start handshake
1171                handshakeStarted = true;
1172
1173                synchronized (this) {
1174                    free();
1175
1176                    if (socket != null) {
1177                        if (autoClose && !socket.isClosed()) socket.close();
1178                    } else {
1179                        if (!super.isClosed()) super.close();
1180                    }
1181                }
1182
1183                return;
1184            }
1185        }
1186
1187        NativeCrypto.SSL_interrupt(sslNativePointer);
1188
1189        synchronized (this) {
1190            synchronized (writeLock) {
1191                synchronized (readLock) {
1192
1193                    IOException pendingException = null;
1194
1195                    // Shut down the SSL connection, per se.
1196                    try {
1197                        if (handshakeStarted) {
1198                            BlockGuard.getThreadPolicy().onNetwork();
1199                            NativeCrypto.SSL_shutdown(sslNativePointer);
1200                        }
1201                    } catch (IOException ex) {
1202                        /*
1203                         * Note the exception at this point, but try to continue
1204                         * to clean the rest of this all up before rethrowing.
1205                         */
1206                        pendingException = ex;
1207                    }
1208
1209                    /*
1210                     * Even if the above call failed, it is still safe to free
1211                     * the native structs, and we need to do so lest we leak
1212                     * memory.
1213                     */
1214                    free();
1215
1216                    if (socket != null) {
1217                        if (autoClose && !socket.isClosed())
1218                            socket.close();
1219                    } else {
1220                        if (!super.isClosed())
1221                            super.close();
1222                    }
1223
1224                    if (pendingException != null) {
1225                        throw pendingException;
1226                    }
1227                }
1228            }
1229        }
1230    }
1231
1232    private void free() {
1233        if (sslNativePointer == 0) {
1234            return;
1235        }
1236        NativeCrypto.SSL_free(sslNativePointer);
1237        sslNativePointer = 0;
1238    }
1239
1240    @Override
1241    protected void finalize() throws IOException {
1242        /*
1243         * Just worry about our own state. Notably we do not try and
1244         * close anything. The SocketImpl, either our own
1245         * PlainSocketImpl, or the Socket we are wrapping, will do
1246         * that. This might mean we do not properly SSL_shutdown, but
1247         * if you want to do that, properly close the socket yourself.
1248         *
1249         * The reason why we don't try to SSL_shutdown, is that there
1250         * can be a race between finalizers where the PlainSocketImpl
1251         * finalizer runs first and closes the socket. However, in the
1252         * meanwhile, the underlying file descriptor could be reused
1253         * for another purpose. If we call SSL_shutdown, the
1254         * underlying socket BIOs still have the old file descriptor
1255         * and will write the close notify to some unsuspecting
1256         * reader.
1257         */
1258        updateInstanceCount(-1);
1259        free();
1260    }
1261}
1262