summaryrefslogtreecommitdiff
path: root/libs/sftp/src/main/java/org
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-10-31 23:14:01 +0100
committerJoel Klinghed <the_jk@spawned.biz>2024-10-31 23:36:29 +0100
commit6d185a2e7fca1e15008ef4906c57be5f42c3c6b3 (patch)
tree3288c6e0767748f53072312c28d4468ff7ea53f8 /libs/sftp/src/main/java/org
parent77f2ab719c50b27b4aeca4d7cbd4b1398337ed78 (diff)
sftp: Verify server fingerprint
If no fingerprint is stored -> save whatever the server gives If a fingerprint is stored -> error if fingerprint does not match
Diffstat (limited to 'libs/sftp/src/main/java/org')
-rw-r--r--libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpConnection.kt29
-rw-r--r--libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpHostsStorage.kt35
-rw-r--r--libs/sftp/src/main/java/org/the_jk/cleversync/sftp/SftpTreeFactory.kt12
3 files changed, 65 insertions, 11 deletions
diff --git a/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpConnection.kt b/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpConnection.kt
index 43eb88a..ed5889a 100644
--- a/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpConnection.kt
+++ b/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpConnection.kt
@@ -3,19 +3,24 @@ package org.the_jk.cleversync.io.sftp
import android.net.Uri
import org.the_jk.cleversync.PathUtils
-internal class SftpConnection(uri: Uri, credentials: SftpCredentials) {
+internal class SftpConnection(uri: Uri, credentials: SftpCredentials, hostsStorage: SftpHostsStorage) {
val description = uri.toString()
private val baseDir = uri.path ?: ""
private val sshSession = NativeSftp.newSshSession()
private var sftpSession: NativeSftp.SftpSession? = null
private var destroyed = false
+ private var fingerprintMismatch = false
- val connected = if (!destroyed) { login(uri, credentials) } else false
+ val connected = if (!destroyed) { login(uri, credentials, hostsStorage) } else false
val error: String
get() = if (destroyed) "[destroyed]" else {
- val err = sftpSession?.lastError()
- if (err.isNullOrEmpty()) sshSession.lastError() else err
+ if (fingerprintMismatch) {
+ "[fingerprint mismatch]"
+ } else {
+ val err = sftpSession?.lastError()
+ if (err.isNullOrEmpty()) sshSession.lastError() else err
+ }
}
protected fun finalize() {
@@ -66,12 +71,20 @@ internal class SftpConnection(uri: Uri, credentials: SftpCredentials) {
override fun toString() = description
- private fun login(uri: Uri, credentials: SftpCredentials): Boolean {
- if (!sshSession.connect(uri.host ?: "", if (uri.port == -1) DEFAULT_PORT else uri.port)) {
+ private fun login(uri: Uri, credentials: SftpCredentials, hostsStorage: SftpHostsStorage): Boolean {
+ val host = uri.host ?: ""
+ val port = if (uri.port == -1) DEFAULT_PORT else uri.port
+ if (!sshSession.connect(host, port)) {
+ return false
+ }
+ val expectedFingerprint = hostsStorage.get(host, port)
+ val fingerprint = sshSession.handshake() ?: return false
+ if (expectedFingerprint == null) {
+ hostsStorage.put(host, port, fingerprint)
+ } else if (expectedFingerprint != fingerprint) {
+ fingerprintMismatch = true
return false
}
- // TODO: Check fingerprint against last one
- if (sshSession.handshake() == null) return false
when (credentials) {
is SftpCredentials.SftpPasswordCredentials ->
if (!sshSession.authenticate(
diff --git a/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpHostsStorage.kt b/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpHostsStorage.kt
new file mode 100644
index 0000000..2fde819
--- /dev/null
+++ b/libs/sftp/src/main/java/org/the_jk/cleversync/io/sftp/SftpHostsStorage.kt
@@ -0,0 +1,35 @@
+package org.the_jk.cleversync.io.sftp
+
+import android.content.Context
+import android.util.Base64
+
+class SftpHostsStorage(context: Context) {
+ private val prefs by lazy {
+ context.getSharedPreferences("sftp_hosts", Context.MODE_PRIVATE)
+ }
+
+ internal fun size(): Int {
+ return prefs.all.size
+ }
+
+ internal fun get(host: String, port: Int): NativeSftp.Fingerprint? {
+ val key = createKey(host, port)
+ val data = prefs.getString(key, null) ?: return null
+ return try {
+ NativeSftp.Fingerprint(Base64.decode(data, Base64.DEFAULT))
+ } catch (_: IllegalArgumentException) {
+ null
+ }
+ }
+
+ internal fun put(host: String, port: Int, fingerprint: NativeSftp.Fingerprint) {
+ val key = createKey(host, port)
+ val editor = prefs.edit()
+ editor.putString(key, Base64.encodeToString(fingerprint.data, Base64.NO_WRAP))
+ editor.apply()
+ }
+
+ private companion object {
+ fun createKey(host: String, port: Int) = "$host:$port"
+ }
+}
diff --git a/libs/sftp/src/main/java/org/the_jk/cleversync/sftp/SftpTreeFactory.kt b/libs/sftp/src/main/java/org/the_jk/cleversync/sftp/SftpTreeFactory.kt
index 7a45829..08894b9 100644
--- a/libs/sftp/src/main/java/org/the_jk/cleversync/sftp/SftpTreeFactory.kt
+++ b/libs/sftp/src/main/java/org/the_jk/cleversync/sftp/SftpTreeFactory.kt
@@ -5,15 +5,21 @@ import org.the_jk.cleversync.io.ModifiableTree
import org.the_jk.cleversync.io.Tree
import org.the_jk.cleversync.io.sftp.SftpConnection
import org.the_jk.cleversync.io.sftp.SftpCredentials
+import org.the_jk.cleversync.io.sftp.SftpHostsStorage
import org.the_jk.cleversync.io.sftp.SftpTree
object SftpTreeFactory {
- fun tree(uri: String, credentials: SftpCredentials): Result<Tree> = modifiableTree(uri, credentials)
+ fun tree(uri: String, credentials: SftpCredentials, hostsStorage: SftpHostsStorage): Result<Tree>
+ = modifiableTree(uri, credentials, hostsStorage)
- fun modifiableTree(uri: String, credentials: SftpCredentials): Result<ModifiableTree> {
+ fun modifiableTree(
+ uri: String,
+ credentials: SftpCredentials,
+ hostsStorage: SftpHostsStorage,
+ ): Result<ModifiableTree> {
val url = Uri.parse(uri)
if (url.scheme != "ssh") return Result.failure(IllegalArgumentException("Invalid url: $uri"))
- val connection = SftpConnection(url, credentials)
+ val connection = SftpConnection(url, credentials, hostsStorage)
if (!connection.connected) {
val e = Exception(connection.error)
connection.destroy()