aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpacien2020-01-20 19:18:12 +0100
committerpacien2020-01-20 19:18:12 +0100
commitb04d9581adb3e3176586f31ffdba123125546201 (patch)
tree468776749a6596b299d7075ab998117a06c58813
parent883b5abc7b2a770146683e7e27bf275bd4064511 (diff)
downloadtincapp-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
-rw-r--r--app/src/main/java/org/pacien/tincapp/context/AppPaths.kt11
-rw-r--r--app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt16
-rw-r--r--app/src/main/java/org/pacien/tincapp/utils/TincKeyring.kt32
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 */
30object AppPaths { 30object 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
19package org.pacien.tincapp.utils 19package org.pacien.tincapp.utils
20 20
21import android.os.ParcelFileDescriptor
22import org.pacien.tincapp.commands.TincApp 21import org.pacien.tincapp.commands.TincApp
22import org.pacien.tincapp.context.AppPaths
23import java.io.File 23import java.io.File
24import java.io.FileNotFoundException 24import java.io.FileNotFoundException
25import 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}