comp500 / SSLSocks

stunnel for Android GUI, allows tunneling over TLS
GNU General Public License v3.0
129 stars 54 forks source link

Caused by: java.io.IOException: error=13, Permission denied #23

Open AmanArora1987 opened 3 years ago

AmanArora1987 commented 3 years ago

Hi,

I am getting below error on device Android 11 (OnePlus 8)

E/StunnelProcessManager: failure
    java.io.IOException: Cannot run program "/data/user/0/link.infra.sslsocks/files/stunnel" (in directory "/data/user/0/link.infra.sslsocks/files"): error=13, Permission denied
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:1050)
        at java.lang.Runtime.exec(Runtime.java:699)
        at java.lang.Runtime.exec(Runtime.java:529)
        at link.infra.sslsocks.service.StunnelProcessManager.start(StunnelProcessManager.java:106)
        at link.infra.sslsocks.service.StunnelIntentService.handleStart(StunnelIntentService.java:81)
        at link.infra.sslsocks.service.StunnelIntentService.onHandleIntent(StunnelIntentService.java:69)
        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:77)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:245)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.io.IOException: error=13, Permission denied
        at java.lang.UNIXProcess.forkAndExec(Native Method)
        at java.lang.UNIXProcess.<init>(UNIXProcess.java:133)
        at java.lang.ProcessImpl.start(ProcessImpl.java:141)
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
lo1ol commented 3 years ago

Hi, it's new demand of Android Q: application can't run executable from application files dir: https://stackoverflow.com/questions/60370424/permission-is-denied-using-android-q-ffmpeg-error-13-permission-denied

Stunnel app has to be inside /data/app/${sslsocks}/lib/${arch} dir. I try to create a patch for this

lo1ol commented 3 years ago

This patch works for me. Notice you have to put stunnel to app/src/main/jniLibs/${arch}/ dir and rename it to stunnel.so.

For example: app/src/main/jniLibs/arm64-v8a/stunnel.so or: app/src/main/jniLibs/armeabi-v7a/stunnel.so

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5cdc764..b73ec30 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
         android:theme="@style/AppTheme"
+        android:extractNativeLibs = "true"
         tools:ignore="GoogleAppIndexingWarning">
         <activity
             android:name=".gui.AdvancedSettingsActivity"
diff --git a/app/src/main/java/link/infra/sslsocks/Constants.java b/app/src/main/java/link/infra/sslsocks/Constants.java
index 150decb..f8026dd 100644
--- a/app/src/main/java/link/infra/sslsocks/Constants.java
+++ b/app/src/main/java/link/infra/sslsocks/Constants.java
@@ -1,7 +1,7 @@
 package link.infra.sslsocks;

 public class Constants {
-   public static final String EXECUTABLE = "stunnel";
+   public static final String EXECUTABLE = "stunnel.so";
    public static final String CONFIG = "config.conf";
    public static final String PSKSECRETS = "psksecrets.txt";
    public static final String PID = "pid";
diff --git a/app/src/main/java/link/infra/sslsocks/gui/main/MainActivity.java b/app/src/main/java/link/infra/sslsocks/gui/main/MainActivity.java
index f247412..5ac985a 100644
--- a/app/src/main/java/link/infra/sslsocks/gui/main/MainActivity.java
+++ b/app/src/main/java/link/infra/sslsocks/gui/main/MainActivity.java
@@ -85,7 +85,7 @@ public class MainActivity extends AppCompatActivity implements KeyFragment.OnLis
        });

        // attempt extraction in activity, to make service start faster
-       StunnelProcessManager.checkAndExtract(this);
+       StunnelProcessManager.checkStunnel(this);
        StunnelProcessManager.setupConfig(this);

        // Create the NotificationChannel, but only on API 26+ because
