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.Os;
     24 import android.system.StructTimeval;
     25 import dalvik.system.BlockGuard;
     26 import dalvik.system.CloseGuard;
     27 import java.io.FileDescriptor;
     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.NoSuchAlgorithmException;
     37 import java.security.PrivateKey;
     38 import java.security.cert.CertificateException;
     39 import java.security.cert.X509Certificate;
     40 import java.security.spec.AlgorithmParameterSpec;
     41 import java.security.spec.ECParameterSpec;
     42 import java.util.Collections;
     43 import java.util.List;
     44 import javax.crypto.spec.GCMParameterSpec;
     45 import javax.net.ssl.SNIHostName;
     46 import javax.net.ssl.SNIServerName;
     47 import javax.net.ssl.SSLEngine;
     48 import javax.net.ssl.SSLParameters;
     49 import javax.net.ssl.SSLSession;
     50 import javax.net.ssl.SSLSocketFactory;
     51 import javax.net.ssl.StandardConstants;
     52 import javax.net.ssl.X509ExtendedTrustManager;
     53 import javax.net.ssl.X509TrustManager;
     54 import libcore.net.NetworkSecurityPolicy;
     55 import sun.security.x509.AlgorithmId;
     56 
     57 class Platform {
     58     private static class NoPreloadHolder {
     59         public static final Platform MAPPER = new Platform();
     60     }
     61 
     62     /**
     63      * Runs all the setup for the platform that only needs to run once.
     64      */
     65     public static void setup() {
     66         NoPreloadHolder.MAPPER.ping();
     67     }
     68 
     69     /**
     70      * Just a placeholder to make sure the class is initialized.
     71      */
     72     private void ping() {
     73     }
     74 
     75     private Platform() {
     76     }
     77 
     78     public static FileDescriptor getFileDescriptor(Socket s) {
     79         return s.getFileDescriptor$();
     80     }
     81 
     82     public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
     83         try {
     84             Field f_impl = Socket.class.getDeclaredField("impl");
     85             f_impl.setAccessible(true);
     86             Object socketImpl = f_impl.get(openSSLSocketImpl);
     87             Field f_fd = SocketImpl.class.getDeclaredField("fd");
     88             f_fd.setAccessible(true);
     89             return (FileDescriptor) f_fd.get(socketImpl);
     90         } catch (Exception e) {
     91             throw new RuntimeException("Can't get FileDescriptor from socket", e);
     92         }
     93     }
     94 
     95     public static String getCurveName(ECParameterSpec spec) {
     96         return spec.getCurveName();
     97     }
     98 
     99     public static void setCurveName(ECParameterSpec spec, String curveName) {
    100         spec.setCurveName(curveName);
    101     }
    102 
    103     public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
    104         StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
    105         try {
    106             Os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
    107         } catch (ErrnoException errnoException) {
    108             throw errnoException.rethrowAsSocketException();
    109         }
    110     }
    111 
    112     public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
    113             OpenSSLSocketImpl socket) {
    114         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
    115         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
    116         List<SNIServerName> serverNames = params.getServerNames();
    117         if (serverNames != null) {
    118             for (SNIServerName serverName : serverNames) {
    119                 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
    120                     socket.setHostname(((SNIHostName) serverName).getAsciiName());
    121                     break;
    122                 }
    123             }
    124         }
    125     }
    126 
    127     public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
    128             OpenSSLSocketImpl socket) {
    129         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
    130         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
    131         if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
    132             params.setServerNames(Collections.<SNIServerName> singletonList(
    133                     new SNIHostName(socket.getHostname())));
    134         }
    135     }
    136 
    137     public static void setSSLParameters(
    138             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
    139         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
    140         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
    141         List<SNIServerName> serverNames = params.getServerNames();
    142         if (serverNames != null) {
    143             for (SNIServerName serverName : serverNames) {
    144                 if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
    145                     engine.setSniHostname(((SNIHostName) serverName).getAsciiName());
    146                     break;
    147                 }
    148             }
    149         }
    150     }
    151 
    152     public static void getSSLParameters(
    153             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
    154         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
    155         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
    156         if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
    157             params.setServerNames(Collections.<SNIServerName>singletonList(
    158                     new SNIHostName(engine.getSniHostname())));
    159         }
    160     }
    161 
    162     /**
    163      * Helper function to unify calls to the different names used for each function taking a
    164      * Socket, SSLEngine, or String (legacy Android).
    165      */
    166     private static boolean checkTrusted(String methodName, X509TrustManager tm,
    167             X509Certificate[] chain, String authType, Class<?> argumentClass,
    168             Object argumentInstance) throws CertificateException {
    169         // Use duck-typing to try and call the hostname-aware method if available.
    170         try {
    171             Method method = tm.getClass().getMethod(methodName,
    172                     X509Certificate[].class,
    173                     String.class,
    174                     argumentClass);
    175             method.invoke(tm, chain, authType, argumentInstance);
    176             return true;
    177         } catch (NoSuchMethodException | IllegalAccessException ignored) {
    178         } catch (InvocationTargetException e) {
    179             if (e.getCause() instanceof CertificateException) {
    180                 throw (CertificateException) e.getCause();
    181             }
    182             throw new RuntimeException(e.getCause());
    183         }
    184         return false;
    185     }
    186 
    187     public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
    188             String authType, OpenSSLSocketImpl socket) throws CertificateException {
    189         if (tm instanceof X509ExtendedTrustManager) {
    190             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    191             x509etm.checkClientTrusted(chain, authType, socket);
    192         } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
    193                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    194                                  socket.getHandshakeSession().getPeerHost())) {
    195             tm.checkClientTrusted(chain, authType);
    196         }
    197     }
    198 
    199     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    200             String authType, OpenSSLSocketImpl socket) throws CertificateException {
    201         if (tm instanceof X509ExtendedTrustManager) {
    202             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    203             x509etm.checkServerTrusted(chain, authType, socket);
    204         } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
    205                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    206                                  socket.getHandshakeSession().getPeerHost())) {
    207             tm.checkServerTrusted(chain, authType);
    208         }
    209     }
    210 
    211     public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
    212             String authType, OpenSSLEngineImpl engine) throws CertificateException {
    213         if (tm instanceof X509ExtendedTrustManager) {
    214             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    215             x509etm.checkClientTrusted(chain, authType, engine);
    216         } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
    217                 && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
    218                                  engine.getHandshakeSession().getPeerHost())) {
    219             tm.checkClientTrusted(chain, authType);
    220         }
    221     }
    222 
    223     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    224             String authType, OpenSSLEngineImpl engine) throws CertificateException {
    225         if (tm instanceof X509ExtendedTrustManager) {
    226             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    227             x509etm.checkServerTrusted(chain, authType, engine);
    228         } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
    229                 && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
    230                                  engine.getHandshakeSession().getPeerHost())) {
    231             tm.checkServerTrusted(chain, authType);
    232         }
    233     }
    234 
    235     /**
    236      * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
    237      * builds since we didn't backport, so return null.
    238      */
    239     public static OpenSSLKey wrapRsaKey(PrivateKey key) {
    240         return null;
    241     }
    242 
    243     /**
    244      * Logs to the system EventLog system.
    245      */
    246     public static void logEvent(String message) {
    247         try {
    248             Class processClass = Class.forName("android.os.Process");
    249             Object processInstance = processClass.newInstance();
    250             Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
    251             int uid = (Integer) myUidMethod.invoke(processInstance);
    252 
    253             Class eventLogClass = Class.forName("android.util.EventLog");
    254             Object eventLogInstance = eventLogClass.newInstance();
    255             Method writeEventMethod = eventLogClass.getMethod("writeEvent",
    256                     new Class[] { Integer.TYPE, Object[].class });
    257             writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
    258                     new Object[] { "conscrypt", uid, message });
    259         } catch (Exception e) {
    260             // Do not log and fail silently
    261         }
    262     }
    263 
    264     /**
    265      * Returns true if the supplied hostname is an literal IP address.
    266      */
    267     public static boolean isLiteralIpAddress(String hostname) {
    268         return InetAddress.isNumeric(hostname);
    269     }
    270 
    271     /**
    272      * Wrap the SocketFactory with the platform wrapper if needed for compatability.
    273      * For the platform-bundled library we never need to wrap.
    274      */
    275     public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
    276         return factory;
    277     }
    278 
    279     /**
    280      * Convert from platform's GCMParameterSpec to our internal version.
    281      */
    282     public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
    283         if (params instanceof GCMParameterSpec) {
    284             GCMParameterSpec gcmParams = (GCMParameterSpec) params;
    285             return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
    286         }
    287         return null;
    288     }
    289 
    290     /**
    291      * Creates a platform version of {@code GCMParameterSpec}.
    292      */
    293     public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
    294         return new GCMParameterSpec(tagLenInBits, iv);
    295     }
    296 
    297     /*
    298      * CloseGuard functions.
    299      */
    300 
    301     public static CloseGuard closeGuardGet() {
    302         return CloseGuard.get();
    303     }
    304 
    305     public static void closeGuardOpen(Object guardObj, String message) {
    306         CloseGuard guard = (CloseGuard) guardObj;
    307         guard.open(message);
    308     }
    309 
    310     public static void closeGuardClose(Object guardObj) {
    311         CloseGuard guard = (CloseGuard) guardObj;
    312         guard.close();
    313     }
    314 
    315     public static void closeGuardWarnIfOpen(Object guardObj) {
    316         CloseGuard guard = (CloseGuard) guardObj;
    317         guard.warnIfOpen();
    318     }
    319 
    320     /*
    321      * BlockGuard functions.
    322      */
    323 
    324     public static void blockGuardOnNetwork() {
    325         BlockGuard.getThreadPolicy().onNetwork();
    326     }
    327 
    328     /**
    329      * OID to Algorithm Name mapping.
    330      */
    331     public static String oidToAlgorithmName(String oid) {
    332         try {
    333             return AlgorithmId.get(oid).getName();
    334         } catch (NoSuchAlgorithmException e) {
    335             return oid;
    336         }
    337     }
    338 
    339     /*
    340      * Pre-Java 8 backward compatibility.
    341      */
    342 
    343     public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
    344         return new OpenSSLExtendedSessionImpl(sslSession);
    345     }
    346 
    347     public static SSLSession unwrapSSLSession(SSLSession sslSession) {
    348         if (sslSession instanceof OpenSSLExtendedSessionImpl) {
    349             return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
    350         }
    351         return sslSession;
    352     }
    353 
    354     /*
    355      * Pre-Java-7 backward compatibility.
    356      */
    357 
    358     public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
    359         return addr.getHostString();
    360     }
    361 
    362     public static boolean isCTVerificationRequired(String hostname) {
    363         return NetworkSecurityPolicy.getInstance()
    364                 .isCertificateTransparencyVerificationRequired(hostname);
    365     }
    366 }
    367