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 17package org.conscrypt; 18 19import android.os.Build; 20import android.util.Log; 21import java.io.FileDescriptor; 22import java.lang.reflect.Field; 23import java.lang.reflect.Method; 24import java.net.InetAddress; 25import java.net.Socket; 26import java.net.SocketException; 27import java.security.InvalidKeyException; 28import java.security.PrivateKey; 29import java.security.cert.CertificateException; 30import java.security.cert.X509Certificate; 31import java.security.spec.ECParameterSpec; 32 33import javax.net.ssl.SSLEngine; 34import javax.net.ssl.SSLParameters; 35import javax.net.ssl.SSLSocketFactory; 36import javax.net.ssl.X509TrustManager; 37 38/** 39 * 40 */ 41public class Platform { 42 private static final String TAG = "Conscrypt"; 43 44 private static Method m_getCurveName; 45 static { 46 try { 47 m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); 48 m_getCurveName.setAccessible(true); 49 } catch (Exception ignored) { 50 } 51 } 52 53 public static void setup() { 54 } 55 56 public static FileDescriptor getFileDescriptor(Socket s) { 57 try { 58 Field f_impl = Socket.class.getDeclaredField("impl"); 59 f_impl.setAccessible(true); 60 Object socketImpl = f_impl.get(s); 61 Class<?> c_socketImpl = Class.forName("java.net.SocketImpl"); 62 Field f_fd = c_socketImpl.getDeclaredField("fd"); 63 f_fd.setAccessible(true); 64 return (FileDescriptor) f_fd.get(socketImpl); 65 } catch (Exception e) { 66 throw new RuntimeException("Can't get FileDescriptor from socket", e); 67 } 68 } 69 70 public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) { 71 return getFileDescriptor(openSSLSocketImpl); 72 } 73 74 public static String getCurveName(ECParameterSpec spec) { 75 if (m_getCurveName == null) { 76 return null; 77 } 78 try { 79 return (String) m_getCurveName.invoke(spec); 80 } catch (Exception e) { 81 return null; 82 } 83 } 84 85 public static void setCurveName(ECParameterSpec spec, String curveName) { 86 try { 87 Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class); 88 setCurveName.invoke(spec, curveName); 89 } catch (Exception ignored) { 90 } 91 } 92 93 /* 94 * Call Os.setsockoptTimeval via reflection. 95 */ 96 public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException { 97 try { 98 Class<?> c_structTimeval = getClass("android.system.StructTimeval", 99 "libcore.io.StructTimeval"); 100 if (c_structTimeval == null) { 101 Log.w(TAG, "Cannot find StructTimeval; not setting socket write timeout"); 102 return; 103 } 104 105 Method m_fromMillis = c_structTimeval.getDeclaredMethod("fromMillis", long.class); 106 Object timeval = m_fromMillis.invoke(null, timeoutMillis); 107 108 Class<?> c_Libcore = Class.forName("libcore.io.Libcore"); 109 if (c_Libcore == null) { 110 Log.w(TAG, "Cannot find libcore.os.Libcore; not setting socket write timeout"); 111 return; 112 } 113 114 Field f_os = c_Libcore.getField("os"); 115 Object instance_os = f_os.get(null); 116 117 Class<?> c_osConstants = getClass("android.system.OsConstants", 118 "libcore.io.OsConstants"); 119 Field f_SOL_SOCKET = c_osConstants.getField("SOL_SOCKET"); 120 Field f_SO_SNDTIMEO = c_osConstants.getField("SO_SNDTIMEO"); 121 122 Method m_setsockoptTimeval = instance_os.getClass().getMethod("setsockoptTimeval", 123 FileDescriptor.class, int.class, int.class, c_structTimeval); 124 125 m_setsockoptTimeval.invoke(instance_os, getFileDescriptor(s), f_SOL_SOCKET.get(null), 126 f_SO_SNDTIMEO.get(null), timeval); 127 } catch (Exception e) { 128 Log.w(TAG, "Could not set socket write timeout: " + e.getMessage()); 129 } 130 } 131 132 /** 133 * Tries to return a Class reference of one of the supplied class names. 134 */ 135 private static Class<?> getClass(String... klasses) { 136 for (String klass : klasses) { 137 try { 138 return Class.forName(klass); 139 } catch (Exception ignored) { 140 } 141 } 142 return null; 143 } 144 145 public static void setEndpointIdentificationAlgorithm(SSLParameters params, 146 String endpointIdentificationAlgorithm) { 147 // TODO: implement this for unbundled 148 } 149 150 public static String getEndpointIdentificationAlgorithm(SSLParameters params) { 151 // TODO: implement this for unbundled 152 return null; 153 } 154 155 public static void checkServerTrusted(X509TrustManager x509tm, X509Certificate[] chain, 156 String authType, String host) throws CertificateException { 157 // TODO: use reflection to find whether we have TrustManagerImpl 158 /* 159 if (x509tm instanceof TrustManagerImpl) { 160 TrustManagerImpl tm = (TrustManagerImpl) x509tm; 161 tm.checkServerTrusted(chain, authType, host); 162 } else { 163 */ 164 x509tm.checkServerTrusted(chain, authType); 165 /* 166 } 167 */ 168 } 169 170 /** 171 * Wraps an old AndroidOpenSSL key instance. This is not needed on platform 172 * builds since we didn't backport, so return null. This code is from 173 * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java 174 */ 175 public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) { 176 // This fixup only applies to pre-JB-MR1 177 if (Build.VERSION.SDK_INT >= 17) { 178 return null; 179 } 180 181 // First, check that this is a proper instance of OpenSSLRSAPrivateKey 182 // or one of its sub-classes. 183 Class<?> superClass; 184 try { 185 superClass = Class 186 .forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey"); 187 } catch (Exception e) { 188 // This may happen if the target device has a completely different 189 // implementation of the java.security APIs, compared to vanilla 190 // Android. Highly unlikely, but still possible. 191 Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e); 192 return null; 193 } 194 if (!superClass.isInstance(javaKey)) { 195 // This may happen if the PrivateKey was not created by the 196 // "AndroidOpenSSL" 197 // provider, which should be the default. That could happen if an 198 // OEM decided 199 // to implement a different default provider. Also highly unlikely. 200 Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" 201 + javaKey.getClass().getCanonicalName()); 202 return null; 203 } 204 205 try { 206 // Use reflection to invoke the 'getOpenSSLKey()' method on 207 // the private key. This returns another Java object that wraps 208 // a native EVP_PKEY. Note that the method is final, so calling 209 // the superclass implementation is ok. 210 Method getKey = superClass.getDeclaredMethod("getOpenSSLKey"); 211 getKey.setAccessible(true); 212 Object opensslKey = null; 213 try { 214 opensslKey = getKey.invoke(javaKey); 215 } finally { 216 getKey.setAccessible(false); 217 } 218 if (opensslKey == null) { 219 // Bail when detecting OEM "enhancement". 220 Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString()); 221 return null; 222 } 223 224 // Use reflection to invoke the 'getPkeyContext' method on the 225 // result of the getOpenSSLKey(). This is an 32-bit integer 226 // which is the address of an EVP_PKEY object. Note that this 227 // method these days returns a 64-bit long, but since this code 228 // path is used for older Android versions, it may still return 229 // a 32-bit int here. To be on the safe side, we cast the return 230 // value via Number rather than directly to Integer or Long. 231 Method getPkeyContext; 232 try { 233 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext"); 234 } catch (Exception e) { 235 // Bail here too, something really not working as expected. 236 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e); 237 return null; 238 } 239 getPkeyContext.setAccessible(true); 240 long evp_pkey = 0; 241 try { 242 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue(); 243 } finally { 244 getPkeyContext.setAccessible(false); 245 } 246 if (evp_pkey == 0) { 247 // The PrivateKey is probably rotten for some reason. 248 Log.e(TAG, "getPkeyContext() returned null"); 249 return null; 250 } 251 return new OpenSSLKey(evp_pkey); 252 } catch (Exception e) { 253 Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e); 254 return null; 255 } 256 } 257 258 /** 259 * Logs to the system EventLog system. 260 */ 261 public static void logEvent(String message) { 262 try { 263 Class processClass = Class.forName("android.os.Process"); 264 Object processInstance = processClass.newInstance(); 265 Method myUidMethod = processClass.getMethod("myUid", (Class[]) null); 266 int uid = (Integer) myUidMethod.invoke(processInstance); 267 268 Class eventLogClass = Class.forName("android.util.EventLog"); 269 Object eventLogInstance = eventLogClass.newInstance(); 270 Method writeEventMethod = eventLogClass.getMethod("writeEvent", 271 new Class[] { Integer.TYPE, Object[].class }); 272 writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */, 273 new Object[] { "conscrypt", uid, message }); 274 } catch (Exception e) { 275 // Fail silently 276 } 277 } 278 279 /** 280 * Returns true if the supplied hostname is an literal IP address. 281 */ 282 public static boolean isLiteralIpAddress(String hostname) { 283 try { 284 Method m_isNumeric = InetAddress.class.getMethod("isNumeric", String.class); 285 return (Boolean) m_isNumeric.invoke(null, hostname); 286 } catch (Exception ignored) { 287 } 288 289 return AddressUtils.isLiteralIpAddress(hostname); 290 } 291 292 /** 293 * Wrap the SocketFactory with the platform wrapper if needed for compatability. 294 */ 295 public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) { 296 if (Build.VERSION.SDK_INT < 19) { 297 return new PreKitKatPlatformOpenSSLSocketAdapterFactory(factory); 298 } else if (Build.VERSION.SDK_INT < 22) { 299 return new KitKatPlatformOpenSSLSocketAdapterFactory(factory); 300 } 301 return factory; 302 } 303} 304