From 840c7ea560020111dd3e89aa3d976229104974be Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 9 Mar 2020 01:36:01 +0800 Subject: RootShell: properly use errormessages Signed-off-by: Jason A. Donenfeld --- .../com/wireguard/android/util/ErrorMessages.java | 15 +++++- .../com/wireguard/android/util/ModuleLoader.java | 6 +-- .../java/com/wireguard/android/util/RootShell.java | 61 +++++++++++++--------- .../com/wireguard/android/util/ToolsInstaller.java | 22 ++++++-- 4 files changed, 71 insertions(+), 33 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/com/wireguard/android/util/ErrorMessages.java b/app/src/main/java/com/wireguard/android/util/ErrorMessages.java index 59d513f4..481a6ffb 100644 --- a/app/src/main/java/com/wireguard/android/util/ErrorMessages.java +++ b/app/src/main/java/com/wireguard/android/util/ErrorMessages.java @@ -13,6 +13,7 @@ import androidx.annotation.Nullable; import com.wireguard.android.Application; import com.wireguard.android.R; import com.wireguard.android.backend.BackendException; +import com.wireguard.android.util.RootShell.RootShellException; import com.wireguard.config.BadConfigException; import com.wireguard.config.BadConfigException.Location; import com.wireguard.config.InetEndpoint; @@ -49,6 +50,14 @@ public final class ErrorMessages { BackendException.Reason.TUN_CREATION_ERROR, R.string.tun_create_error, BackendException.Reason.GO_ACTIVATION_ERROR_CODE, R.string.tunnel_on_error )); + private static final Map RSE_REASON_MAP = new EnumMap<>(Maps.of( + RootShellException.Reason.NO_ROOT_ACCESS, R.string.error_root, + RootShellException.Reason.SHELL_MARKER_COUNT_ERROR, R.string.shell_marker_count_error, + RootShellException.Reason.SHELL_EXIT_STATUS_READ_ERROR, R.string.shell_exit_status_read_error, + RootShellException.Reason.SHELL_START_ERROR, R.string.shell_start_error, + RootShellException.Reason.CREATE_BIN_DIR_ERROR, R.string.create_bin_dir_error, + RootShellException.Reason.CREATE_TEMP_DIR_ERROR, R.string.create_temp_dir_error + )); private static final Map KFE_FORMAT_MAP = new EnumMap<>(Maps.of( Format.BASE64, R.string.key_length_explanation_base64, Format.BINARY, R.string.key_length_explanation_binary, @@ -89,6 +98,9 @@ public final class ErrorMessages { } else if (rootCause instanceof BackendException) { final BackendException be = (BackendException) rootCause; message = resources.getString(BE_REASON_MAP.get(be.getReason()), be.getFormat()); + } else if (rootCause instanceof RootShellException) { + final RootShellException rse = (RootShellException) rootCause; + message = resources.getString(RSE_REASON_MAP.get(rse.getReason()), rse.getFormat()); } else if (rootCause.getMessage() != null) { message = rootCause.getMessage(); } else { @@ -135,7 +147,8 @@ public final class ErrorMessages { private static Throwable rootCause(final Throwable throwable) { Throwable cause = throwable; while (cause.getCause() != null) { - if (cause instanceof BadConfigException || cause instanceof BackendException) + if (cause instanceof BadConfigException || cause instanceof BackendException || + cause instanceof RootShellException) break; final Throwable nextCause = cause.getCause(); if (nextCause instanceof RemoteException) diff --git a/app/src/main/java/com/wireguard/android/util/ModuleLoader.java b/app/src/main/java/com/wireguard/android/util/ModuleLoader.java index 7794dd5c..7c943d26 100644 --- a/app/src/main/java/com/wireguard/android/util/ModuleLoader.java +++ b/app/src/main/java/com/wireguard/android/util/ModuleLoader.java @@ -10,7 +10,7 @@ import android.system.OsConstants; import android.util.Base64; import com.wireguard.android.Application; -import com.wireguard.android.util.RootShell.NoRootException; +import com.wireguard.android.util.RootShell.RootShellException; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAPublicKey; @@ -55,7 +55,7 @@ public class ModuleLoader { return moduleDir.exists() && moduleDir.isDirectory(); } - public void loadModule() throws IOException, NoRootException { + public void loadModule() throws IOException, RootShellException { Application.getRootShell().run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath())); } @@ -122,7 +122,7 @@ public class ModuleLoader { return hashes; } - public Integer download() throws IOException, NoRootException, NoSuchAlgorithmException { + public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException { final List output = new ArrayList<>(); Application.getRootShell().run(output, "sha256sum /proc/version|cut -d ' ' -f 1"); if (output.size() != 1 || output.get(0).length() != 64) diff --git a/app/src/main/java/com/wireguard/android/util/RootShell.java b/app/src/main/java/com/wireguard/android/util/RootShell.java index 1fe5667a..63d2c906 100644 --- a/app/src/main/java/com/wireguard/android/util/RootShell.java +++ b/app/src/main/java/com/wireguard/android/util/RootShell.java @@ -10,11 +10,10 @@ import androidx.annotation.Nullable; import android.util.Log; import com.wireguard.android.BuildConfig; -import com.wireguard.android.R; +import com.wireguard.android.util.RootShell.RootShellException.Reason; import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -30,8 +29,6 @@ public class RootShell { private static final String SU = "su"; private static final String TAG = "WireGuard/" + RootShell.class.getSimpleName(); - private final Context context; - private final String deviceNotRootedMessage; private final File localBinaryDir; private final File localTemporaryDir; private final Object lock = new Object(); @@ -42,12 +39,10 @@ public class RootShell { @Nullable private BufferedReader stdout; public RootShell(final Context context) { - deviceNotRootedMessage = context.getString(R.string.error_root); localBinaryDir = new File(context.getCodeCacheDir(), "bin"); localTemporaryDir = new File(context.getCacheDir(), "tmp"); preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n", BuildConfig.APPLICATION_ID, localBinaryDir, localTemporaryDir); - this.context = context; } private static boolean isExecutableInPath(final String name) { @@ -83,7 +78,7 @@ public class RootShell { * @return The exit value of the command. */ public int run(@Nullable final Collection output, final String command) - throws IOException, NoRootException { + throws IOException, RootShellException { synchronized (lock) { /* Start inside synchronized block to prevent a concurrent call to stop(). */ start(); @@ -122,24 +117,24 @@ public class RootShell { } } if (markersSeen != 4) - throw new IOException(context.getString(R.string.shell_marker_count_error, markersSeen)); + throw new RootShellException(Reason.SHELL_MARKER_COUNT_ERROR, markersSeen); if (errnoStdout != errnoStderr) - throw new IOException(context.getString(R.string.shell_exit_status_read_error)); + throw new RootShellException(Reason.SHELL_EXIT_STATUS_READ_ERROR); Log.v(TAG, "exit: " + errnoStdout); return errnoStdout; } } - public void start() throws IOException, NoRootException { + public void start() throws IOException, RootShellException { if (!isExecutableInPath(SU)) - throw new NoRootException(deviceNotRootedMessage); + throw new RootShellException(Reason.NO_ROOT_ACCESS); synchronized (lock) { if (isRunning()) return; if (!localBinaryDir.isDirectory() && !localBinaryDir.mkdirs()) - throw new FileNotFoundException(context.getString(R.string.create_bin_dir_error)); + throw new RootShellException(Reason.CREATE_BIN_DIR_ERROR); if (!localTemporaryDir.isDirectory() && !localTemporaryDir.mkdirs()) - throw new FileNotFoundException(context.getString(R.string.create_temp_dir_error)); + throw new RootShellException(Reason.CREATE_TEMP_DIR_ERROR); try { final ProcessBuilder builder = new ProcessBuilder().command(SU); builder.environment().put("LC_ALL", "C"); @@ -147,7 +142,9 @@ public class RootShell { process = builder.start(); } catch (final IOException e) { // A failure at this stage means the device isn't rooted. - throw new NoRootException(deviceNotRootedMessage, e); + final RootShellException rse = new RootShellException(Reason.NO_ROOT_ACCESS); + rse.initCause(e); + throw rse; } stdin = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8); stdout = new BufferedReader(new InputStreamReader(process.getInputStream(), @@ -160,18 +157,18 @@ public class RootShell { final String uid = stdout.readLine(); if (!"0".equals(uid)) { Log.w(TAG, "Root check did not return correct UID: " + uid); - throw new NoRootException(deviceNotRootedMessage); + throw new RootShellException(Reason.NO_ROOT_ACCESS); } if (!isRunning()) { String line; while ((line = stderr.readLine()) != null) { Log.w(TAG, "Root check returned an error: " + line); if (line.contains("Permission denied")) - throw new NoRootException(deviceNotRootedMessage); + throw new RootShellException(Reason.NO_ROOT_ACCESS); } - throw new IOException(context.getString(R.string.shell_start_error, process.exitValue())); + throw new RootShellException(Reason.SHELL_START_ERROR, process.exitValue()); } - } catch (final IOException | NoRootException e) { + } catch (final IOException | RootShellException e) { stop(); throw e; } @@ -187,13 +184,29 @@ public class RootShell { } } - public static class NoRootException extends Exception { - public NoRootException(final String message, final Throwable cause) { - super(message, cause); + public static class RootShellException extends Exception { + public enum Reason { + NO_ROOT_ACCESS, + SHELL_MARKER_COUNT_ERROR, + SHELL_EXIT_STATUS_READ_ERROR, + SHELL_START_ERROR, + CREATE_BIN_DIR_ERROR, + CREATE_TEMP_DIR_ERROR } - - public NoRootException(final String message) { - super(message); + private final Reason reason; + private final Object[] format; + public RootShellException(final Reason reason, final Object ...format) { + this.reason = reason; + this.format = format; + } + public boolean isIORelated() { + return reason != Reason.NO_ROOT_ACCESS; + } + public Reason getReason() { + return reason; + } + public Object[] getFormat() { + return format; } } } diff --git a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java index 82d1e94e..5483db35 100644 --- a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java +++ b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java @@ -13,7 +13,7 @@ import android.util.Log; import com.wireguard.android.Application; import com.wireguard.android.BuildConfig; import com.wireguard.android.R; -import com.wireguard.android.util.RootShell.NoRootException; +import com.wireguard.android.util.RootShell.RootShellException; import java.io.File; import java.io.FileNotFoundException; @@ -63,7 +63,7 @@ public final class ToolsInstaller { return null; } - public int areInstalled() throws NoRootException { + public int areInstalled() throws RootShellException { if (INSTALL_DIR == null) return ERROR; final StringBuilder script = new StringBuilder(); @@ -81,6 +81,10 @@ public final class ToolsInstaller { return willInstallAsMagiskModule() ? NO | MAGISK : NO | SYSTEM; } catch (final IOException ignored) { return ERROR; + } catch (final RootShellException e) { + if (e.isIORelated()) + return ERROR; + throw e; } } @@ -102,11 +106,11 @@ public final class ToolsInstaller { } } - public int install() throws NoRootException, IOException { + public int install() throws RootShellException, IOException { return willInstallAsMagiskModule() ? installMagisk() : installSystem(); } - private int installMagisk() throws NoRootException, IOException { + private int installMagisk() throws RootShellException, IOException { extract(); final StringBuilder script = new StringBuilder("set -ex; "); @@ -125,10 +129,14 @@ public final class ToolsInstaller { return Application.getRootShell().run(null, script.toString()) == 0 ? YES | MAGISK : ERROR; } catch (final IOException ignored) { return ERROR; + } catch (final RootShellException e) { + if (e.isIORelated()) + return ERROR; + throw e; } } - private int installSystem() throws NoRootException, IOException { + private int installSystem() throws RootShellException, IOException { if (INSTALL_DIR == null) return OsConstants.ENOENT; extract(); @@ -143,6 +151,10 @@ public final class ToolsInstaller { return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR; } catch (final IOException ignored) { return ERROR; + } catch (final RootShellException e) { + if (e.isIORelated()) + return ERROR; + throw e; } } -- cgit v1.2.3-59-g8ed1b