OpenSSLSocketImpl.java revision 0e9746b7b132058651155b33f219c7789997985b
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.conscrypt;
18
19import dalvik.system.BlockGuard;
20import dalvik.system.CloseGuard;
21import java.io.FileDescriptor;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25import java.net.InetAddress;
26import java.net.Socket;
27import java.net.SocketException;
28import java.security.InvalidKeyException;
29import java.security.PrivateKey;
30import java.security.SecureRandom;
31import java.security.cert.CertificateEncodingException;
32import java.security.cert.CertificateException;
33import java.security.cert.X509Certificate;
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.HashSet;
37import java.util.Set;
38import javax.net.ssl.HandshakeCompletedEvent;
39import javax.net.ssl.HandshakeCompletedListener;
40import javax.net.ssl.SSLException;
41import javax.net.ssl.SSLHandshakeException;
42import javax.net.ssl.SSLProtocolException;
43import javax.net.ssl.SSLSession;
44import javax.net.ssl.X509TrustManager;
45import javax.security.auth.x500.X500Principal;
46import static libcore.io.OsConstants.*;
47import libcore.io.ErrnoException;
48import libcore.io.Libcore;
49import libcore.io.Streams;
50import libcore.io.StructTimeval;
51
52/**
53 * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
54 * <p>
55 * Extensions to SSLSocket include:
56 * <ul>
57 * <li>handshake timeout
58 * <li>session tickets
59 * <li>Server Name Indication
60 * </ul>
61 */
62public class OpenSSLSocketImpl
63        extends javax.net.ssl.SSLSocket
64        implements NativeCrypto.SSLHandshakeCallbacks {
65
66    /**
67     * Protects handshakeStarted and handshakeCompleted.
68     */
69    private final Object handshakeLock = new Object();
70
71    /**
72     * First thread to try to handshake sets this to true.
73     */
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    /**
85     * Protected by synchronizing on this. Starts as 0, set by
86     * startHandshake, reset to 0 on close.
87     */
88    private long sslNativePointer;
89
90    /**
91     * Protected by synchronizing on this. Starts as null, set by
92     * getInputStream after startHandshake.
93     */
94    private InputStream is;
95
96    /**
97     * Protected by synchronizing on this. Starts as null, set by
98     * getInputStream after startHandshake.
99     */
100    private OutputStream os;
101
102    /**
103     * OpenSSL only lets one thread read at a time, so this is used to
104     * make sure we serialize callers of SSL_read. Thread is already
105     * expected to have completed handshaking.
106     */
107    private final Object readLock = new Object();
108
109    /**
110     * OpenSSL only lets one thread write at a time, so this is used
111     * to make sure we serialize callers of SSL_write. Thread is
112     * already expected to have completed handshaking.
113     */
114    private final Object writeLock = new Object();
115
116    private final Socket socket;
117    private final boolean autoClose;
118    private final String wrappedHost;
119    private final int wrappedPort;
120    private final SSLParametersImpl sslParameters;
121    private final CloseGuard guard = CloseGuard.get();
122
123    private String[] enabledProtocols;
124    private String[] enabledCipherSuites;
125    private byte[] npnProtocols;
126    private byte[] alpnProtocols;
127    private boolean useSessionTickets;
128    private String hostname;
129
130    /**
131     * Whether the TLS Channel ID extension is enabled. This field is
132     * server-side only.
133     */
134    private boolean channelIdEnabled;
135
136    /**
137     * Private key for the TLS Channel ID extension. This field is
138     * client-side only. Set during startHandshake.
139     */
140    private OpenSSLKey channelIdPrivateKey;
141
142    /** Set during startHandshake. */
143    private OpenSSLSessionImpl sslSession;
144
145    private ArrayList<HandshakeCompletedListener> listeners;
146
147    /**
148     * Local cache of timeout to avoid getsockopt on every read and
149     * write for non-wrapped sockets. Note that
150     * OpenSSLSocketImplWrapper overrides setSoTimeout and
151     * getSoTimeout to delegate to the wrapped socket.
152     */
153    private int readTimeoutMilliseconds = 0;
154    private int writeTimeoutMilliseconds = 0;
155
156    private int handshakeTimeoutMilliseconds = -1;  // -1 = same as timeout; 0 = infinite
157
158    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
159        this.socket = this;
160        this.wrappedHost = null;
161        this.wrappedPort = -1;
162        this.autoClose = false;
163        this.sslParameters = sslParameters;
164        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
165        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
166    }
167
168    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
169                                String[] enabledProtocols,
170                                String[] enabledCipherSuites) throws IOException {
171        this.socket = this;
172        this.wrappedHost = null;
173        this.wrappedPort = -1;
174        this.autoClose = false;
175        this.sslParameters = sslParameters;
176        this.enabledProtocols = enabledProtocols;
177        this.enabledCipherSuites = enabledCipherSuites;
178    }
179
180    protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
181            throws IOException {
182        super(host, port);
183        this.socket = this;
184        this.wrappedHost = null;
185        this.wrappedPort = -1;
186        this.autoClose = false;
187        this.sslParameters = sslParameters;
188        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
189        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
190    }
191
192    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
193            throws IOException {
194        super(address, port);
195        this.socket = this;
196        this.wrappedHost = null;
197        this.wrappedPort = -1;
198        this.autoClose = false;
199        this.sslParameters = sslParameters;
200        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
201        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
202    }
203
204
205    protected OpenSSLSocketImpl(String host, int port,
206                                InetAddress clientAddress, int clientPort,
207                                SSLParametersImpl sslParameters) throws IOException {
208        super(host, port, clientAddress, clientPort);
209        this.socket = this;
210        this.wrappedHost = null;
211        this.wrappedPort = -1;
212        this.autoClose = false;
213        this.sslParameters = sslParameters;
214        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
215        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
216    }
217
218    protected OpenSSLSocketImpl(InetAddress address, int port,
219                                InetAddress clientAddress, int clientPort,
220                                SSLParametersImpl sslParameters) throws IOException {
221        super(address, port, clientAddress, clientPort);
222        this.socket = this;
223        this.wrappedHost = null;
224        this.wrappedPort = -1;
225        this.autoClose = false;
226        this.sslParameters = sslParameters;
227        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
228        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
229    }
230
231    /**
232     * Create an SSL socket that wraps another socket. Invoked by
233     * OpenSSLSocketImplWrapper constructor.
234     */
235    protected OpenSSLSocketImpl(Socket socket, String host, int port,
236            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
237        this.socket = socket;
238        this.wrappedHost = host;
239        this.wrappedPort = port;
240        this.autoClose = autoClose;
241        this.sslParameters = sslParameters;
242        this.enabledProtocols = NativeCrypto.getDefaultProtocols();
243        this.enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
244
245        // this.timeout is not set intentionally.
246        // OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
247        // to wrapped socket
248    }
249
250    /**
251     * Gets the suitable session reference from the session cache container.
252     */
253    private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
254        String hostName = getPeerHostName();
255        int port = getPeerPort();
256        if (hostName == null) {
257            return null;
258        }
259        OpenSSLSessionImpl session = (OpenSSLSessionImpl) sessionContext.getSession(hostName, port);
260        if (session == null) {
261            return null;
262        }
263
264        String protocol = session.getProtocol();
265        boolean protocolFound = false;
266        for (String enabledProtocol : enabledProtocols) {
267            if (protocol.equals(enabledProtocol)) {
268                protocolFound = true;
269                break;
270            }
271        }
272        if (!protocolFound) {
273            return null;
274        }
275
276        String cipherSuite = session.getCipherSuite();
277        boolean cipherSuiteFound = false;
278        for (String enabledCipherSuite : enabledCipherSuites) {
279            if (cipherSuite.equals(enabledCipherSuite)) {
280                cipherSuiteFound = true;
281                break;
282            }
283        }
284        if (!cipherSuiteFound) {
285            return null;
286        }
287
288        return session;
289    }
290
291    private void checkOpen() throws SocketException {
292        if (isClosed()) {
293            throw new SocketException("Socket is closed");
294        }
295    }
296
297    /**
298     * Starts a TLS/SSL handshake on this connection using some native methods
299     * from the OpenSSL library. It can negotiate new encryption keys, change
300     * cipher suites, or initiate a new session. The certificate chain is
301     * verified if the correspondent property in java.Security is set. All
302     * listeners are notified at the end of the TLS/SSL handshake.
303     */
304    @Override public synchronized void startHandshake() throws IOException {
305        synchronized (handshakeLock) {
306            checkOpen();
307            if (!handshakeStarted) {
308                handshakeStarted = true;
309            } else {
310                return;
311            }
312        }
313
314        // note that this modifies the global seed, not something specific to the connection
315        final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
316        final SecureRandom secureRandom = sslParameters.getSecureRandomMember();
317        if (secureRandom == null) {
318            NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
319        } else {
320            NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes));
321        }
322
323        final boolean client = sslParameters.getUseClientMode();
324
325        final long sslCtxNativePointer = (client) ?
326                sslParameters.getClientSessionContext().sslCtxNativePointer :
327                sslParameters.getServerSessionContext().sslCtxNativePointer;
328
329        this.sslNativePointer = 0;
330        boolean exception = true;
331        try {
332            sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
333            guard.open("close");
334
335            if (npnProtocols != null) {
336                NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
337            }
338
339            if (client && alpnProtocols != null) {
340                NativeCrypto.SSL_CTX_set_alpn_protos(sslCtxNativePointer, alpnProtocols);
341            }
342
343            // setup server certificates and private keys.
344            // clients will receive a call back to request certificates.
345            if (!client) {
346                Set<String> keyTypes = new HashSet<String>();
347                for (String enabledCipherSuite : enabledCipherSuites) {
348                    if (enabledCipherSuite.equals(NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
349                        continue;
350                    }
351                    String keyType = CipherSuite.getByName(enabledCipherSuite).getServerKeyType();
352                    if (keyType != null) {
353                        keyTypes.add(keyType);
354                    }
355                }
356                for (String keyType : keyTypes) {
357                    try {
358                        setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
359                                                                                       null,
360                                                                                       this));
361                    } catch (CertificateEncodingException e) {
362                        throw new IOException(e);
363                    }
364                }
365            }
366
367            NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
368            NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
369            if (useSessionTickets) {
370                NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
371            }
372            if (hostname != null) {
373                NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname);
374            }
375
376            boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
377            if (!enableSessionCreation) {
378                NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
379                                                              enableSessionCreation);
380            }
381
382            AbstractSessionContext sessionContext;
383            OpenSSLSessionImpl sessionToReuse;
384            if (client) {
385                // look for client session to reuse
386                ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
387                sessionContext = clientSessionContext;
388                sessionToReuse = getCachedClientSession(clientSessionContext);
389                if (sessionToReuse != null) {
390                    NativeCrypto.SSL_set_session(sslNativePointer,
391                                                 sessionToReuse.sslSessionNativePointer);
392                }
393            } else {
394                sessionContext = sslParameters.getServerSessionContext();
395                sessionToReuse = null;
396            }
397
398            // setup peer certificate verification
399            if (client) {
400                // TODO support for anonymous cipher would require us to
401                // conditionally use SSL_VERIFY_NONE
402            } else {
403                // needing client auth takes priority...
404                boolean certRequested;
405                if (sslParameters.getNeedClientAuth()) {
406                    NativeCrypto.SSL_set_verify(sslNativePointer,
407                                                NativeCrypto.SSL_VERIFY_PEER
408                                                | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
409                    certRequested = true;
410                // ... over just wanting it...
411                } else if (sslParameters.getWantClientAuth()) {
412                    NativeCrypto.SSL_set_verify(sslNativePointer,
413                                                NativeCrypto.SSL_VERIFY_PEER);
414                    certRequested = true;
415                // ... and it defaults properly so don't call SSL_set_verify in the common case.
416                } else {
417                    certRequested = false;
418                }
419
420                if (certRequested) {
421                    X509TrustManager trustManager = sslParameters.getTrustManager();
422                    X509Certificate[] issuers = trustManager.getAcceptedIssuers();
423                    if (issuers != null && issuers.length != 0) {
424                        byte[][] issuersBytes;
425                        try {
426                            issuersBytes = encodeIssuerX509Principals(issuers);
427                        } catch (CertificateEncodingException e) {
428                            throw new IOException("Problem encoding principals", e);
429                        }
430                        NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
431                    }
432                }
433            }
434
435            // Temporarily use a different timeout for the handshake process
436            int savedReadTimeoutMilliseconds = getSoTimeout();
437            int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
438            if (handshakeTimeoutMilliseconds >= 0) {
439                setSoTimeout(handshakeTimeoutMilliseconds);
440                setSoWriteTimeout(handshakeTimeoutMilliseconds);
441            }
442
443            // TLS Channel ID
444            if (channelIdEnabled) {
445                if (client) {
446                    // Client-side TLS Channel ID
447                    if (channelIdPrivateKey == null) {
448                        throw new SSLHandshakeException("Invalid TLS channel ID key specified");
449                    }
450                    NativeCrypto.SSL_set1_tls_channel_id(sslNativePointer,
451                            channelIdPrivateKey.getPkeyContext());
452                } else {
453                    // Server-side TLS Channel ID
454                    NativeCrypto.SSL_enable_tls_channel_id(sslNativePointer);
455                }
456            }
457
458            int sslSessionNativePointer;
459            try {
460                sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer,
461                        socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols,
462                        client ? null : alpnProtocols);
463            } catch (CertificateException e) {
464                SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
465                wrapper.initCause(e);
466                throw wrapper;
467            }
468            byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
469            if (sessionToReuse != null && Arrays.equals(sessionToReuse.getId(), sessionId)) {
470                this.sslSession = sessionToReuse;
471                sslSession.lastAccessedTime = System.currentTimeMillis();
472                NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
473            } else {
474                if (!enableSessionCreation) {
475                    // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
476                    throw new IllegalStateException("SSL Session may not be created");
477                }
478                X509Certificate[] localCertificates
479                        = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
480                X509Certificate[] peerCertificates
481                        = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
482                this.sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
483                        peerCertificates, getPeerHostName(), getPeerPort(), sessionContext);
484                // if not, putSession later in handshakeCompleted() callback
485                if (handshakeCompleted) {
486                    sessionContext.putSession(sslSession);
487                }
488            }
489
490            // Restore the original timeout now that the handshake is complete
491            if (handshakeTimeoutMilliseconds >= 0) {
492                setSoTimeout(savedReadTimeoutMilliseconds);
493                setSoWriteTimeout(savedWriteTimeoutMilliseconds);
494            }
495
496            // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
497            if (handshakeCompleted) {
498                notifyHandshakeCompletedListeners();
499            }
500
501            exception = false;
502        } catch (SSLProtocolException e) {
503            throw new SSLHandshakeException(e);
504        } finally {
505            // on exceptional exit, treat the socket as closed
506            if (exception) {
507                close();
508            }
509        }
510    }
511
512    private static byte[][] encodeIssuerX509Principals(X509Certificate[] certificates)
513            throws CertificateEncodingException {
514        byte[][] principalBytes = new byte[certificates.length][];
515        for (int i = 0; i < certificates.length; i++) {
516            principalBytes[i] = certificates[i].getIssuerX500Principal().getEncoded();
517        }
518        return principalBytes;
519    }
520
521    String getPeerHostName() {
522        if (wrappedHost != null) {
523            return wrappedHost;
524        }
525        InetAddress inetAddress = super.getInetAddress();
526        if (inetAddress != null) {
527            return inetAddress.getHostName();
528        }
529        return null;
530    }
531
532    int getPeerPort() {
533        return wrappedHost == null ? super.getPort() : wrappedPort;
534    }
535
536    /**
537     * Return a possibly null array of X509Certificates given the
538     * possibly null array of DER encoded bytes.
539     */
540    private static OpenSSLX509Certificate[] createCertChain(long[] certificateRefs)
541            throws IOException {
542        if (certificateRefs == null) {
543            return null;
544        }
545        OpenSSLX509Certificate[] certificates = new OpenSSLX509Certificate[certificateRefs.length];
546        for (int i = 0; i < certificateRefs.length; i++) {
547            certificates[i] = new OpenSSLX509Certificate(certificateRefs[i]);
548        }
549        return certificates;
550    }
551
552    private void setCertificate(String alias) throws CertificateEncodingException, SSLException {
553        if (alias == null) {
554            return;
555        }
556        PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
557        if (privateKey == null) {
558            return;
559        }
560        X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
561        if (certificates == null) {
562            return;
563        }
564
565        /*
566         * Make sure we keep a reference to the OpenSSLX509Certificate by using
567         * this array. Otherwise, if they're not OpenSSLX509Certificate
568         * instances originally, they may be garbage collected before we complete
569         * our JNI calls.
570         */
571        OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[certificates.length];
572        long[] x509refs = new long[certificates.length];
573        for (int i = 0; i < certificates.length; i++) {
574            OpenSSLX509Certificate openSslCert = OpenSSLX509Certificate
575                    .fromCertificate(certificates[i]);
576            openSslCerts[i] = openSslCert;
577            x509refs[i] = openSslCert.getContext();
578        }
579
580        // Note that OpenSSL says to use SSL_use_certificate before SSL_use_PrivateKey.
581        NativeCrypto.SSL_use_certificate(sslNativePointer, x509refs);
582
583        try {
584            final OpenSSLKey key = OpenSSLKey.fromPrivateKey(privateKey);
585            NativeCrypto.SSL_use_PrivateKey(sslNativePointer, key.getPkeyContext());
586        } catch (InvalidKeyException e) {
587            throw new SSLException(e);
588        }
589
590        // checks the last installed private key and certificate,
591        // so need to do this once per loop iteration
592        NativeCrypto.SSL_check_private_key(sslNativePointer);
593    }
594
595    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
596    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
597            throws CertificateEncodingException, SSLException {
598
599        String[] keyTypes = new String[keyTypeBytes.length];
600        for (int i = 0; i < keyTypeBytes.length; i++) {
601            keyTypes[i] = CipherSuite.getClientKeyType(keyTypeBytes[i]);
602        }
603
604        X500Principal[] issuers;
605        if (asn1DerEncodedPrincipals == null) {
606            issuers = null;
607        } else {
608            issuers = new X500Principal[asn1DerEncodedPrincipals.length];
609            for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
610                issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
611            }
612        }
613        setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this));
614    }
615
616    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
617    public void handshakeCompleted() {
618        handshakeCompleted = true;
619
620        // If sslSession is null, the handshake was completed during
621        // the call to NativeCrypto.SSL_do_handshake and not during a
622        // later read operation. That means we do not need to fix up
623        // the SSLSession and session cache or notify
624        // HandshakeCompletedListeners, it will be done in
625        // startHandshake.
626        if (sslSession == null) {
627            return;
628        }
629
630        // reset session id from the native pointer and update the
631        // appropriate cache.
632        sslSession.resetId();
633        AbstractSessionContext sessionContext =
634            (sslParameters.getUseClientMode())
635            ? sslParameters.getClientSessionContext()
636                : sslParameters.getServerSessionContext();
637        sessionContext.putSession(sslSession);
638
639        // let listeners know we are finally done
640        notifyHandshakeCompletedListeners();
641    }
642
643    private void notifyHandshakeCompletedListeners() {
644        if (listeners != null && !listeners.isEmpty()) {
645            // notify the listeners
646            HandshakeCompletedEvent event =
647                new HandshakeCompletedEvent(this, sslSession);
648            for (HandshakeCompletedListener listener : listeners) {
649                try {
650                    listener.handshakeCompleted(event);
651                } catch (RuntimeException e) {
652                    // The RI runs the handlers in a separate thread,
653                    // which we do not. But we try to preserve their
654                    // behavior of logging a problem and not killing
655                    // the handshaking thread just because a listener
656                    // has a problem.
657                    Thread thread = Thread.currentThread();
658                    thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
659                }
660            }
661        }
662    }
663
664    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
665    @Override public void verifyCertificateChain(long[] certRefs, String authMethod)
666            throws CertificateException {
667        try {
668            if (certRefs == null || certRefs.length == 0) {
669                throw new SSLException("Peer sent no certificate");
670            }
671            OpenSSLX509Certificate[] peerCertChain = new OpenSSLX509Certificate[certRefs.length];
672            for (int i = 0; i < certRefs.length; i++) {
673                peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]);
674            }
675            boolean client = sslParameters.getUseClientMode();
676            if (client) {
677                X509TrustManager x509tm = sslParameters.getTrustManager();
678                if (x509tm instanceof TrustManagerImpl) {
679                    TrustManagerImpl tm = (TrustManagerImpl) x509tm;
680                    tm.checkServerTrusted(peerCertChain, authMethod, wrappedHost);
681                } else {
682                    x509tm.checkServerTrusted(peerCertChain, authMethod);
683                }
684            } else {
685                String authType = peerCertChain[0].getPublicKey().getAlgorithm();
686                sslParameters.getTrustManager().checkClientTrusted(peerCertChain,
687                                                                   authType);
688            }
689
690        } catch (CertificateException e) {
691            throw e;
692        } catch (Exception e) {
693            throw new CertificateException(e);
694        }
695    }
696
697    @Override public InputStream getInputStream() throws IOException {
698        checkOpen();
699        synchronized (this) {
700            if (is == null) {
701                is = new SSLInputStream();
702            }
703
704            return is;
705        }
706    }
707
708    @Override public OutputStream getOutputStream() throws IOException {
709        checkOpen();
710        synchronized (this) {
711            if (os == null) {
712                os = new SSLOutputStream();
713            }
714
715            return os;
716        }
717    }
718
719    /**
720     * This inner class provides input data stream functionality
721     * for the OpenSSL native implementation. It is used to
722     * read data received via SSL protocol.
723     */
724    private class SSLInputStream extends InputStream {
725        SSLInputStream() throws IOException {
726            /*
727             * Note: When startHandshake() throws an exception, no
728             * SSLInputStream object will be created.
729             */
730            OpenSSLSocketImpl.this.startHandshake();
731        }
732
733        /**
734         * Reads one byte. If there is no data in the underlying buffer,
735         * this operation can block until the data will be
736         * available.
737         * @return read value.
738         * @throws IOException
739         */
740        @Override
741        public int read() throws IOException {
742            return Streams.readSingleByte(this);
743        }
744
745        /**
746         * Method acts as described in spec for superclass.
747         * @see java.io.InputStream#read(byte[],int,int)
748         */
749        @Override
750        public int read(byte[] buf, int offset, int byteCount) throws IOException {
751            BlockGuard.getThreadPolicy().onNetwork();
752            synchronized (readLock) {
753                checkOpen();
754                Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
755                if (byteCount == 0) {
756                    return 0;
757                }
758                return NativeCrypto.SSL_read(sslNativePointer, socket.getFileDescriptor$(),
759                        OpenSSLSocketImpl.this, buf, offset, byteCount, getSoTimeout());
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();
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 oneByte) throws IOException {
784            Streams.writeSingleByte(this, oneByte);
785        }
786
787        /**
788         * Method acts as described in spec for superclass.
789         * @see java.io.OutputStream#write(byte[],int,int)
790         */
791        @Override
792        public void write(byte[] buf, int offset, int byteCount) throws IOException {
793            BlockGuard.getThreadPolicy().onNetwork();
794            synchronized (writeLock) {
795                checkOpen();
796                Arrays.checkOffsetAndCount(buf.length, offset, byteCount);
797                if (byteCount == 0) {
798                    return;
799                }
800                NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(),
801                        OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds);
802            }
803        }
804    }
805
806
807    @Override public SSLSession getSession() {
808        if (sslSession == null) {
809            try {
810                startHandshake();
811            } catch (IOException e) {
812                // return an invalid session with
813                // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
814                return SSLSessionImpl.getNullSession();
815            }
816        }
817        return sslSession;
818    }
819
820    @Override public void addHandshakeCompletedListener(
821            HandshakeCompletedListener listener) {
822        if (listener == null) {
823            throw new IllegalArgumentException("Provided listener is null");
824        }
825        if (listeners == null) {
826            listeners = new ArrayList<HandshakeCompletedListener>();
827        }
828        listeners.add(listener);
829    }
830
831    @Override public void removeHandshakeCompletedListener(
832            HandshakeCompletedListener listener) {
833        if (listener == null) {
834            throw new IllegalArgumentException("Provided listener is null");
835        }
836        if (listeners == null) {
837            throw new IllegalArgumentException(
838                    "Provided listener is not registered");
839        }
840        if (!listeners.remove(listener)) {
841            throw new IllegalArgumentException(
842                    "Provided listener is not registered");
843        }
844    }
845
846    @Override public boolean getEnableSessionCreation() {
847        return sslParameters.getEnableSessionCreation();
848    }
849
850    @Override public void setEnableSessionCreation(boolean flag) {
851        sslParameters.setEnableSessionCreation(flag);
852    }
853
854    @Override public String[] getSupportedCipherSuites() {
855        return NativeCrypto.getSupportedCipherSuites();
856    }
857
858    @Override public String[] getEnabledCipherSuites() {
859        return enabledCipherSuites.clone();
860    }
861
862    @Override public void setEnabledCipherSuites(String[] suites) {
863        enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
864    }
865
866    @Override public String[] getSupportedProtocols() {
867        return NativeCrypto.getSupportedProtocols();
868    }
869
870    @Override public String[] getEnabledProtocols() {
871        return enabledProtocols.clone();
872    }
873
874    @Override public void setEnabledProtocols(String[] protocols) {
875        enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
876    }
877
878    /**
879     * This method enables session ticket support.
880     *
881     * @param useSessionTickets True to enable session tickets
882     */
883    public void setUseSessionTickets(boolean useSessionTickets) {
884        this.useSessionTickets = useSessionTickets;
885    }
886
887    /**
888     * This method enables Server Name Indication
889     *
890     * @param hostname the desired SNI hostname, or null to disable
891     */
892    public void setHostname(String hostname) {
893        this.hostname = hostname;
894    }
895
896    /**
897     * Enables/disables TLS Channel ID for this server socket.
898     *
899     * <p>This method needs to be invoked before the handshake starts.
900     *
901     * @throws IllegalStateException if this is a client socket or if the handshake has already
902     *         started.
903
904     */
905    public void setChannelIdEnabled(boolean enabled) {
906        if (getUseClientMode()) {
907            throw new IllegalStateException("Client mode");
908        }
909        if (handshakeStarted) {
910            throw new IllegalStateException(
911                    "Could not enable/disable Channel ID after the initial handshake has"
912                    + " begun.");
913        }
914        this.channelIdEnabled = enabled;
915    }
916
917    /**
918     * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
919     * handshake completes.
920     *
921     * @return channel ID or {@code null} if not available.
922     *
923     * @throws IllegalStateException if this is a client socket or if the handshake has not yet
924     *         completed.
925     * @throws SSLException if channel ID is available but could not be obtained.
926     */
927    public byte[] getChannelId() throws SSLException {
928        if (getUseClientMode()) {
929            throw new IllegalStateException("Client mode");
930        }
931        if (!handshakeCompleted) {
932            throw new IllegalStateException(
933                    "Channel ID is only available after handshake completes");
934        }
935        return NativeCrypto.SSL_get_tls_channel_id(sslNativePointer);
936    }
937
938    /**
939     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
940     *
941     * <p>This method needs to be invoked before the handshake starts.
942     *
943     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
944     *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
945     *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
946     *
947     * @throws IllegalStateException if this is a server socket or if the handshake has already
948     *         started.
949     */
950    public void setChannelIdPrivateKey(PrivateKey privateKey) {
951        if (!getUseClientMode()) {
952            throw new IllegalStateException("Server mode");
953        }
954        if (handshakeStarted) {
955            throw new IllegalStateException(
956                    "Could not change Channel ID private key after the initial handshake has"
957                    + " begun.");
958        }
959        if (privateKey == null) {
960            this.channelIdEnabled = false;
961            this.channelIdPrivateKey = null;
962        } else {
963            this.channelIdEnabled = true;
964            try {
965                this.channelIdPrivateKey = OpenSSLKey.fromPrivateKey(privateKey);
966            } catch (InvalidKeyException e) {
967                // Will have error in startHandshake
968            }
969        }
970    }
971
972    @Override public boolean getUseClientMode() {
973        return sslParameters.getUseClientMode();
974    }
975
976    @Override public void setUseClientMode(boolean mode) {
977        if (handshakeStarted) {
978            throw new IllegalArgumentException(
979                    "Could not change the mode after the initial handshake has begun.");
980        }
981        sslParameters.setUseClientMode(mode);
982    }
983
984    @Override public boolean getWantClientAuth() {
985        return sslParameters.getWantClientAuth();
986    }
987
988    @Override public boolean getNeedClientAuth() {
989        return sslParameters.getNeedClientAuth();
990    }
991
992    @Override public void setNeedClientAuth(boolean need) {
993        sslParameters.setNeedClientAuth(need);
994    }
995
996    @Override public void setWantClientAuth(boolean want) {
997        sslParameters.setWantClientAuth(want);
998    }
999
1000    @Override public void sendUrgentData(int data) throws IOException {
1001        throw new SocketException("Method sendUrgentData() is not supported.");
1002    }
1003
1004    @Override public void setOOBInline(boolean on) throws SocketException {
1005        throw new SocketException("Methods sendUrgentData, setOOBInline are not supported.");
1006    }
1007
1008    @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
1009        super.setSoTimeout(readTimeoutMilliseconds);
1010        this.readTimeoutMilliseconds = readTimeoutMilliseconds;
1011    }
1012
1013    @Override public int getSoTimeout() throws SocketException {
1014        return readTimeoutMilliseconds;
1015    }
1016
1017    /**
1018     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
1019     */
1020    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
1021        this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
1022
1023        StructTimeval tv = StructTimeval.fromMillis(writeTimeoutMilliseconds);
1024        try {
1025            Libcore.os.setsockoptTimeval(getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
1026        } catch (ErrnoException errnoException) {
1027            throw errnoException.rethrowAsSocketException();
1028        }
1029    }
1030
1031    /**
1032     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
1033     */
1034    public int getSoWriteTimeout() throws SocketException {
1035        return writeTimeoutMilliseconds;
1036    }
1037
1038    /**
1039     * Set the handshake timeout on this socket.  This timeout is specified in
1040     * milliseconds and will be used only during the handshake process.
1041     */
1042    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
1043        this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
1044    }
1045
1046    @Override public void close() throws IOException {
1047        // TODO: Close SSL sockets using a background thread so they close gracefully.
1048
1049        synchronized (handshakeLock) {
1050            if (!handshakeStarted) {
1051                // prevent further attempts to start handshake
1052                handshakeStarted = true;
1053
1054                synchronized (this) {
1055                    free();
1056
1057                    if (socket != this) {
1058                        if (autoClose && !socket.isClosed()) {
1059                            socket.close();
1060                        }
1061                    } else {
1062                        if (!super.isClosed()) {
1063                            super.close();
1064                        }
1065                    }
1066                }
1067
1068                return;
1069            }
1070        }
1071
1072        synchronized (this) {
1073
1074            // Interrupt any outstanding reads or writes before taking the writeLock and readLock
1075            NativeCrypto.SSL_interrupt(sslNativePointer);
1076
1077            synchronized (writeLock) {
1078                synchronized (readLock) {
1079                    // Shut down the SSL connection, per se.
1080                    try {
1081                        if (handshakeStarted) {
1082                            BlockGuard.getThreadPolicy().onNetwork();
1083                            NativeCrypto.SSL_shutdown(sslNativePointer, socket.getFileDescriptor$(),
1084                                    this);
1085                        }
1086                    } catch (IOException ignored) {
1087                        /*
1088                         * Note that although close() can throw
1089                         * IOException, the RI does not throw if there
1090                         * is problem sending a "close notify" which
1091                         * can happen if the underlying socket is closed.
1092                         */
1093                    } finally {
1094                        /*
1095                         * Even if the above call failed, it is still safe to free
1096                         * the native structs, and we need to do so lest we leak
1097                         * memory.
1098                         */
1099                        free();
1100
1101                        if (socket != this) {
1102                            if (autoClose && !socket.isClosed()) {
1103                                socket.close();
1104                            }
1105                        } else {
1106                            if (!super.isClosed()) {
1107                                super.close();
1108                            }
1109                        }
1110                    }
1111                }
1112            }
1113        }
1114    }
1115
1116    private void free() {
1117        if (sslNativePointer == 0) {
1118            return;
1119        }
1120        NativeCrypto.SSL_free(sslNativePointer);
1121        sslNativePointer = 0;
1122        guard.close();
1123    }
1124
1125    @Override protected void finalize() throws Throwable {
1126        try {
1127            /*
1128             * Just worry about our own state. Notably we do not try and
1129             * close anything. The SocketImpl, either our own
1130             * PlainSocketImpl, or the Socket we are wrapping, will do
1131             * that. This might mean we do not properly SSL_shutdown, but
1132             * if you want to do that, properly close the socket yourself.
1133             *
1134             * The reason why we don't try to SSL_shutdown, is that there
1135             * can be a race between finalizers where the PlainSocketImpl
1136             * finalizer runs first and closes the socket. However, in the
1137             * meanwhile, the underlying file descriptor could be reused
1138             * for another purpose. If we call SSL_shutdown, the
1139             * underlying socket BIOs still have the old file descriptor
1140             * and will write the close notify to some unsuspecting
1141             * reader.
1142             */
1143            if (guard != null) {
1144                guard.warnIfOpen();
1145            }
1146            free();
1147        } finally {
1148            super.finalize();
1149        }
1150    }
1151
1152    @Override
1153    public FileDescriptor getFileDescriptor$() {
1154        if (socket == this) {
1155            return super.getFileDescriptor$();
1156        } else {
1157            return socket.getFileDescriptor$();
1158        }
1159    }
1160
1161    /**
1162     * Returns the protocol agreed upon by client and server, or null if no
1163     * protocol was agreed upon.
1164     */
1165    public byte[] getNpnSelectedProtocol() {
1166        return NativeCrypto.SSL_get_npn_negotiated_protocol(sslNativePointer);
1167    }
1168
1169    /**
1170     * Returns the protocol agreed upon by client and server, or {@code null} if
1171     * no protocol was agreed upon.
1172     */
1173    public byte[] getAlpnSelectedProtocol() {
1174        return NativeCrypto.SSL_get0_alpn_selected(sslNativePointer);
1175    }
1176
1177    /**
1178     * Sets the list of protocols this peer is interested in. If null no
1179     * protocols will be used.
1180     *
1181     * @param npnProtocols a non-empty array of protocol names. From
1182     *     SSL_select_next_proto, "vector of 8-bit, length prefixed byte
1183     *     strings. The length byte itself is not included in the length. A byte
1184     *     string of length 0 is invalid. No byte string may be truncated.".
1185     */
1186    public void setNpnProtocols(byte[] npnProtocols) {
1187        if (npnProtocols != null && npnProtocols.length == 0) {
1188            throw new IllegalArgumentException("npnProtocols.length == 0");
1189        }
1190        this.npnProtocols = npnProtocols;
1191    }
1192
1193    /**
1194     * Sets the list of protocols this peer is interested in. If the list is
1195     * {@code null}, no protocols will be used.
1196     *
1197     * @param alpnProtocols a non-empty array of protocol names. From
1198     *            SSL_select_next_proto, "vector of 8-bit, length prefixed byte
1199     *            strings. The length byte itself is not included in the length.
1200     *            A byte string of length 0 is invalid. No byte string may be
1201     *            truncated.".
1202     */
1203    public void setAlpnProtocols(byte[] alpnProtocols) {
1204        if (alpnProtocols != null && alpnProtocols.length == 0) {
1205            throw new IllegalArgumentException("alpnProtocols.length == 0");
1206        }
1207        this.alpnProtocols = alpnProtocols;
1208    }
1209}
1210