diff options
author | pacien | 2020-01-20 17:07:12 +0100 |
---|---|---|
committer | pacien | 2020-01-20 17:07:12 +0100 |
commit | 883b5abc7b2a770146683e7e27bf275bd4064511 (patch) | |
tree | 81dd200fc2cea8e2030b5b5b68c39abe3c32ab46 /app/src/main/java/org | |
parent | 3fc8a2ed3bfbcbd29bc22c2c73416e2708cd7615 (diff) | |
download | tincapp-883b5abc7b2a770146683e7e27bf275bd4064511.tar.gz |
pass network device fd via unix socket instead of inheritance
Workaround for new shared memory restrictions added in Android 10
preventing file descriptor leakage to sub-processes.
This change set BREAKS ENCRYPTED PRIVATE KEYS SUPPORT.
GitHub: https://github.com/pacien/tincapp/issues/92
Diffstat (limited to 'app/src/main/java/org')
4 files changed, 46 insertions, 47 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt index 29e011f..0a8a774 100644 --- a/app/src/main/java/org/pacien/tincapp/commands/Executor.kt +++ b/app/src/main/java/org/pacien/tincapp/commands/Executor.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2018 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2020 Pacien TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -30,42 +30,10 @@ import java.io.InputStreamReader | |||
30 | * @author pacien | 30 | * @author pacien |
31 | */ | 31 | */ |
32 | internal object Executor { | 32 | internal object Executor { |
33 | private const val FAILED = -1 | ||
34 | private const val SUCCESS = 0 | ||
35 | |||
36 | class CommandExecutionException(msg: String) : Exception(msg) | 33 | class CommandExecutionException(msg: String) : Exception(msg) |
37 | 34 | ||
38 | init { | ||
39 | System.loadLibrary("exec") | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * @return FAILED (-1) on error, forked child PID otherwise | ||
44 | */ | ||
45 | private external fun forkExec(args: Array<String>): Int | ||
46 | |||
47 | /** | ||
48 | * @return FAILED (-1) on error, the exit status of the process otherwise | ||
49 | */ | ||
50 | private external fun wait(pid: Int): Int | ||
51 | |||
52 | private fun read(stream: InputStream) = BufferedReader(InputStreamReader(stream)).readLines() | 35 | private fun read(stream: InputStream) = BufferedReader(InputStreamReader(stream)).readLines() |
53 | 36 | ||
54 | fun forkExec(cmd: Command): CompletableFuture<Unit> { | ||
55 | val pid = forkExec(cmd.asArray()).also { | ||
56 | if (it == FAILED) throw CommandExecutionException("Could not fork child process.") | ||
57 | } | ||
58 | |||
59 | return runAsyncTask { | ||
60 | val exitCode = wait(pid) | ||
61 | when (exitCode) { | ||
62 | SUCCESS -> Unit | ||
63 | FAILED -> throw CommandExecutionException("Process terminated abnormally.") | ||
64 | else -> throw CommandExecutionException("Non-zero exit status code ($exitCode).") | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | fun run(cmd: Command): Process = try { | 37 | fun run(cmd: Command): Process = try { |
70 | ProcessBuilder(cmd.asList()).start() | 38 | ProcessBuilder(cmd.asList()).start() |
71 | } catch (e: IOException) { | 39 | } catch (e: IOException) { |
diff --git a/app/src/main/java/org/pacien/tincapp/commands/Tincd.kt b/app/src/main/java/org/pacien/tincapp/commands/Tincd.kt index 92be0f5..c0b0048 100644 --- a/app/src/main/java/org/pacien/tincapp/commands/Tincd.kt +++ b/app/src/main/java/org/pacien/tincapp/commands/Tincd.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2018 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2020 Pacien TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -18,20 +18,23 @@ | |||
18 | 18 | ||
19 | package org.pacien.tincapp.commands | 19 | package org.pacien.tincapp.commands |
20 | 20 | ||
21 | import java8.util.concurrent.CompletableFuture | ||
21 | import org.pacien.tincapp.context.AppPaths | 22 | import org.pacien.tincapp.context.AppPaths |
23 | import java.io.File | ||
22 | 24 | ||
23 | /** | 25 | /** |
24 | * @author pacien | 26 | * @author pacien |
25 | */ | 27 | */ |
26 | object Tincd { | 28 | object Tincd { |
27 | fun start(netName: String, deviceFd: Int, ed25519PrivateKeyFd: Int? = null, rsaPrivateKeyFd: Int? = null) = | 29 | fun start(netName: String, device: String, ed25519PrivateKey: File? = null, rsaPrivateKey: File? = null): CompletableFuture<Unit> = |
28 | Executor.forkExec(Command(AppPaths.tincd().absolutePath) | 30 | Executor.call(Command(AppPaths.tincd().absolutePath) |
29 | .withOption("no-detach") | 31 | .withOption("no-detach") |
30 | .withOption("config", AppPaths.confDir(netName).absolutePath) | 32 | .withOption("config", AppPaths.confDir(netName).absolutePath) |
31 | .withOption("pidfile", AppPaths.pidFile(netName).absolutePath) | 33 | .withOption("pidfile", AppPaths.pidFile(netName).absolutePath) |
32 | .withOption("logfile", AppPaths.logFile(netName).absolutePath) | 34 | .withOption("logfile", AppPaths.logFile(netName).absolutePath) |
33 | .withOption("option", "DeviceType=fd") | 35 | .withOption("option", "DeviceType=fd") |
34 | .withOption("option", "Device=$deviceFd") | 36 | .withOption("option", "Device=@$device") |
35 | .apply { if (ed25519PrivateKeyFd != null) withOption("option", "Ed25519PrivateKeyFile=/proc/self/fd/$ed25519PrivateKeyFd") } | 37 | .apply { if (ed25519PrivateKey != null) withOption("option", "Ed25519PrivateKeyFile=${ed25519PrivateKey.absolutePath}") } |
36 | .apply { if (rsaPrivateKeyFd != null) withOption("option", "PrivateKeyFile=/proc/self/fd/$rsaPrivateKeyFd") }) | 38 | .apply { if (rsaPrivateKey != null) withOption("option", "PrivateKeyFile=${rsaPrivateKey.absolutePath}") } |
39 | ).thenApply { } | ||
37 | } | 40 | } |
diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt index a877929..4d8d5d0 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2019 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2020 Pacien TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -21,6 +21,7 @@ package org.pacien.tincapp.context | |||
21 | import android.app.Application | 21 | import android.app.Application |
22 | import android.content.Context | 22 | import android.content.Context |
23 | import android.content.Intent | 23 | import android.content.Intent |
24 | import android.content.pm.ApplicationInfo | ||
24 | import android.net.Uri | 25 | import android.net.Uri |
25 | import android.os.Build | 26 | import android.os.Build |
26 | import android.os.Handler | 27 | import android.os.Handler |
@@ -47,7 +48,7 @@ class App : Application() { | |||
47 | 48 | ||
48 | private fun setupCrashHandler() { | 49 | private fun setupCrashHandler() { |
49 | val logger = LoggerFactory.getLogger(this.javaClass) | 50 | val logger = LoggerFactory.getLogger(this.javaClass) |
50 | val systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler() | 51 | val systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler()!! |
51 | val crashRecorder = CrashRecorder(logger, systemCrashHandler) | 52 | val crashRecorder = CrashRecorder(logger, systemCrashHandler) |
52 | Thread.setDefaultUncaughtExceptionHandler(crashRecorder) | 53 | Thread.setDefaultUncaughtExceptionHandler(crashRecorder) |
53 | } | 54 | } |
@@ -61,6 +62,11 @@ class App : Application() { | |||
61 | fun getContext() = appContext!! | 62 | fun getContext() = appContext!! |
62 | fun getResources() = getContext().resources!! | 63 | fun getResources() = getContext().resources!! |
63 | 64 | ||
65 | fun getApplicationInfo(): ApplicationInfo = | ||
66 | getContext() | ||
67 | .packageManager | ||
68 | .getApplicationInfo(BuildConfig.APPLICATION_ID, 0) | ||
69 | |||
64 | fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = | 70 | fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = |
65 | notificationManager.notifyError(appContext!!.getString(title), msg, manualLink) | 71 | notificationManager.notifyError(appContext!!.getString(title), msg, manualLink) |
66 | 72 | ||
diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 40e9004..48cb1df 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2019 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2020 Pacien TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -21,6 +21,7 @@ package org.pacien.tincapp.service | |||
21 | import android.app.Service | 21 | import android.app.Service |
22 | import android.content.Context | 22 | import android.content.Context |
23 | import android.content.Intent | 23 | import android.content.Intent |
24 | import android.net.LocalServerSocket | ||
24 | import android.net.VpnService | 25 | import android.net.VpnService |
25 | import android.os.ParcelFileDescriptor | 26 | import android.os.ParcelFileDescriptor |
26 | import androidx.localbroadcastmanager.content.LocalBroadcastManager | 27 | import androidx.localbroadcastmanager.content.LocalBroadcastManager |
@@ -43,6 +44,7 @@ import org.pacien.tincapp.intent.Actions | |||
43 | import org.pacien.tincapp.utils.TincKeyring | 44 | import org.pacien.tincapp.utils.TincKeyring |
44 | import org.slf4j.LoggerFactory | 45 | import org.slf4j.LoggerFactory |
45 | import java.io.FileNotFoundException | 46 | import java.io.FileNotFoundException |
47 | import java.security.AccessControlException | ||
46 | 48 | ||
47 | /** | 49 | /** |
48 | * @author pacien | 50 | * @author pacien |
@@ -100,6 +102,7 @@ class TincVpnService : VpnService() { | |||
100 | log.info("Starting tinc daemon for network \"$netName\".") | 102 | log.info("Starting tinc daemon for network \"$netName\".") |
101 | if (isConnected() || getCurrentNetName() != null) stopVpn().join() | 103 | if (isConnected() || getCurrentNetName() != null) stopVpn().join() |
102 | 104 | ||
105 | // FIXME: pass decrypted private keys via temp file | ||
103 | val privateKeys = try { | 106 | val privateKeys = try { |
104 | TincConfiguration.fromTincConfiguration(AppPaths.existing(AppPaths.tincConfFile(netName))).let { tincCfg -> | 107 | TincConfiguration.fromTincConfiguration(AppPaths.existing(AppPaths.tincConfFile(netName))).let { tincCfg -> |
105 | Pair( | 108 | Pair( |