Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright 2014 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 java.io.FileDescriptor;
     20 import java.lang.reflect.Field;
     21 import java.lang.reflect.Method;
     22 import java.net.InetSocketAddress;
     23 import java.net.Socket;
     24 import java.net.SocketException;
     25 import java.net.SocketImpl;
     26 import java.nio.channels.SocketChannel;
     27 import java.security.NoSuchAlgorithmException;
     28 import java.security.PrivateKey;
     29 import java.security.Security;
     30 import java.security.cert.CertificateException;
     31 import java.security.cert.X509Certificate;
     32 import java.security.spec.AlgorithmParameterSpec;
     33 import java.security.spec.ECParameterSpec;
     34 import java.util.Arrays;
     35 import java.util.Collections;
     36 import java.util.List;
     37 import javax.crypto.spec.GCMParameterSpec;
     38 import javax.net.ssl.SNIHostName;
     39 import javax.net.ssl.SNIServerName;
     40 import javax.net.ssl.SSLParameters;
     41 import javax.net.ssl.SSLSession;
     42 import javax.net.ssl.SSLSocketFactory;
     43 import javax.net.ssl.StandardConstants;
     44 import javax.net.ssl.X509ExtendedTrustManager;
     45 import javax.net.ssl.X509TrustManager;
     46 import sun.security.x509.AlgorithmId;
     47 
     48 /**
     49  * Platform-specific methods for OpenJDK
     50  */
     51 final class Platform {
     52     private static final String TAG = "Conscrypt";
     53 
     54     private static Method m_getCurveName;
     55     static {
     56         try {
     57             m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName");
     58             m_getCurveName.setAccessible(true);
     59         } catch (Exception ignored) {
     60         }
     61     }
     62 
     63     private Platform() {
     64     }
     65 
     66     public static void setup() {
     67     }
     68 
     69     public static FileDescriptor getFileDescriptor(Socket s) {
     70         try {
     71             SocketChannel channel = s.getChannel();
     72             if (channel != null) {
     73                 Field f_fd = channel.getClass().getDeclaredField("fd");
     74                 f_fd.setAccessible(true);
     75                 return (FileDescriptor) f_fd.get(channel);
     76             }
     77         } catch (Exception e) {
     78             // Try socket class below...
     79         }
     80 
     81         try {
     82             Field f_impl = Socket.class.getDeclaredField("impl");
     83             f_impl.setAccessible(true);
     84             Object socketImpl = f_impl.get(s);
     85             Field f_fd = SocketImpl.class.getDeclaredField("fd");
     86             f_fd.setAccessible(true);
     87             return (FileDescriptor) f_fd.get(socketImpl);
     88         } catch (Exception e) {
     89             throw new RuntimeException("Can't get FileDescriptor from socket", e);
     90         }
     91     }
     92 
     93     public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
     94         return getFileDescriptor(openSSLSocketImpl);
     95     }
     96 
     97     public static String getCurveName(ECParameterSpec spec) {
     98         if (m_getCurveName == null) {
     99             return null;
    100         }
    101         try {
    102             return (String) m_getCurveName.invoke(spec);
    103         } catch (Exception e) {
    104             return null;
    105         }
    106     }
    107 
    108     public static void setCurveName(ECParameterSpec spec, String curveName) {
    109         // This doesn't appear to be needed.
    110     }
    111 
    112     /*
    113      * Call Os.setsockoptTimeval via reflection.
    114      */
    115     public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
    116         // TODO: figure this out on the RI
    117     }
    118 
    119     public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
    120             OpenSSLSocketImpl 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     public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
    135             OpenSSLSocketImpl 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     public static void setSSLParameters(
    145             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl 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.setSniHostname(((SNIHostName) serverName).getAsciiName());
    153                     break;
    154                 }
    155             }
    156         }
    157     }
    158 
    159     public static void getSSLParameters(
    160             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
    161         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
    162         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
    163         if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
    164             params.setServerNames(Collections.<SNIServerName>singletonList(
    165                     new SNIHostName(engine.getSniHostname())));
    166         }
    167     }
    168 
    169     /**
    170      * Tries to return a Class reference of one of the supplied class names.
    171      */
    172     private static Class<?> getClass(String... klasses) {
    173         for (String klass : klasses) {
    174             try {
    175                 return Class.forName(klass);
    176             } catch (Exception ignored) {
    177             }
    178         }
    179         return null;
    180     }
    181 
    182     public static void setEndpointIdentificationAlgorithm(SSLParameters params,
    183             String endpointIdentificationAlgorithm) {
    184         params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
    185     }
    186 
    187     public static String getEndpointIdentificationAlgorithm(SSLParameters params) {
    188         return params.getEndpointIdentificationAlgorithm();
    189     }
    190 
    191     public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
    192             String authType, OpenSSLSocketImpl socket) throws CertificateException {
    193         if (tm instanceof X509ExtendedTrustManager) {
    194             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    195             x509etm.checkClientTrusted(chain, authType, socket);
    196         } else {
    197             tm.checkClientTrusted(chain, authType);
    198         }
    199     }
    200 
    201     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    202             String authType, OpenSSLSocketImpl socket) throws CertificateException {
    203         if (tm instanceof X509ExtendedTrustManager) {
    204             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    205             x509etm.checkServerTrusted(chain, authType, socket);
    206         } else {
    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 {
    217             tm.checkClientTrusted(chain, authType);
    218         }
    219     }
    220 
    221     public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
    222             String authType, OpenSSLEngineImpl engine) throws CertificateException {
    223         if (tm instanceof X509ExtendedTrustManager) {
    224             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
    225             x509etm.checkServerTrusted(chain, authType, engine);
    226         } else {
    227             tm.checkServerTrusted(chain, authType);
    228         }
    229     }
    230 
    231     /**
    232      * Wraps an old AndroidOpenSSL key instance. This is not needed on RI.
    233      */
    234     public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) {
    235         return null;
    236     }
    237 
    238     /**
    239      * Logs to the system EventLog system.
    240      */
    241     public static void logEvent(String message) {
    242     }
    243 
    244     /**
    245      * Returns true if the supplied hostname is an literal IP address.
    246      */
    247     public static boolean isLiteralIpAddress(String hostname) {
    248         // TODO: any RI API to make this better?
    249         return AddressUtils.isLiteralIpAddress(hostname);
    250     }
    251 
    252     /**
    253      * For unbundled versions, SNI is always enabled by default.
    254      */
    255     public static boolean isSniEnabledByDefault() {
    256         return true;
    257     }
    258 
    259     /**
    260      * Currently we don't wrap anything from the RI.
    261      */
    262     public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
    263         return factory;
    264     }
    265 
    266     /**
    267      * Convert from platform's GCMParameterSpec to our internal version.
    268      */
    269     public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
    270         if (params instanceof GCMParameterSpec) {
    271             GCMParameterSpec gcmParams = (GCMParameterSpec) params;
    272             return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
    273         }
    274         return null;
    275     }
    276 
    277     /**
    278      * Creates a platform version of {@code GCMParameterSpec}.
    279      */
    280     public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
    281         return new GCMParameterSpec(tagLenInBits, iv);
    282     }
    283 
    284     /*
    285      * CloseGuard functions.
    286      */
    287 
    288     public static Object closeGuardGet() {
    289         return null;
    290     }
    291 
    292     public static void closeGuardOpen(Object guardObj, String message) {
    293     }
    294 
    295     public static void closeGuardClose(Object guardObj) {
    296     }
    297 
    298     public static void closeGuardWarnIfOpen(Object guardObj) {
    299     }
    300 
    301     /*
    302      * BlockGuard functions.
    303      */
    304 
    305     public static void blockGuardOnNetwork() {
    306     }
    307 
    308     /**
    309      * OID to Algorithm Name mapping.
    310      */
    311     public static String oidToAlgorithmName(String oid) {
    312         try {
    313             return AlgorithmId.get(oid).getName();
    314         } catch (NoSuchAlgorithmException e) {
    315             return oid;
    316         }
    317     }
    318 
    319     /*
    320      * Pre-Java-8 backward compatibility.
    321      */
    322 
    323     public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
    324         return new OpenSSLExtendedSessionImpl(sslSession);
    325     }
    326 
    327     public static SSLSession unwrapSSLSession(SSLSession sslSession) {
    328         if (sslSession instanceof OpenSSLExtendedSessionImpl) {
    329             return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
    330         }
    331         return sslSession;
    332     }
    333 
    334     /*
    335      * Pre-Java-7 backward compatibility.
    336      */
    337 
    338     public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
    339         return addr.getHostString();
    340     }
    341 
    342     /**
    343      * Check if SCT verification is required for a given hostname.
    344      *
    345      * SCT Verification is enabled using {@code Security} properties.
    346      * The "conscrypt.ct.enable" property must be true, as well as a per domain property.
    347      * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce."
    348      * is used as the property name.
    349      * Basic globbing is also supported.
    350      *
    351      * For example, for the domain foo.bar.com, the following properties will be
    352      * looked up, in order of precedence.
    353      * - conscrypt.ct.enforce.com.bar.foo
    354      * - conscrypt.ct.enforce.com.bar.*
    355      * - conscrypt.ct.enforce.com.*
    356      * - conscrypt.ct.enforce.*
    357      */
    358     public static boolean isCTVerificationRequired(String hostname) {
    359         if (hostname == null) {
    360             return false;
    361         }
    362 
    363         String property = Security.getProperty("conscrypt.ct.enable");
    364         if (property == null || Boolean.valueOf(property.toLowerCase()) == false) {
    365             return false;
    366         }
    367 
    368         List<String> parts = Arrays.asList(hostname.split("\\."));
    369         Collections.reverse(parts);
    370 
    371         boolean enable = false;
    372         String propertyName = "conscrypt.ct.enforce";
    373         // The loop keeps going on even once we've found a match
    374         // This allows for finer grained settings on subdomains
    375         for (String part: parts) {
    376             property = Security.getProperty(propertyName + ".*");
    377             if (property != null) {
    378                 enable = Boolean.valueOf(property.toLowerCase());
    379             }
    380 
    381             propertyName = propertyName + "." + part;
    382         }
    383 
    384         property = Security.getProperty(propertyName);
    385         if (property != null) {
    386             enable = Boolean.valueOf(property.toLowerCase());
    387         }
    388         return enable;
    389     }
    390 }
    391