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