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