From 8735eee662d663f1d11a776ef5786d4f2ec465c6 Mon Sep 17 00:00:00 2001 From: albexk Date: Thu, 19 Sep 2024 23:38:58 +0300 Subject: [PATCH] Exclude protocol libraries from loading at application startup --- client/android/cloak/src/main/kotlin/Cloak.kt | 6 ++ .../amnezia/vpn/protocol/openvpn/OpenVpn.kt | 6 +- .../protocolApi/src/main/kotlin/Exceptions.kt | 1 - .../protocolApi/src/main/kotlin/Protocol.kt | 54 --------------- client/android/res/values/libs.xml | 1 - .../src/org/amnezia/vpn/AmneziaActivity.kt | 13 ++++ .../src/org/amnezia/vpn/AmneziaVpnService.kt | 2 +- .../utils/src/main/kotlin/LibraryLoader.kt | 66 +++++++++++++++++++ .../vpn/protocol/wireguard/Wireguard.kt | 2 +- 9 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 client/android/utils/src/main/kotlin/LibraryLoader.kt diff --git a/client/android/cloak/src/main/kotlin/Cloak.kt b/client/android/cloak/src/main/kotlin/Cloak.kt index 18a5e6c7..d408fb19 100644 --- a/client/android/cloak/src/main/kotlin/Cloak.kt +++ b/client/android/cloak/src/main/kotlin/Cloak.kt @@ -3,10 +3,16 @@ package org.amnezia.vpn.protocol.cloak import android.util.Base64 import net.openvpn.ovpn3.ClientAPI_Config import org.amnezia.vpn.protocol.openvpn.OpenVpn +import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary import org.json.JSONObject class Cloak : OpenVpn() { + override fun internalInit() { + super.internalInit() + if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin") + } + override fun parseConfig(config: JSONObject): ClientAPI_Config { val openVpnConfig = ClientAPI_Config() diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index fa0e19f1..22fe35cd 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -11,6 +11,7 @@ import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.VpnStartException +import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary import org.amnezia.vpn.util.net.InetNetwork import org.amnezia.vpn.util.net.getLocalNetworks import org.amnezia.vpn.util.net.parseInetAddress @@ -34,7 +35,10 @@ open class OpenVpn : Protocol() { } override fun internalInit() { - if (!isInitialized) loadSharedLibrary(context, "ovpn3") + if (!isInitialized) { + loadSharedLibrary(context, "ovpn3") + loadSharedLibrary(context, "ovpnutil") + } if (this::scope.isInitialized) { scope.cancel() } diff --git a/client/android/protocolApi/src/main/kotlin/Exceptions.kt b/client/android/protocolApi/src/main/kotlin/Exceptions.kt index 739a327c..b80648b0 100644 --- a/client/android/protocolApi/src/main/kotlin/Exceptions.kt +++ b/client/android/protocolApi/src/main/kotlin/Exceptions.kt @@ -2,7 +2,6 @@ package org.amnezia.vpn.protocol sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) -class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) diff --git a/client/android/protocolApi/src/main/kotlin/Protocol.kt b/client/android/protocolApi/src/main/kotlin/Protocol.kt index 24cbc595..b5c382be 100644 --- a/client/android/protocolApi/src/main/kotlin/Protocol.kt +++ b/client/android/protocolApi/src/main/kotlin/Protocol.kt @@ -158,60 +158,6 @@ abstract class Protocol { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) vpnBuilder.setMetered(false) } - - companion object { - private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean { - Log.d(TAG, "Extracting library: $libraryName") - val apks = hashSetOf() - context.applicationInfo.run { - sourceDir?.let { apks += it } - splitSourceDirs?.let { apks += it } - } - for (abi in Build.SUPPORTED_ABIS) { - for (apk in apks) { - ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile -> - val mappedName = System.mapLibraryName(libraryName) - val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator) - val zipEntry = zipFile.getEntry(libraryZipPath) - zipEntry?.let { - Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}") - FileOutputStream(destination).use { outStream -> - zipFile.getInputStream(zipEntry).use { inStream -> - inStream.copyTo(outStream, 32 * 1024) - outStream.fd.sync() - } - } - } - return true - } - } - } - return false - } - - @SuppressLint("UnsafeDynamicallyLoadedCode") - fun loadSharedLibrary(context: Context, libraryName: String) { - Log.d(TAG, "Loading library: $libraryName") - try { - System.loadLibrary(libraryName) - return - } catch (_: UnsatisfiedLinkError) { - Log.d(TAG, "Failed to load library, try to extract it from apk") - } - var tempFile: File? = null - try { - tempFile = File.createTempFile("lib", ".so", context.codeCacheDir) - if (extractLibrary(context, libraryName, tempFile)) { - System.load(tempFile.absolutePath) - return - } - } catch (e: Exception) { - throw LoadLibraryException("Failed to load library apk: $libraryName", e) - } finally { - tempFile?.delete() - } - } - } } private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask) diff --git a/client/android/res/values/libs.xml b/client/android/res/values/libs.xml index fe63866f..3ccf1d80 100644 --- a/client/android/res/values/libs.xml +++ b/client/android/res/values/libs.xml @@ -3,7 +3,6 @@ - diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 9d1c31cb..d5026425 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.withContext import org.amnezia.vpn.protocol.getStatistics import org.amnezia.vpn.protocol.getStatus import org.amnezia.vpn.qt.QtAndroidController +import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Prefs import org.json.JSONException @@ -158,6 +159,7 @@ class AmneziaActivity : QtActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "Create Amnezia activity: $intent") + loadLibs() window.apply { addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) statusBarColor = getColor(R.color.black) @@ -179,6 +181,17 @@ class AmneziaActivity : QtActivity() { runBlocking { vpnProto = proto.await() } } + private fun loadLibs() { + listOf( + "rsapss", + "crypto_3", + "ssl_3", + "ssh" + ).forEach { + loadSharedLibrary(this.applicationContext, it) + } + } + private fun registerBroadcastReceivers() { notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { registerBroadcastReceiver( diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt index 54330861..6a7da7c7 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -40,7 +40,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.amnezia.vpn.protocol.BadConfigException -import org.amnezia.vpn.protocol.LoadLibraryException import org.amnezia.vpn.protocol.ProtocolState.CONNECTED import org.amnezia.vpn.protocol.ProtocolState.CONNECTING import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED @@ -50,6 +49,7 @@ import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN import org.amnezia.vpn.protocol.VpnException import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.putStatus +import org.amnezia.vpn.util.LoadLibraryException import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.Prefs import org.amnezia.vpn.util.net.NetworkState diff --git a/client/android/utils/src/main/kotlin/LibraryLoader.kt b/client/android/utils/src/main/kotlin/LibraryLoader.kt new file mode 100644 index 00000000..f1c6465e --- /dev/null +++ b/client/android/utils/src/main/kotlin/LibraryLoader.kt @@ -0,0 +1,66 @@ +package org.amnezia.vpn.util + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Build +import java.io.File +import java.io.FileOutputStream +import java.util.zip.ZipFile + +private const val TAG = "LibraryLoader" + +object LibraryLoader { + private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean { + Log.d(TAG, "Extracting library: $libraryName") + val apks = hashSetOf() + context.applicationInfo.run { + sourceDir?.let { apks += it } + splitSourceDirs?.let { apks += it } + } + for (abi in Build.SUPPORTED_ABIS) { + for (apk in apks) { + ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile -> + val mappedName = System.mapLibraryName(libraryName) + val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator) + val zipEntry = zipFile.getEntry(libraryZipPath) + zipEntry?.let { + Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}") + FileOutputStream(destination).use { outStream -> + zipFile.getInputStream(zipEntry).use { inStream -> + inStream.copyTo(outStream, 32 * 1024) + outStream.fd.sync() + } + } + } + return true + } + } + } + return false + } + + @SuppressLint("UnsafeDynamicallyLoadedCode") + fun loadSharedLibrary(context: Context, libraryName: String) { + Log.d(TAG, "Loading library: $libraryName") + try { + System.loadLibrary(libraryName) + return + } catch (_: UnsatisfiedLinkError) { + Log.d(TAG, "Failed to load library, try to extract it from apk") + } + var tempFile: File? = null + try { + tempFile = File.createTempFile("lib", ".so", context.codeCacheDir) + if (extractLibrary(context, libraryName, tempFile)) { + System.load(tempFile.absolutePath) + return + } + } catch (e: Exception) { + throw LoadLibraryException("Failed to load library apk: $libraryName", e) + } finally { + tempFile?.delete() + } + } +} + +class LoadLibraryException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index c32ab8c2..31e7f9be 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -3,7 +3,6 @@ package org.amnezia.vpn.protocol.wireguard import android.net.VpnService.Builder import java.io.IOException import java.util.Locale -import java.util.TreeMap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext @@ -13,6 +12,7 @@ import org.amnezia.vpn.protocol.ProtocolState.CONNECTED import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.VpnStartException +import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary import org.amnezia.vpn.util.Log import org.amnezia.vpn.util.asSequence import org.amnezia.vpn.util.net.InetEndpoint