diff --git a/app/src/main/java/link/infra/sslsocks/service/StunnelProcessManager.java b/app/src/main/java/link/infra/sslsocks/service/StunnelProcessManager.java
index 75cf422..266a59e 100644
--- a/app/src/main/java/link/infra/sslsocks/service/StunnelProcessManager.java
+++ b/app/src/main/java/link/infra/sslsocks/service/StunnelProcessManager.java
@@ -2,11 +2,14 @@ package link.infra.sslsocks.service;

 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
 import android.content.res.AssetManager;
 import android.util.Log;

 import androidx.preference.PreferenceManager;

+import android.content.pm.PackageManager;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InterruptedIOException;
@@ -40,27 +43,25 @@ public class StunnelProcessManager {
        return false;
    }

-   public static void checkAndExtract(Context context) {
-       File execFile = new File(context.getFilesDir().getPath() + "/" + EXECUTABLE);
-       if (execFile.exists() && !hasBeenUpdated(context)) {
-           return; // already extracted
-       }
+   private static String getExecDir(Context context) {
+       String libraryDir = null;
+       try {
+           PackageManager p = context.getPackageManager();
+           libraryDir = p.getApplicationInfo("link.infra.sslsocks", 0).nativeLibraryDir;
+       } catch(PackageManager.NameNotFoundException e) {

-       //noinspection ResultOfMethodCallIgnored
-       execFile.getParentFile().mkdir();
+       }

-       // Extract stunnel exectuable
-       AssetManager am = context.getAssets();
-       try (BufferedSource in = Okio.buffer(Okio.source(am.open(EXECUTABLE)));
-            BufferedSink out = Okio.buffer(Okio.sink(execFile))) {
-           out.writeAll(in);
+       return libraryDir;
+   }

-           //noinspection ResultOfMethodCallIgnored
-           execFile.setExecutable(true);
+   public static void checkStunnel(Context context) throws RuntimeException{
+       File execFile = new File(getExecDir(context) + "/" + EXECUTABLE);

-           Log.d(TAG, "Extracted stunnel binary successfully");
-       } catch (Exception e) {
-           Log.e(TAG, "Failed stunnel extraction: ", e);
+       if (execFile.exists() && !hasBeenUpdated(context)) {
+           return;
+       } else {
+           throw new RuntimeException("Stunnel app doesn't exists. Move it to app/src/main/jniLibs/${arch} as stunnel.so");
        }
    }

@@ -90,13 +91,14 @@ public class StunnelProcessManager {
        if (stunnelProcess != null || pidFile.exists()) {
            stop(context);
        }
-       checkAndExtract(context);
+
+       checkStunnel(context);
        setupConfig(context);
        context.clearLog();
        try {
            String[] env = new String[0];
            File workingDirectory = new File(context.getFilesDir().getPath());
-           stunnelProcess = Runtime.getRuntime().exec(context.getFilesDir().getPath() + "/" + EXECUTABLE + " " + CONFIG, env, workingDirectory);
+           stunnelProcess = Runtime.getRuntime().exec(getExecDir(context) + "/" + EXECUTABLE + " " + CONFIG, env, workingDirectory);
            readInputStream(context, Okio.buffer(Okio.source(stunnelProcess.getErrorStream())));
            readInputStream(context, Okio.buffer(Okio.source(stunnelProcess.getInputStream())));
            stunnelProcess.waitFor();
@@ -162,12 +164,12 @@ public class StunnelProcessManager {
        if (stunnelProcess != null || pidFile.exists()) {
            stop(context);
        }
-       checkAndExtract(context);
+       checkStunnel(context);
        try {
            String[] env = new String[0];
            File workingDirectory = new File(context.getFilesDir().getPath());
            // Make the process fail, so we can extract just the version from the error stream
-           stunnelProcess = Runtime.getRuntime().exec(context.getFilesDir().getPath() + "/" + EXECUTABLE + " THISFILESHOULDNOTEXIST", env, workingDirectory);
+           stunnelProcess = Runtime.getRuntime().exec(getExecDir(context) + "/" + EXECUTABLE + " THISFILESHOULDNOTEXIST", env, workingDirectory);
            BufferedSource errors = Okio.buffer(Okio.source(stunnelProcess.getErrorStream()));

            Pattern versionPattern = Pattern.compile("stunnel ([\\d.]+)");