Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright 2013 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 
     17 package org.conscrypt;
     18 
     19 import static android.system.OsConstants.SOL_SOCKET;
     20 import static android.system.OsConstants.SO_SNDTIMEO;
     21 
     22 import android.system.ErrnoException;
     23 import android.system.StructTimeval;
     24 import dalvik.system.BlockGuard;
     25 import dalvik.system.CloseGuard;
     26 import java.io.FileDescriptor;
     27 import java.io.IOException;
     28 import java.lang.reflect.Field;
     29 import java.lang.reflect.InvocationTargetException;
     30 import java.lang.reflect.Method;
     31 import java.net.InetAddress;
     32 import java.net.InetSocketAddress;
     33 import java.net.Socket;
     34 import java.net.SocketException;
     35 import java.net.SocketImpl;
     36 import java.security.AlgorithmParameters;
     37 import java.security.NoSuchAlgorithmException;
     38 import java.security.PrivateKey;
     39 import java.security.cert.CertificateException;
     40 import java.security.cert.X509Certificate;
     41 import java.security.spec.AlgorithmParameterSpec;
     42 import java.security.spec.ECParameterSpec;
     43 import java.security.spec.InvalidParameterSpecException;
     44 import java.util.Collections;
     45 import java.util.List;
     46 import javax.crypto.spec.GCMParameterSpec;
     47 import javax.net.ssl.SNIHostName;
     48 import javax.net.ssl.SNIServerName;
     49 import javax.net.ssl.SSLEngine;
     50 import javax.net.ssl.SSLParameters;
     51 import javax.net.ssl.SSLSession;
     52 import javax.net.ssl.SSLSocketFactory;
     53 import javax.net.ssl.StandardConstants;
     54 import javax.net.ssl.X509ExtendedTrustManager;
     55 import javax.net.ssl.X509TrustManager;
     56 import libcore.io.Libcore;
     57 import libcore.net.NetworkSecurityPolicy;
     58 import sun.security.x509.AlgorithmId;
     59 
     60 final class Platform {
     61     private static class NoPreloadHolder { public static final Platform MAPPER = new Platform(); }
     62 
     63     /**
     64      * Runs all the setup for the platform that only needs to run once.
     65      */
     66     public static void setup() {
     67         NoPreloadHolder.MAPPER.ping();
     68     }
     69 
     70     /**
     71      * Just a placeholder to make sure the class is initialized.
     72      */
     73     private void ping() {}
     74 
     75     private Platform() {}
     76 
     77     /**
     78      * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
     79      * if the default constructor is used.
     80      */
     81     static String getDefaultProviderName() {
     82         return "AndroidOpenSSL";
     83     }
     84 
     85     static FileDescriptor getFileDescriptor(Socket s) {
     86         return s.getFileDescriptor$();
     87     }
     88 
     89     static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
     90         try {
     91             Field f_impl = Socket.class.getDeclaredField("impl");
     92             f_impl.setAccessible(true);
     93             Object socketImpl = f_impl.get(socket);
     94             Field f_fd = SocketImpl.class.getDeclaredField("fd");
     95             f_fd.setAccessible(true);
     96             return (FileDescriptor) f_fd.get(socketImpl);
     97         } catch (Exception e) {
     98             throw new RuntimeException("Can't get FileDescriptor from socket", e);
     99         }
    100     }
    101 
    102     static String getCurveName(ECParameterSpec spec) {
    103         return spec.getCurveName();
    104     }
    105 
    106     static void setCurveName(ECParameterSpec spec, String curveName) {
    107         spec.setCurveName(curveName);
    108     }
    109 
    110     static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
    111         StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
    112         try {
    113             Libcore.os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
    114         } catch (ErrnoException errnoException) {
    115             throw errnoException.rethrowAsSocketException();
    116         }
    117     }
    118 
    119     static void setSSLParameters(
    120             SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
    121         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
    122         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
    123         List<SNIServerName> serverNames = params.getServerNames();
    124         if (serverNames != null) {
    125             for (SNIServerName serverName : serverNames) {
    126                 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
    127                     socket.setHostname(((SNIHostName) serverName).getAsciiName());
    128                     break;
    129                 }
    130             }
    131         }
    132     }
    133 
    134     static void getSSLParameters(
    135             SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
    136         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
    137         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
    138         if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
    139             params.setServerNames(Collections.<SNIServerName>singletonList(
    140                     new SNIHostName(socket.getHostname())));
    141         }
    142     }
    143 
    144     static void setSSLParameters(
    145             SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
    146         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
    147         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
    148         List<SNIServerName> serverNames = params.getServerNames();
    149         if (serverNames != null) {
    150             for (SNIServerName serverName : serverNames) {
    151                 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
    152                     engine.setHostname(((SNIHostName) serverName).getAsciiName());
    153                     break;
    154                 }
    155             }
    156         }
    157     }
    158 
    159     static void getSSLParameters(
    160             SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
    161         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
    162         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
    163         if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
    164             params.setServerNames(Collections.<SNIServerName>singletonList(
    165                     new SNIHostName(engine.getHostname())));
    166         }
    167     }
    168 
    169     /**
    170      * Helper function to unify calls to the different names used for each function taking a
    171      * Socket, SSLEngine, or String (legacy Android).
    172      */
    173     private static boolean checkTrusted(String methodName, X509TrustManager tm,
    174             X509Certificate[] chain, String authType, Class<?> argumentClass,
    175             Object argumentInstance) throws CertificateException {
    176         // Use duck-typing to try and call the hostname-aware method if available.
    177         try {
    178             Method method = tm.getClass().getMethod(
    179                     methodName, X509Certificate[].class, String.class, argumentClass);
    180             method.invoke(tm, chain, authType, argumentInstance);
    181             return true;
    182         } catch (NoSuchMethodException | IllegalAccessException ignored) {
    183         } catch (InvocationTargetException e) {
    184             if (e.getCause() instanceof CertificateException) {
    185                 throw(CertificateException) e.getCause();
    186             }
    187             throw new RuntimeException(e.getCause());
    188         }
    189         return false;
    190     }
    191 
    192     static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
    193             AbstractConscryptSocket socket) throws CertificateException {
    194         if (tm instanceof X509ExtendedTrustManager) {
    195             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    196             x509etm.checkClientTrusted(chain, authType, socket);
    197         } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
    198                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    199                            socket.getHandshakeSession().getPeerHost())) {
    200             tm.checkClientTrusted(chain, authType);
    201         }
    202     }
    203 
    204     static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
    205             AbstractConscryptSocket socket) throws CertificateException {
    206         if (tm instanceof X509ExtendedTrustManager) {
    207             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    208             x509etm.checkServerTrusted(chain, authType, socket);
    209         } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
    210                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    211                            socket.getHandshakeSession().getPeerHost())) {
    212             tm.checkServerTrusted(chain, authType);
    213         }
    214     }
    215 
    216     static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
    217             ConscryptEngine engine) throws CertificateException {
    218         if (tm instanceof X509ExtendedTrustManager) {
    219             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    220             x509etm.checkClientTrusted(chain, authType, engine);
    221         } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
    222                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    223                            engine.getHandshakeSession().getPeerHost())) {
    224             tm.checkClientTrusted(chain, authType);
    225         }
    226     }
    227 
    228     static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
    229             ConscryptEngine engine) throws CertificateException {
    230         if (tm instanceof X509ExtendedTrustManager) {
    231             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    232             x509etm.checkServerTrusted(chain, authType, engine);
    233         } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
    234                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    235                            engine.getHandshakeSession().getPeerHost())) {
    236             tm.checkServerTrusted(chain, authType);
    237         }
    238     }
    239 
    240     /**
    241      * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
    242      * builds since we didn't backport, so return null.
    243      */
    244     static OpenSSLKey wrapRsaKey(PrivateKey key) {
    245         return null;
    246     }
    247 
    248     /**
    249      * Logs to the system EventLog system.
    250      */
    251     static void logEvent(String message) {
    252         try {
    253             Class processClass = Class.forName("android.os.Process");
    254             Object processInstance = processClass.newInstance();
    255             Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
    256             int uid = (Integer) myUidMethod.invoke(processInstance);
    257 
    258             Class eventLogClass = Class.forName("android.util.EventLog");
    259             Object eventLogInstance = eventLogClass.newInstance();
    260             Method writeEventMethod = eventLogClass.getMethod(
    261                     "writeEvent", new Class[] {Integer.TYPE, Object[].class});
    262             writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
    263                     new Object[] {"conscrypt", uid, message});
    264         } catch (Exception e) {
    265             // Do not log and fail silently
    266         }
    267     }
    268 
    269     /**
    270      * Returns true if the supplied hostname is an literal IP address.
    271      */
    272     static boolean isLiteralIpAddress(String hostname) {
    273         return InetAddress.isNumeric(hostname);
    274     }
    275 
    276     static SSLEngine wrapEngine(ConscryptEngine engine) {
    277         return new Java8EngineWrapper(engine);
    278     }
    279 
    280     static SSLEngine unwrapEngine(SSLEngine engine) {
    281         return Java8EngineWrapper.getDelegate(engine);
    282     }
    283 
    284     static ConscryptEngineSocket createEngineSocket(SSLParametersImpl sslParameters)
    285             throws IOException {
    286         return new Java8EngineSocket(sslParameters);
    287     }
    288 
    289     static ConscryptEngineSocket createEngineSocket(String hostname, int port,
    290             SSLParametersImpl sslParameters) throws IOException {
    291         return new Java8EngineSocket(hostname, port, sslParameters);
    292     }
    293 
    294     static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
    295             SSLParametersImpl sslParameters) throws IOException {
    296         return new Java8EngineSocket(address, port, sslParameters);
    297     }
    298 
    299     static ConscryptEngineSocket createEngineSocket(String hostname, int port,
    300             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    301             throws IOException {
    302         return new Java8EngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
    303     }
    304 
    305     static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
    306             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    307             throws IOException {
    308         return new Java8EngineSocket(address, port, clientAddress, clientPort, sslParameters);
    309     }
    310 
    311     static ConscryptEngineSocket createEngineSocket(Socket socket, String hostname, int port,
    312             boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
    313         return new Java8EngineSocket(socket, hostname, port, autoClose, sslParameters);
    314     }
    315 
    316     static ConscryptFileDescriptorSocket createFileDescriptorSocket(SSLParametersImpl sslParameters)
    317             throws IOException {
    318         return new Java8FileDescriptorSocket(sslParameters);
    319     }
    320 
    321     static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
    322             SSLParametersImpl sslParameters) throws IOException {
    323         return new Java8FileDescriptorSocket(hostname, port, sslParameters);
    324     }
    325 
    326     static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
    327             SSLParametersImpl sslParameters) throws IOException {
    328         return new Java8FileDescriptorSocket(address, port, sslParameters);
    329     }
    330 
    331     static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
    332             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    333             throws IOException {
    334         return new Java8FileDescriptorSocket(
    335                 hostname, port, clientAddress, clientPort, sslParameters);
    336     }
    337 
    338     static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
    339             InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
    340             throws IOException {
    341         return new Java8FileDescriptorSocket(
    342                 address, port, clientAddress, clientPort, sslParameters);
    343     }
    344 
    345     static ConscryptFileDescriptorSocket createFileDescriptorSocket(Socket socket, String hostname,
    346             int port, boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
    347         return new Java8FileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
    348     }
    349 
    350     /**
    351      * Wrap the SocketFactory with the platform wrapper if needed for compatability.
    352      * For the platform-bundled library we never need to wrap.
    353      */
    354     static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
    355         return factory;
    356     }
    357 
    358     /**
    359      * Convert from platform's GCMParameterSpec to our internal version.
    360      */
    361     static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
    362         if (params instanceof GCMParameterSpec) {
    363             GCMParameterSpec gcmParams = (GCMParameterSpec) params;
    364             return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
    365         }
    366         return null;
    367     }
    368 
    369     /**
    370      * Convert from an opaque AlgorithmParameters to the platform's GCMParameterSpec.
    371      */
    372     static AlgorithmParameterSpec fromGCMParameters(AlgorithmParameters params) {
    373         try {
    374             return params.getParameterSpec(GCMParameterSpec.class);
    375         } catch (InvalidParameterSpecException e) {
    376             return null;
    377         }
    378     }
    379 
    380     /**
    381      * Creates a platform version of {@code GCMParameterSpec}.
    382      */
    383     static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
    384         return new GCMParameterSpec(tagLenInBits, iv);
    385     }
    386 
    387     /*
    388      * CloseGuard functions.
    389      */
    390 
    391     static CloseGuard closeGuardGet() {
    392         return CloseGuard.get();
    393     }
    394 
    395     static void closeGuardOpen(Object guardObj, String message) {
    396         CloseGuard guard = (CloseGuard) guardObj;
    397         guard.open(message);
    398     }
    399 
    400     static void closeGuardClose(Object guardObj) {
    401         CloseGuard guard = (CloseGuard) guardObj;
    402         guard.close();
    403     }
    404 
    405     static void closeGuardWarnIfOpen(Object guardObj) {
    406         CloseGuard guard = (CloseGuard) guardObj;
    407         guard.warnIfOpen();
    408     }
    409 
    410     /*
    411      * BlockGuard functions.
    412      */
    413 
    414     static void blockGuardOnNetwork() {
    415         BlockGuard.getThreadPolicy().onNetwork();
    416     }
    417 
    418     /**
    419      * OID to Algorithm Name mapping.
    420      */
    421     static String oidToAlgorithmName(String oid) {
    422         try {
    423             return AlgorithmId.get(oid).getName();
    424         } catch (NoSuchAlgorithmException e) {
    425             return oid;
    426         }
    427     }
    428 
    429     /**
    430      * Provides extended capabilities for the session if supported by the platform.
    431      */
    432     static SSLSession wrapSSLSession(ConscryptSession sslSession) {
    433         return new Java8ExtendedSSLSession(sslSession);
    434     }
    435 
    436     public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
    437         try {
    438             Method getHolder = InetAddress.class.getDeclaredMethod("holder");
    439             getHolder.setAccessible(true);
    440 
    441             Method getOriginalHostName = Class.forName("java.net.InetAddress$InetAddressHolder")
    442                                                  .getDeclaredMethod("getOriginalHostName");
    443             getOriginalHostName.setAccessible(true);
    444 
    445             String originalHostName = (String) getOriginalHostName.invoke(getHolder.invoke(addr));
    446             if (originalHostName == null) {
    447                 return addr.getHostAddress();
    448             }
    449             return originalHostName;
    450         } catch (InvocationTargetException e) {
    451             throw new RuntimeException("Failed to get originalHostName", e);
    452         } catch (ClassNotFoundException ignore) {
    453             // passthrough and return addr.getHostAddress()
    454         } catch (IllegalAccessException ignore) {
    455         } catch (NoSuchMethodException ignore) {
    456         }
    457         return addr.getHostAddress();
    458     }
    459 
    460     /*
    461      * Pre-Java-7 backward compatibility.
    462      */
    463 
    464     static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
    465         return addr.getHostString();
    466     }
    467 
    468     static boolean isCTVerificationRequired(String hostname) {
    469         return NetworkSecurityPolicy.getInstance().isCertificateTransparencyVerificationRequired(
    470                 hostname);
    471     }
    472 }
    473