1 /*
2 * Copyright (C) 2017 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 #ifndef CONSCRYPT_JNIUTIL_H_
18 #define CONSCRYPT_JNIUTIL_H_
19
20 #include <jni.h>
21 #include <openssl/ssl.h>
22
23 #include <conscrypt/logging.h>
24 #include <conscrypt/macros.h>
25 #include <nativehelper/scoped_local_ref.h>
26
27 namespace conscrypt {
28 namespace jniutil {
29
30 extern JavaVM* gJavaVM;
31 extern jclass cryptoUpcallsClass;
32 extern jclass openSslInputStreamClass;
33 extern jclass nativeRefClass;
34
35 extern jclass byteArrayClass;
36 extern jclass calendarClass;
37 extern jclass objectClass;
38 extern jclass objectArrayClass;
39 extern jclass integerClass;
40 extern jclass inputStreamClass;
41 extern jclass outputStreamClass;
42 extern jclass stringClass;
43 extern jclass byteBufferClass;
44
45 extern jfieldID nativeRef_address;
46
47 extern jmethodID calendar_setMethod;
48 extern jmethodID inputStream_readMethod;
49 extern jmethodID integer_valueOfMethod;
50 extern jmethodID openSslInputStream_readLineMethod;
51 extern jmethodID outputStream_writeMethod;
52 extern jmethodID outputStream_flushMethod;
53 extern jmethodID buffer_positionMethod;
54 extern jmethodID buffer_limitMethod;
55 extern jmethodID buffer_isDirectMethod;
56 extern jmethodID cryptoUpcallsClass_rawSignMethod;
57 extern jmethodID cryptoUpcallsClass_rsaSignMethod;
58 extern jmethodID cryptoUpcallsClass_rsaDecryptMethod;
59 extern jmethodID sslHandshakeCallbacks_verifyCertificateChain;
60 extern jmethodID sslHandshakeCallbacks_onSSLStateChange;
61 extern jmethodID sslHandshakeCallbacks_clientCertificateRequested;
62 extern jmethodID sslHandshakeCallbacks_serverCertificateRequested;
63 extern jmethodID sslHandshakeCallbacks_clientPSKKeyRequested;
64 extern jmethodID sslHandshakeCallbacks_serverPSKKeyRequested;
65 extern jmethodID sslHandshakeCallbacks_onNewSessionEstablished;
66 extern jmethodID sslHandshakeCallbacks_selectApplicationProtocol;
67 extern jmethodID sslHandshakeCallbacks_serverSessionRequested;
68
69 /**
70 * Initializes the JNI constants from the environment.
71 */
72 void init(JavaVM* vm, JNIEnv* env);
73
74 /**
75 * Obtains the current thread's JNIEnv
76 */
getJNIEnv(JavaVM * gJavaVM)77 inline JNIEnv* getJNIEnv(JavaVM* gJavaVM) {
78 JNIEnv* env;
79
80 #ifdef ANDROID
81 int ret = gJavaVM->AttachCurrentThread(&env, nullptr);
82 #else
83 int ret = gJavaVM->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr);
84 #endif
85 if (ret < 0) {
86 CONSCRYPT_LOG_ERROR("Could not attach JavaVM to find current JNIEnv");
87 return nullptr;
88 }
89 return env;
90 }
91
92 /**
93 * Obtains the current thread's JNIEnv
94 */
getJNIEnv()95 inline JNIEnv* getJNIEnv() {
96 return getJNIEnv(gJavaVM);
97 }
98
getGlobalRefToClass(JNIEnv * env,const char * className)99 inline jclass getGlobalRefToClass(JNIEnv* env, const char* className) {
100 ScopedLocalRef<jclass> localClass(env, env->FindClass(className));
101 jclass globalRef = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
102 if (globalRef == nullptr) {
103 CONSCRYPT_LOG_ERROR("failed to find class %s", className);
104 abort();
105 }
106 return globalRef;
107 }
108
getMethodRef(JNIEnv * env,jclass clazz,const char * name,const char * sig)109 inline jmethodID getMethodRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
110 jmethodID localMethod = env->GetMethodID(clazz, name, sig);
111 if (localMethod == nullptr) {
112 CONSCRYPT_LOG_ERROR("could not find method %s", name);
113 abort();
114 }
115 return localMethod;
116 }
117
getFieldRef(JNIEnv * env,jclass clazz,const char * name,const char * sig)118 inline jfieldID getFieldRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) {
119 jfieldID localField = env->GetFieldID(clazz, name, sig);
120 if (localField == nullptr) {
121 CONSCRYPT_LOG_ERROR("could not find field %s", name);
122 abort();
123 }
124 return localField;
125 }
126
findClass(JNIEnv * env,const char * name)127 inline jclass findClass(JNIEnv* env, const char* name) {
128 ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
129 jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
130 if (result == nullptr) {
131 CONSCRYPT_LOG_ERROR("failed to find class '%s'", name);
132 abort();
133 }
134 return result;
135 }
136
137 /**
138 * Register one or more native methods with a particular class.
139 * "className" looks like "java/lang/String". Aborts on failure.
140 */
141 void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods,
142 int numMethods);
143
144 /**
145 * Returns the int fd from a java.io.FileDescriptor.
146 */
147 extern int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor);
148
149 /**
150 * Returns true if buffer is a non-null direct ByteBuffer instance.
151 */
152 extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer);
153
154 /**
155 * Returns true if the VM's JNI GetByteArrayElements method is likely to create a copy when
156 * invoked on an array of the provided size.
157 */
158 extern bool isGetByteArrayElementsLikelyToReturnACopy(size_t size);
159
160 /**
161 * Throw an exception with the specified class and an optional message.
162 *
163 * The "className" argument will be passed directly to FindClass, which
164 * takes strings with slashes (e.g. "java/lang/Object").
165 *
166 * If an exception is currently pending, we log a warning message and
167 * clear it.
168 *
169 * Returns 0 on success, nonzero if something failed (e.g. the exception
170 * class couldn't be found, so *an* exception will still be pending).
171 */
172 extern int throwException(JNIEnv* env, const char* className, const char* msg);
173
174 /**
175 * Throw a java.lang.RuntimeException, with an optional message.
176 */
177 extern int throwRuntimeException(JNIEnv* env, const char* msg);
178
179 #ifdef CONSCRYPT_CHECK_ERROR_QUEUE
180 /**
181 * Throw a java.lang.AssertionError, with an optional message.
182 */
183 extern int throwAssertionError(JNIEnv* env, const char* msg);
184 #endif
185
186 /*
187 * Throw a java.lang.NullPointerException, with an optional message.
188 */
189 extern int throwNullPointerException(JNIEnv* env, const char* msg);
190
191 /**
192 * Throws a OutOfMemoryError with the given string as a message.
193 */
194 extern int throwOutOfMemory(JNIEnv* env, const char* message);
195
196 /**
197 * Throws a BadPaddingException with the given string as a message.
198 */
199 extern int throwBadPaddingException(JNIEnv* env, const char* message);
200
201 /**
202 * Throws a SignatureException with the given string as a message.
203 */
204 extern int throwSignatureException(JNIEnv* env, const char* message);
205
206 /**
207 * Throws a InvalidKeyException with the given string as a message.
208 */
209 extern int throwInvalidKeyException(JNIEnv* env, const char* message);
210
211 /**
212 * Throws a SignatureException with the given string as a message.
213 */
214 extern int throwIllegalBlockSizeException(JNIEnv* env, const char* message);
215
216 /**
217 * Throws a NoSuchAlgorithmException with the given string as a message.
218 */
219 extern int throwNoSuchAlgorithmException(JNIEnv* env, const char* message);
220
221 /**
222 * Throws an IOException with the given string as a message.
223 */
224 extern int throwIOException(JNIEnv* env, const char* message);
225
226 /**
227 * Throws a CertificateException with the given string as a message.
228 */
229 extern int throwCertificateException(JNIEnv* env, const char* message);
230
231 /**
232 * Throws a ParsingException with the given string as a message.
233 */
234 extern int throwParsingException(JNIEnv* env, const char* message);
235
236 extern int throwInvalidAlgorithmParameterException(JNIEnv* env, const char* message);
237
238 extern int throwForAsn1Error(JNIEnv* env, int reason, const char* message,
239 int (*defaultThrow)(JNIEnv*, const char*));
240
241 extern int throwForCipherError(JNIEnv* env, int reason, const char* message,
242 int (*defaultThrow)(JNIEnv*, const char*));
243
244 extern int throwForEvpError(JNIEnv* env, int reason, const char* message,
245 int (*defaultThrow)(JNIEnv*, const char*));
246
247 extern int throwForRsaError(JNIEnv* env, int reason, const char* message,
248 int (*defaultThrow)(JNIEnv*, const char*));
249
250 extern int throwForX509Error(JNIEnv* env, int reason, const char* message,
251 int (*defaultThrow)(JNIEnv*, const char*));
252
253 /*
254 * Checks this thread's OpenSSL error stack and throws an appropriate exception
255 * type based on the type of error found. If no error is present, throws
256 * AssertionError.
257 */
258 extern void throwExceptionFromBoringSSLError(
259 JNIEnv* env, const char* location,
260 int (*defaultThrow)(JNIEnv*, const char*) = throwRuntimeException);
261
262 /**
263 * Throws an SocketTimeoutException with the given string as a message.
264 */
265 extern int throwSocketTimeoutException(JNIEnv* env, const char* message);
266
267 /**
268 * Throws a javax.net.ssl.SSLException with the given string as a message.
269 */
270 extern int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message);
271
272 /**
273 * Throws a javax.net.ssl.SSLException with the given string as a message.
274 */
275 extern int throwSSLExceptionStr(JNIEnv* env, const char* message);
276
277 /**
278 * Throws a javax.net.ssl.SSLProcotolException with the given string as a message.
279 */
280 extern int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message);
281
282 /**
283 * Throws an SSLException with a message constructed from the current
284 * SSL errors. This will also log the errors.
285 *
286 * @param env the JNI environment
287 * @param ssl the possibly null SSL
288 * @param sslErrorCode error code returned from SSL_get_error() or
289 * SSL_ERROR_NONE to probe with ERR_get_error
290 * @param message null-ok; general error message
291 */
292 extern int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode,
293 const char* message,
294 int (*actualThrow)(JNIEnv*,
295 const char*) = throwSSLExceptionStr);
296
297 #ifdef CONSCRYPT_CHECK_ERROR_QUEUE
298 /**
299 * Class that checks that the error queue is empty on destruction. It should only be used
300 * via the macro CHECK_ERROR_QUEUE_ON_RETURN, which can be placed at the top of a function to
301 * ensure that the error queue is empty whenever the function exits.
302 */
303 class ErrorQueueChecker {
304 public:
ErrorQueueChecker(JNIEnv * env)305 explicit ErrorQueueChecker(JNIEnv* env) : env(env) {}
~ErrorQueueChecker()306 ~ErrorQueueChecker() {
307 if (ERR_peek_error() != 0) {
308 const char* file;
309 int line;
310 uint32_t error = ERR_get_error_line(&file, &line);
311 char message[256];
312 ERR_error_string_n(error, message, sizeof(message));
313 char result[500];
314 snprintf(result, sizeof(result),
315 "Error queue should have been empty but was (%s:%d) %s", file, line, message);
316 // If there's a pending exception, we want to throw the assertion error instead
317 env->ExceptionClear();
318 throwAssertionError(env, result);
319 }
320 }
321
322 private:
323 JNIEnv* env;
324 };
325
326 #define CHECK_ERROR_QUEUE_ON_RETURN conscrypt::jniutil::ErrorQueueChecker __checker(env)
327 #else
328 #define CHECK_ERROR_QUEUE_ON_RETURN UNUSED_ARGUMENT(env)
329 #endif // CONSCRYPT_CHECK_ERROR_QUEUE
330
331 } // namespace jniutil
332 } // namespace conscrypt
333
334 #endif // CONSCRYPT_JNIUTIL_H_
335