From 96b3d0d6c6d86e140bebdb6b6cd96d2925097e23 Mon Sep 17 00:00:00 2001 From: Sebb Date: Sun, 26 Nov 2023 23:56:52 +0000 Subject: [PATCH] Crypto 179 - Crash on macOS with default crypto library (#282) * CRYPTO-179 Crash on macOS with default crypto lib [skip ci] --- src/changes/changes.xml | 1 + .../org/apache/commons/crypto/Crypto.java | 5 ++ .../commons/crypto/jna/OpenSslMacOS.java | 49 +++++++++++++++++++ .../commons/crypto/jna/OpenSslNativeJna.java | 13 +++++ .../org/apache/commons/crypto/DynamicLoader.c | 12 +++++ 5 files changed, 80 insertions(+) create mode 100644 src/main/java/org/apache/commons/crypto/jna/OpenSslMacOS.java diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 25959d4a2..e426c7882 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -65,6 +65,7 @@ + Crash on macOS with default crypto library Library is reloaded multiple times JNA tests rely on JNI code OpenSslCryptoRandom.isNativeCodeEnabled() throws if library cannot be loaded diff --git a/src/main/java/org/apache/commons/crypto/Crypto.java b/src/main/java/org/apache/commons/crypto/Crypto.java index e8d08295b..5aca20adf 100644 --- a/src/main/java/org/apache/commons/crypto/Crypto.java +++ b/src/main/java/org/apache/commons/crypto/Crypto.java @@ -106,6 +106,11 @@ private static Properties getComponentProperties() { /** Default name for loading SSL crypto library using JNA */ public static final String JNA_LIBRARY_NAME_DEFAULT = "crypto"; + /** + * Name for loading SSL crypto library using dlopen on macOS + * JNA automatically adds prefix and suffix; dlopen does not + */ + public static final String MACOS_LIBRARY_NAME_DEFAULT = "libcrypto.dylib"; private static boolean quiet; diff --git a/src/main/java/org/apache/commons/crypto/jna/OpenSslMacOS.java b/src/main/java/org/apache/commons/crypto/jna/OpenSslMacOS.java new file mode 100644 index 000000000..b4165de75 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/jna/OpenSslMacOS.java @@ -0,0 +1,49 @@ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package org.apache.commons.crypto.jna; + +import com.sun.jna.Native; + +/* + * Get access to dlopen_preflight from JNA code + * For use on macOS only - CRYPTO-179 + */ +class OpenSslMacOS { + + static native boolean dlopen_preflight(String path); + + static native String dlerror(); + + static { + Native.register((String)null); + } + + /** + * Check if can load library OK + * @param path + * @return null if OK, else error message + */ + public static String checkLibrary(String path) { + if (dlopen_preflight(path)){ + return null; + } + return dlerror(); + } + +} diff --git a/src/main/java/org/apache/commons/crypto/jna/OpenSslNativeJna.java b/src/main/java/org/apache/commons/crypto/jna/OpenSslNativeJna.java index 8662b0264..c4b1d1e38 100644 --- a/src/main/java/org/apache/commons/crypto/jna/OpenSslNativeJna.java +++ b/src/main/java/org/apache/commons/crypto/jna/OpenSslNativeJna.java @@ -55,6 +55,19 @@ final class OpenSslNativeJna { OpenSslJna.debug("OpenSslNativeJna static init start"); final String libraryName = System.getProperty(Crypto.JNA_LIBRARY_NAME, Crypto.JNA_LIBRARY_NAME_DEFAULT); OpenSslJna.debug("OpenSslNativeJna NativeLibrary.getInstance('%s')", libraryName); + // CRYPTO-179 - avoid crash + if ("Mac OS X".equals(System.getProperty("os.name")) + && System.getProperty(Crypto.JNA_LIBRARY_NAME, "").isEmpty() + && System.getProperty(Crypto.JNA_LIBRARY_PATH, "").isEmpty() + ) { + String ret = OpenSslMacOS.checkLibrary(Crypto.MACOS_LIBRARY_NAME_DEFAULT); + if (ret != null) { + throw new UnsatisfiedLinkError( + String.format("Cannot load default library '%s'; need jni.library.path! (%s)", + Crypto.MACOS_LIBRARY_NAME_DEFAULT, ret)); + } + } + System.err.println("Lib check4" ); @SuppressWarnings("resource") // NativeLibrary.getInstance returns a singleton final NativeLibrary crypto = NativeLibrary.getInstance(libraryName); OpenSslJna.debug("OpenSslNativeJna NativeLibrary.getInstance('%s') -> %s", libraryName, crypto); diff --git a/src/main/native/org/apache/commons/crypto/DynamicLoader.c b/src/main/native/org/apache/commons/crypto/DynamicLoader.c index 1f2656276..68b731a3d 100644 --- a/src/main/native/org/apache/commons/crypto/DynamicLoader.c +++ b/src/main/native/org/apache/commons/crypto/DynamicLoader.c @@ -39,6 +39,18 @@ HMODULE open_library(JNIEnv *env) } } } +#ifdef MAC_OS + #include + if (0 == strncmp(COMMONS_CRYPTO_OPENSSL_LIBRARY,libraryPath, sizeof(COMMONS_CRYPTO_OPENSSL_LIBRARY))) { + bool ret = dlopen_preflight(libraryPath); + if (!ret) { + char msg[1000]; + snprintf(msg, sizeof(msg), "Cannot load default library '%s'; need jni.library.path! (%s)", libraryPath, dlerror()); + THROW(env, "java/lang/UnsatisfiedLinkError", msg); + return 0; + } + } +#endif #ifdef UNIX openssl = dlopen(libraryPath, RTLD_LAZY | RTLD_GLOBAL); #endif