| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef CONSCRYPT_JNIUTIL_H_ |
| #define CONSCRYPT_JNIUTIL_H_ |
| |
| #include <jni.h> |
| #include <openssl/ssl.h> |
| |
| #include <conscrypt/logging.h> |
| #include <conscrypt/macros.h> |
| #include <nativehelper/scoped_local_ref.h> |
| |
| namespace conscrypt { |
| namespace jniutil { |
| |
| extern JavaVM* gJavaVM; |
| extern jclass cryptoUpcallsClass; |
| extern jclass openSslInputStreamClass; |
| extern jclass nativeRefClass; |
| |
| extern jclass byteArrayClass; |
| extern jclass calendarClass; |
| extern jclass objectClass; |
| extern jclass objectArrayClass; |
| extern jclass integerClass; |
| extern jclass inputStreamClass; |
| extern jclass outputStreamClass; |
| extern jclass stringClass; |
| extern jclass byteBufferClass; |
| |
| extern jfieldID nativeRef_address; |
| |
| extern jmethodID calendar_setMethod; |
| extern jmethodID inputStream_readMethod; |
| extern jmethodID integer_valueOfMethod; |
| extern jmethodID openSslInputStream_readLineMethod; |
| extern jmethodID outputStream_writeMethod; |
| extern jmethodID outputStream_flushMethod; |
| extern jmethodID buffer_positionMethod; |
| extern jmethodID buffer_limitMethod; |
| extern jmethodID buffer_isDirectMethod; |
| extern jmethodID cryptoUpcallsClass_rawSignMethod; |
| extern jmethodID cryptoUpcallsClass_rsaSignMethod; |
| extern jmethodID cryptoUpcallsClass_rsaDecryptMethod; |
| extern jmethodID sslHandshakeCallbacks_verifyCertificateChain; |
| extern jmethodID sslHandshakeCallbacks_onSSLStateChange; |
| extern jmethodID sslHandshakeCallbacks_clientCertificateRequested; |
| extern jmethodID sslHandshakeCallbacks_serverCertificateRequested; |
| extern jmethodID sslHandshakeCallbacks_clientPSKKeyRequested; |
| extern jmethodID sslHandshakeCallbacks_serverPSKKeyRequested; |
| extern jmethodID sslHandshakeCallbacks_onNewSessionEstablished; |
| extern jmethodID sslHandshakeCallbacks_selectApplicationProtocol; |
| extern jmethodID sslHandshakeCallbacks_serverSessionRequested; |
| |
| /** |
| * Initializes the JNI constants from the environment. |
| */ |
| void init(JavaVM* vm, JNIEnv* env); |
| |
| /** |
| * Obtains the current thread's JNIEnv |
| */ |
| inline JNIEnv* getJNIEnv(JavaVM* gJavaVM) { |
| JNIEnv* env; |
| |
| #ifdef ANDROID |
| int ret = gJavaVM->AttachCurrentThread(&env, nullptr); |
| #else |
| int ret = gJavaVM->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr); |
| #endif |
| if (ret < 0) { |
| CONSCRYPT_LOG_ERROR("Could not attach JavaVM to find current JNIEnv"); |
| return nullptr; |
| } |
| return env; |
| } |
| |
| /** |
| * Obtains the current thread's JNIEnv |
| */ |
| inline JNIEnv* getJNIEnv() { |
| return getJNIEnv(gJavaVM); |
| } |
| |
| inline jclass getGlobalRefToClass(JNIEnv* env, const char* className) { |
| ScopedLocalRef<jclass> localClass(env, env->FindClass(className)); |
| jclass globalRef = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); |
| if (globalRef == nullptr) { |
| CONSCRYPT_LOG_ERROR("failed to find class %s", className); |
| abort(); |
| } |
| return globalRef; |
| } |
| |
| inline jmethodID getMethodRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) { |
| jmethodID localMethod = env->GetMethodID(clazz, name, sig); |
| if (localMethod == nullptr) { |
| CONSCRYPT_LOG_ERROR("could not find method %s", name); |
| abort(); |
| } |
| return localMethod; |
| } |
| |
| inline jfieldID getFieldRef(JNIEnv* env, jclass clazz, const char* name, const char* sig) { |
| jfieldID localField = env->GetFieldID(clazz, name, sig); |
| if (localField == nullptr) { |
| CONSCRYPT_LOG_ERROR("could not find field %s", name); |
| abort(); |
| } |
| return localField; |
| } |
| |
| inline jclass findClass(JNIEnv* env, const char* name) { |
| ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); |
| jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); |
| if (result == nullptr) { |
| CONSCRYPT_LOG_ERROR("failed to find class '%s'", name); |
| abort(); |
| } |
| return result; |
| } |
| |
| /** |
| * Register one or more native methods with a particular class. |
| * "className" looks like "java/lang/String". Aborts on failure. |
| */ |
| void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, |
| int numMethods); |
| |
| /** |
| * Returns the int fd from a java.io.FileDescriptor. |
| */ |
| extern int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor); |
| |
| /** |
| * Returns true if buffer is a non-null direct ByteBuffer instance. |
| */ |
| extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer); |
| |
| /** |
| * Returns true if the VM's JNI GetByteArrayElements method is likely to create a copy when |
| * invoked on an array of the provided size. |
| */ |
| extern bool isGetByteArrayElementsLikelyToReturnACopy(size_t size); |
| |
| /** |
| * Throw an exception with the specified class and an optional message. |
| * |
| * The "className" argument will be passed directly to FindClass, which |
| * takes strings with slashes (e.g. "java/lang/Object"). |
| * |
| * If an exception is currently pending, we log a warning message and |
| * clear it. |
| * |
| * Returns 0 on success, nonzero if something failed (e.g. the exception |
| * class couldn't be found, so *an* exception will still be pending). |
| */ |
| extern int throwException(JNIEnv* env, const char* className, const char* msg); |
| |
| /** |
| * Throw a java.lang.RuntimeException, with an optional message. |
| */ |
| extern int throwRuntimeException(JNIEnv* env, const char* msg); |
| |
| #ifdef CONSCRYPT_CHECK_ERROR_QUEUE |
| /** |
| * Throw a java.lang.AssertionError, with an optional message. |
| */ |
| extern int throwAssertionError(JNIEnv* env, const char* msg); |
| #endif |
| |
| /* |
| * Throw a java.lang.NullPointerException, with an optional message. |
| */ |
| extern int throwNullPointerException(JNIEnv* env, const char* msg); |
| |
| /** |
| * Throws a OutOfMemoryError with the given string as a message. |
| */ |
| extern int throwOutOfMemory(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a BadPaddingException with the given string as a message. |
| */ |
| extern int throwBadPaddingException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a SignatureException with the given string as a message. |
| */ |
| extern int throwSignatureException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a InvalidKeyException with the given string as a message. |
| */ |
| extern int throwInvalidKeyException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a SignatureException with the given string as a message. |
| */ |
| extern int throwIllegalBlockSizeException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a NoSuchAlgorithmException with the given string as a message. |
| */ |
| extern int throwNoSuchAlgorithmException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws an IOException with the given string as a message. |
| */ |
| extern int throwIOException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a CertificateException with the given string as a message. |
| */ |
| extern int throwCertificateException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a ParsingException with the given string as a message. |
| */ |
| extern int throwParsingException(JNIEnv* env, const char* message); |
| |
| extern int throwInvalidAlgorithmParameterException(JNIEnv* env, const char* message); |
| |
| extern int throwForAsn1Error(JNIEnv* env, int reason, const char* message, |
| int (*defaultThrow)(JNIEnv*, const char*)); |
| |
| extern int throwForCipherError(JNIEnv* env, int reason, const char* message, |
| int (*defaultThrow)(JNIEnv*, const char*)); |
| |
| extern int throwForEvpError(JNIEnv* env, int reason, const char* message, |
| int (*defaultThrow)(JNIEnv*, const char*)); |
| |
| extern int throwForRsaError(JNIEnv* env, int reason, const char* message, |
| int (*defaultThrow)(JNIEnv*, const char*)); |
| |
| extern int throwForX509Error(JNIEnv* env, int reason, const char* message, |
| int (*defaultThrow)(JNIEnv*, const char*)); |
| |
| /* |
| * Checks this thread's OpenSSL error stack and throws an appropriate exception |
| * type based on the type of error found. If no error is present, throws |
| * AssertionError. |
| */ |
| extern void throwExceptionFromBoringSSLError( |
| JNIEnv* env, const char* location, |
| int (*defaultThrow)(JNIEnv*, const char*) = throwRuntimeException); |
| |
| /** |
| * Throws an SocketTimeoutException with the given string as a message. |
| */ |
| extern int throwSocketTimeoutException(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a javax.net.ssl.SSLException with the given string as a message. |
| */ |
| extern int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a javax.net.ssl.SSLException with the given string as a message. |
| */ |
| extern int throwSSLExceptionStr(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws a javax.net.ssl.SSLProcotolException with the given string as a message. |
| */ |
| extern int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message); |
| |
| /** |
| * Throws an SSLException with a message constructed from the current |
| * SSL errors. This will also log the errors. |
| * |
| * @param env the JNI environment |
| * @param ssl the possibly null SSL |
| * @param sslErrorCode error code returned from SSL_get_error() or |
| * SSL_ERROR_NONE to probe with ERR_get_error |
| * @param message null-ok; general error message |
| */ |
| extern int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode, |
| const char* message, |
| int (*actualThrow)(JNIEnv*, |
| const char*) = throwSSLExceptionStr); |
| |
| #ifdef CONSCRYPT_CHECK_ERROR_QUEUE |
| /** |
| * Class that checks that the error queue is empty on destruction. It should only be used |
| * via the macro CHECK_ERROR_QUEUE_ON_RETURN, which can be placed at the top of a function to |
| * ensure that the error queue is empty whenever the function exits. |
| */ |
| class ErrorQueueChecker { |
| public: |
| explicit ErrorQueueChecker(JNIEnv* env) : env(env) {} |
| ~ErrorQueueChecker() { |
| if (ERR_peek_error() != 0) { |
| const char* file; |
| int line; |
| uint32_t error = ERR_get_error_line(&file, &line); |
| char message[256]; |
| ERR_error_string_n(error, message, sizeof(message)); |
| char result[500]; |
| snprintf(result, sizeof(result), |
| "Error queue should have been empty but was (%s:%d) %s", file, line, message); |
| // If there's a pending exception, we want to throw the assertion error instead |
| env->ExceptionClear(); |
| throwAssertionError(env, result); |
| } |
| } |
| |
| private: |
| JNIEnv* env; |
| }; |
| |
| #define CHECK_ERROR_QUEUE_ON_RETURN conscrypt::jniutil::ErrorQueueChecker __checker(env) |
| #else |
| #define CHECK_ERROR_QUEUE_ON_RETURN UNUSED_ARGUMENT(env) |
| #endif // CONSCRYPT_CHECK_ERROR_QUEUE |
| |
| } // namespace jniutil |
| } // namespace conscrypt |
| |
| #endif // CONSCRYPT_JNIUTIL_H_ |