diff --git a/build.gradle.kts b/build.gradle.kts index 7a1782aa..0af76808 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -154,7 +154,7 @@ abstract class FixPosixFilePermissionClassVisitorFactory : signature: String?, exceptions: Array? ): MethodVisitor { - if (name == "attributesToPermissions") { // org.apache.sshd.common.subsystem.sftp.SftpHelper.attributesToPermissions + if (name == "attributesToPermissions") { // org.apache.sshd.sftp.common.SftpHelper.attributesToPermissions return object : MethodVisitor(instrumentationContext.apiVersion.get(), super.visitMethod(access, name, descriptor, signature, exceptions)) { override fun visitTypeInsn(opcode: Int, type: String?) { // We need to prevent Android Desugar modifying the `PosixFilePermission` classname. @@ -179,7 +179,7 @@ abstract class FixPosixFilePermissionClassVisitorFactory : } override fun isInstrumentable(classData: ClassData): Boolean { - return (classData.className == "org.apache.sshd.common.subsystem.sftp.SftpHelper").also { + return (classData.className == "org.apache.sshd.sftp.common.SftpHelper").also { if (it) println("SftpHelper Found! Instrumenting...") } } @@ -224,6 +224,8 @@ dependencies { implementation(libs.slf4j.handroid) implementation(libs.apache.sshd.core) + implementation(libs.apache.sshd.sftp) + implementation(libs.apache.sshd.scp) implementation(libs.apache.mina.core) //For some reason, makes sshd-core:0.14.0 work without NIO, which isn't available until Android 8 (api 26) //implementation("com.github.bright:slf4android:0.1.6") { transitive = true } // For org.apache.sshd debugging diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b603cc6..20b22066 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ reactiveStreams = "1.0.4" recyclerview = "1.3.2" rxjava = "2.2.21" sl4j = "2.0.4" -sshdCore = "1.0.0" +sshdCore = "2.12.1" swiperefreshlayout = "1.1.0" uiToolingPreview = "1.6.7" univocityParsers = "2.9.1" @@ -74,6 +74,8 @@ logger = { module = "com.klinkerapps:logger", version.ref = "logger" } material = { module = "com.google.android.material:material", version.ref = "material" } apache-mina-core = { module = "org.apache.mina:mina-core", version.ref = "minaCore" } apache-sshd-core = { module = "org.apache.sshd:sshd-core", version.ref = "sshdCore" } +apache-sshd-sftp = { module = "org.apache.sshd:sshd-sftp", version.ref = "sshdCore" } +apache-sshd-scp = { module = "org.apache.sshd:sshd-scp", version.ref = "sshdCore" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } reactive-streams = { module = "org.reactivestreams:reactive-streams", version.ref = "reactiveStreams" } rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" } diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/DHG14_256Factory.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/DHG14_256Factory.kt index 8f121b29..d71930fb 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/DHG14_256Factory.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/DHG14_256Factory.kt @@ -11,7 +11,7 @@ import org.apache.sshd.common.kex.AbstractDH import org.apache.sshd.common.kex.DHFactory import org.apache.sshd.common.kex.DHG import org.apache.sshd.common.kex.DHGroupData -import org.apache.sshd.common.util.SecurityUtils +import org.apache.sshd.common.util.security.SecurityUtils import java.math.BigInteger object DHG14_256Factory : DHFactory { diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SignatureRSASHA256.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SignatureRSASHA256.kt index 82f7237a..3120d13c 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SignatureRSASHA256.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SignatureRSASHA256.kt @@ -6,6 +6,7 @@ */ package org.kde.kdeconnect.Plugins.SftpPlugin +import org.apache.sshd.common.session.SessionContext import org.apache.sshd.common.signature.AbstractSignature import org.apache.sshd.common.signature.Signature import org.apache.sshd.common.signature.SignatureFactory @@ -23,22 +24,18 @@ class SignatureRSASHA256 : AbstractSignature("SHA256withRSA") { } @Throws(Exception::class) - override fun sign(): ByteArray { + override fun sign(session: SessionContext): ByteArray { return signature.sign() } @Throws(Exception::class) - override fun verify(sig: ByteArray): Boolean { + override fun verify(session: SessionContext, sig: ByteArray): Boolean { var data = sig - val encoding = extractEncodedSignature(data) + val encoding = extractEncodedSignature(data) { type -> + type == "rsa-sha2-256" + } if (encoding != null) { - val keyType = encoding.first - ValidateUtils.checkTrue( - "rsa-sha2-256" == keyType, - "Mismatched key type: %s", - keyType - ) - data = encoding.second + data = encoding.value } return signature.verify(data) diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt index 2eebcb2f..9f922962 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt @@ -9,20 +9,22 @@ package org.kde.kdeconnect.Plugins.SftpPlugin import android.content.Context import android.os.Build import android.util.Log -import org.apache.sshd.common.NamedFactory import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory import org.apache.sshd.common.kex.BuiltinDHFactories import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider +import org.apache.sshd.common.session.SessionContext import org.apache.sshd.common.signature.BuiltinSignatures -import org.apache.sshd.common.util.SecurityUtils -import org.apache.sshd.server.Command +import org.apache.sshd.common.util.io.PathUtils +import org.apache.sshd.common.util.security.SecurityUtils.SECURITY_PROVIDER_REGISTRARS +import org.apache.sshd.scp.server.ScpCommandFactory +import org.apache.sshd.server.ServerBuilder import org.apache.sshd.server.SshServer import org.apache.sshd.server.auth.password.PasswordAuthenticator import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator -import org.apache.sshd.server.command.ScpCommandFactory import org.apache.sshd.server.kex.DHGServer import org.apache.sshd.server.session.ServerSession -import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory +import org.apache.sshd.server.subsystem.SubsystemFactory +import org.apache.sshd.sftp.server.SftpSubsystemFactory import org.kde.kdeconnect.Device import org.kde.kdeconnect.Helpers.RandomHelper import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper @@ -30,6 +32,7 @@ import org.kde.kdeconnect.Helpers.SecurityHelpers.constantTimeCompare import org.kde.kdeconnect.Plugins.SftpPlugin.saf.SafFileSystemFactory import java.io.IOException import java.nio.charset.StandardCharsets +import java.nio.file.Path import java.security.GeneralSecurityException import java.security.KeyPair import java.security.MessageDigest @@ -47,7 +50,7 @@ internal class SimpleSftpServer { var isInitialized: Boolean = false - private val sshd: SshServer = SshServer.setUpDefaultServer() + private lateinit var sshd: SshServer private var safFileSystemFactory: SafFileSystemFactory? = null @@ -57,18 +60,18 @@ internal class SimpleSftpServer { @Throws(GeneralSecurityException::class) fun initialize(context: Context?, device: Device) { - sshd.signatureFactories = - listOf( - BuiltinSignatures.nistp256, - BuiltinSignatures.nistp384, - BuiltinSignatures.nistp521, - BuiltinSignatures.dsa, - SignatureRSASHA256.Factory, - BuiltinSignatures.rsa // Insecure SHA1, left for backwards compatibility + sshd = ServerBuilder.builder().apply { + signatureFactories( + listOf( + BuiltinSignatures.nistp256, + BuiltinSignatures.nistp384, + BuiltinSignatures.nistp521, + BuiltinSignatures.dsa, + SignatureRSASHA256.Factory, + BuiltinSignatures.rsa // Insecure SHA1, left for backwards compatibility + ) ) - - sshd.keyExchangeFactories = - listOf( + keyExchangeFactories(listOf( BuiltinDHFactories.ecdhp256, // ecdh-sha2-nistp256 BuiltinDHFactories.ecdhp384, // ecdh-sha2-nistp384 BuiltinDHFactories.ecdhp521, // ecdh-sha2-nistp521 @@ -76,8 +79,17 @@ internal class SimpleSftpServer { BuiltinDHFactories.dhg14, // Insecure diffie-hellman-group14-sha1, left for backwards-compatibility. ).map { DHGServer.newFactory(it) - } + }) + fileSystemFactory( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + NativeFileSystemFactory() + } else { + safFileSystemFactory = SafFileSystemFactory(context!!) + safFileSystemFactory // FIXME: This is not working + } + ) + }.build() // Reuse this device keys for the ssh connection as well val keyPair = KeyPair( @@ -85,18 +97,12 @@ internal class SimpleSftpServer { RsaHelper.getPrivateKey(context) ) sshd.keyPairProvider = object : AbstractKeyPairProvider() { - override fun loadKeys(): Iterable = listOf(keyPair) + override fun loadKeys(session: SessionContext): Iterable = listOf(keyPair) } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - sshd.fileSystemFactory = NativeFileSystemFactory() - } else { - safFileSystemFactory = SafFileSystemFactory(context!!) - sshd.fileSystemFactory = safFileSystemFactory // FIXME: This is not working - } sshd.commandFactory = ScpCommandFactory() sshd.subsystemFactories = - listOf>(SftpSubsystemFactory()) + listOf(SftpSubsystemFactory()) keyAuth.deviceKey = device.certificate.publicKey @@ -182,7 +188,8 @@ internal class SimpleSftpServer { const val USER: String = "kdeconnect" init { - SecurityUtils.setRegisterBouncyCastle(false) + System.setProperty(SECURITY_PROVIDER_REGISTRARS, "") // disable BouncyCastle + PathUtils.setUserHomeFolderResolver { Path.of("/") } // TODO: Remove it when SSHD Core is fixed } } } diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystem.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystem.kt index 49fb8521..03294caa 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystem.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystem.kt @@ -3,7 +3,6 @@ package org.kde.kdeconnect.Plugins.SftpPlugin.saf import android.content.Context import android.util.Log import org.apache.sshd.common.file.util.BaseFileSystem -import org.apache.sshd.common.file.util.ImmutableList import java.nio.file.attribute.UserPrincipalLookupService import java.nio.file.spi.FileSystemProvider @@ -26,7 +25,7 @@ class SafFileSystem( throw UnsupportedOperationException("SAF does not support user principal lookup") } - override fun create(root: String, names: ImmutableList): SafPath { + override fun create(root: String, names: List): SafPath { Log.v(TAG, "create: $root, $names") return SafPath(this, root, names) } diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemFactory.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemFactory.kt index ff8f3522..9289c66b 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemFactory.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemFactory.kt @@ -8,10 +8,11 @@ package org.kde.kdeconnect.Plugins.SftpPlugin.saf import android.content.Context import android.util.Log -import org.apache.sshd.common.session.Session import org.apache.sshd.common.file.FileSystemFactory +import org.apache.sshd.common.session.SessionContext import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin import java.nio.file.FileSystem +import java.nio.file.Path class SafFileSystemFactory(private val context: Context) : FileSystemFactory { private val provider = SafFileSystemProvider() @@ -38,11 +39,13 @@ class SafFileSystemFactory(private val context: Context) : FileSystemFactory { } } - override fun createFileSystem(session: Session): FileSystem { - return SafFileSystem(provider, roots, session.username, context) + override fun createFileSystem(session: SessionContext?): FileSystem { + return SafFileSystem(provider, roots, session!!.username, context) } companion object { private const val TAG = "SafFileSystemFactory" } + + override fun getUserHomeDir(session: SessionContext?): Path? = null } \ No newline at end of file diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt index d2593af3..651ed6ef 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt @@ -1,13 +1,12 @@ package org.kde.kdeconnect.Plugins.SftpPlugin.saf import org.apache.sshd.common.file.util.BasePath -import org.apache.sshd.common.file.util.ImmutableList import java.nio.file.LinkOption import java.nio.file.Path class SafPath( fileSystem: SafFileSystem, - root: String, names: ImmutableList + root: String, names: List ) : BasePath(fileSystem, root, names) { override fun toRealPath(vararg options: LinkOption?): Path { return this // FIXME