diff options
author | pacien | 2020-01-20 19:18:12 +0100 |
---|---|---|
committer | pacien | 2020-01-20 19:18:12 +0100 |
commit | b04d9581adb3e3176586f31ffdba123125546201 (patch) | |
tree | 468776749a6596b299d7075ab998117a06c58813 | |
parent | 883b5abc7b2a770146683e7e27bf275bd4064511 (diff) | |
download | tincapp-b04d9581adb3e3176586f31ffdba123125546201.tar.gz |
use private temp files to pass decrypted private keys
Android 10 (API 29) doesn't allow us to pass them by sharing file
descriptors anymore, making the use of temp files mandatory.
GitHub: https://github.com/pacien/tincapp/issues/92
3 files changed, 38 insertions, 21 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt index 1efb7cf..2394586 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.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 |
@@ -25,7 +25,7 @@ import java.io.FileNotFoundException | |||
25 | /** | 25 | /** |
26 | * @author pacien | 26 | * @author pacien |
27 | * | 27 | * |
28 | * @implNote Logs and PID files are stored in the cache directory for easy clean up. | 28 | * @implNote Logs and PID files are stored in the cache directory for automatic collection. |
29 | */ | 29 | */ |
30 | object AppPaths { | 30 | object AppPaths { |
31 | private const val TINCD_BIN = "libtincd.so" | 31 | private const val TINCD_BIN = "libtincd.so" |
@@ -40,15 +40,16 @@ object AppPaths { | |||
40 | private const val NET_TINC_CONF_FILE = "tinc.conf" | 40 | private const val NET_TINC_CONF_FILE = "tinc.conf" |
41 | private const val NET_HOSTS_DIR = "hosts" | 41 | private const val NET_HOSTS_DIR = "hosts" |
42 | private const val NET_INVITATION_FILE = "invitation-data" | 42 | private const val NET_INVITATION_FILE = "invitation-data" |
43 | private const val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" | 43 | |
44 | private const val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" | 44 | const val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" |
45 | const val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" | ||
45 | 46 | ||
46 | private val context by lazy { App.getContext() } | 47 | private val context by lazy { App.getContext() } |
47 | 48 | ||
48 | fun storageAvailable() = | 49 | fun storageAvailable() = |
49 | Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } | 50 | Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } |
50 | 51 | ||
51 | private fun internalCacheDir() = context.cacheDir!! | 52 | fun internalCacheDir() = context.cacheDir!! |
52 | fun cacheDir() = context.externalCacheDir!! | 53 | fun cacheDir() = context.externalCacheDir!! |
53 | fun confDir() = context.getExternalFilesDir(null)!! | 54 | fun confDir() = context.getExternalFilesDir(null)!! |
54 | private fun binDir() = File(context.applicationInfo.nativeLibraryDir) | 55 | private fun binDir() = File(context.applicationInfo.nativeLibraryDir) |
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 48cb1df..c688742 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | |||
@@ -102,12 +102,17 @@ class TincVpnService : VpnService() { | |||
102 | log.info("Starting tinc daemon for network \"$netName\".") | 102 | log.info("Starting tinc daemon for network \"$netName\".") |
103 | if (isConnected() || getCurrentNetName() != null) stopVpn().join() | 103 | if (isConnected() || getCurrentNetName() != null) stopVpn().join() |
104 | 104 | ||
105 | // FIXME: pass decrypted private keys via temp file | ||
106 | val privateKeys = try { | 105 | val privateKeys = try { |
107 | TincConfiguration.fromTincConfiguration(AppPaths.existing(AppPaths.tincConfFile(netName))).let { tincCfg -> | 106 | TincConfiguration.fromTincConfiguration(AppPaths.existing(AppPaths.tincConfFile(netName))).let { tincCfg -> |
108 | Pair( | 107 | Pair( |
109 | TincKeyring.openPrivateKey(tincCfg.ed25519PrivateKeyFile ?: AppPaths.defaultEd25519PrivateKeyFile(netName), passphrase), | 108 | TincKeyring.unlockKey( |
110 | TincKeyring.openPrivateKey(tincCfg.privateKeyFile ?: AppPaths.defaultRsaPrivateKeyFile(netName), passphrase)) | 109 | AppPaths.NET_DEFAULT_ED25519_PRIVATE_KEY_FILE, |
110 | tincCfg.ed25519PrivateKeyFile ?: AppPaths.defaultEd25519PrivateKeyFile(netName), | ||
111 | passphrase), | ||
112 | TincKeyring.unlockKey( | ||
113 | AppPaths.NET_DEFAULT_RSA_PRIVATE_KEY_FILE, | ||
114 | tincCfg.privateKeyFile ?: AppPaths.defaultRsaPrivateKeyFile(netName), | ||
115 | passphrase)) | ||
111 | } | 116 | } |
112 | } catch (e: FileNotFoundException) { | 117 | } catch (e: FileNotFoundException) { |
113 | Pair(null, null) | 118 | Pair(null, null) |
@@ -143,15 +148,12 @@ class TincVpnService : VpnService() { | |||
143 | val serverSocket = LocalServerSocket(DEVICE_FD_ABSTRACT_SOCKET) | 148 | val serverSocket = LocalServerSocket(DEVICE_FD_ABSTRACT_SOCKET) |
144 | Executor.runAsyncTask { serveDeviceFd(serverSocket, deviceFd) } | 149 | Executor.runAsyncTask { serveDeviceFd(serverSocket, deviceFd) } |
145 | 150 | ||
146 | // FIXME: pass decrypted private keys via temp file | 151 | val daemon = Tincd.start(netName, DEVICE_FD_ABSTRACT_SOCKET, privateKeys.first, privateKeys.second) |
147 | val daemon = Tincd.start(netName, DEVICE_FD_ABSTRACT_SOCKET, null, null) | ||
148 | setState(netName, passphrase, interfaceCfg, deviceFd, daemon) | 152 | setState(netName, passphrase, interfaceCfg, deviceFd, daemon) |
149 | 153 | ||
150 | waitForDaemonStartup().whenComplete { _, exception -> | 154 | waitForDaemonStartup().whenComplete { _, exception -> |
151 | serverSocket.close() | 155 | serverSocket.close() |
152 | deviceFd.close() | 156 | deviceFd.close() |
153 | privateKeys.first?.close() | ||
154 | privateKeys.second?.close() | ||
155 | 157 | ||
156 | if (exception != null) { | 158 | if (exception != null) { |
157 | reportError(resources.getString(R.string.notification_error_message_daemon_exited, exception.cause!!.defaultMessage()), exception) | 159 | reportError(resources.getString(R.string.notification_error_message_daemon_exited, exception.cause!!.defaultMessage()), exception) |
diff --git a/app/src/main/java/org/pacien/tincapp/utils/TincKeyring.kt b/app/src/main/java/org/pacien/tincapp/utils/TincKeyring.kt index bae38ac..89bb246 100644 --- a/app/src/main/java/org/pacien/tincapp/utils/TincKeyring.kt +++ b/app/src/main/java/org/pacien/tincapp/utils/TincKeyring.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,10 +18,11 @@ | |||
18 | 18 | ||
19 | package org.pacien.tincapp.utils | 19 | package org.pacien.tincapp.utils |
20 | 20 | ||
21 | import android.os.ParcelFileDescriptor | ||
22 | import org.pacien.tincapp.commands.TincApp | 21 | import org.pacien.tincapp.commands.TincApp |
22 | import org.pacien.tincapp.context.AppPaths | ||
23 | import java.io.File | 23 | import java.io.File |
24 | import java.io.FileNotFoundException | 24 | import java.io.FileNotFoundException |
25 | import java.io.FileWriter | ||
25 | 26 | ||
26 | /** | 27 | /** |
27 | * @author pacien | 28 | * @author pacien |
@@ -33,12 +34,25 @@ object TincKeyring { | |||
33 | false | 34 | false |
34 | } | 35 | } |
35 | 36 | ||
36 | fun openPrivateKey(f: File?, passphrase: String?): ParcelFileDescriptor? { | 37 | fun unlockKey(target: String, input: File?, passphrase: String?): File? { |
37 | if (f == null || !f.exists() || passphrase == null) return null | 38 | if (input == null || !input.exists() || passphrase == null) return null |
38 | val pipe = ParcelFileDescriptor.createPipe() | 39 | val decryptedKey = PemUtils.decrypt(PemUtils.read(input), passphrase) |
39 | val decryptedKey = PemUtils.decrypt(PemUtils.read(f), passphrase) | 40 | val decryptedFile = tempKey(target) |
40 | val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]) | 41 | PemUtils.write(decryptedKey, FileWriter(decryptedFile, false)) |
41 | PemUtils.write(decryptedKey, outputStream.writer()) | 42 | return decryptedFile |
42 | return pipe[0] | 43 | } |
44 | |||
45 | private fun tempKey(name: String): File { | ||
46 | val file = File(AppPaths.internalCacheDir(), name) | ||
47 | file.createNewFile() | ||
48 | file.deleteOnExit() | ||
49 | file.makePrivate() | ||
50 | return file | ||
51 | } | ||
52 | |||
53 | private fun File.makePrivate() { | ||
54 | this.setExecutable(false, false) | ||
55 | this.setReadable(true, true) | ||
56 | this.setWritable(true, true) | ||
43 | } | 57 | } |
44 | } | 58 | } |