From 9e0fb93f910bd1e890103d44fda56f52ab5c821e Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Tue, 8 Sep 2020 12:08:06 +0200 Subject: Support multiple authentication methods (#825) * Offer password SSH authentication after publickey * git: re-add back button handling Signed-off-by: Harsh Shandilya * Hide unsupported authentication methods Signed-off-by: Harsh Shandilya * GitCommandExecutor: cleanup and address build warning Signed-off-by: Harsh Shandilya * Address review comments Signed-off-by: Harsh Shandilya * DecryptActivity: hide menu items until decrypt finishes Signed-off-by: Harsh Shandilya * Add changelog entry Signed-off-by: Harsh Shandilya Co-authored-by: Harsh Shandilya --- .../zeapo/pwdstore/git/operation/GitOperation.kt | 20 +++++------- .../pwdstore/git/sshj/OpenKeychainKeyProvider.kt | 2 +- .../zeapo/pwdstore/git/sshj/SshjSessionFactory.kt | 37 +++++++++++++--------- 3 files changed, 31 insertions(+), 28 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt index ad6a3e29..85c38c6a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt @@ -19,7 +19,7 @@ import com.zeapo.pwdstore.UserPreference import com.zeapo.pwdstore.git.GitCommandExecutor import com.zeapo.pwdstore.git.config.AuthMode import com.zeapo.pwdstore.git.config.GitSettings -import com.zeapo.pwdstore.git.sshj.SshAuthData +import com.zeapo.pwdstore.git.sshj.SshAuthMethod import com.zeapo.pwdstore.git.sshj.SshKey import com.zeapo.pwdstore.git.sshj.SshjSessionFactory import com.zeapo.pwdstore.utils.BiometricAuthenticator @@ -99,8 +99,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { } } - private fun registerAuthProviders(authData: SshAuthData, credentialsProvider: CredentialsProvider? = null) { - sshSessionFactory = SshjSessionFactory(authData, hostKeyFile) + private fun registerAuthProviders(authMethod: SshAuthMethod, credentialsProvider: CredentialsProvider? = null) { + sshSessionFactory = SshjSessionFactory(authMethod, hostKeyFile) commands.filterIsInstance>().forEach { command -> command.setTransportConfigCallback { transport: Transport -> (transport as? SshTransport)?.sshSessionFactory = sshSessionFactory @@ -154,8 +154,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { } when (result) { is BiometricAuthenticator.Result.Success -> { - registerAuthProviders( - SshAuthData.SshKey(CredentialFinder(callingActivity, AuthMode.SshKey))) + registerAuthProviders(SshAuthMethod.SshKey(callingActivity)) } is BiometricAuthenticator.Result.Cancelled -> { return Err(SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER)) @@ -173,7 +172,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { } } } else { - registerAuthProviders(SshAuthData.SshKey(CredentialFinder(callingActivity, AuthMode.SshKey))) + registerAuthProviders(SshAuthMethod.SshKey(callingActivity)) } } else { onMissingSshKeyFile() @@ -181,13 +180,10 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) { // error, allowing users to make the SSH key selection. return Err(SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER)) } - AuthMode.OpenKeychain -> registerAuthProviders(SshAuthData.OpenKeychain(callingActivity)) + AuthMode.OpenKeychain -> registerAuthProviders(SshAuthMethod.OpenKeychain(callingActivity)) AuthMode.Password -> { - val credentialFinder = CredentialFinder(callingActivity, AuthMode.Password) - val httpsCredentialProvider = HttpsCredentialsProvider(credentialFinder) - registerAuthProviders( - SshAuthData.Password(CredentialFinder(callingActivity, AuthMode.Password)), - httpsCredentialProvider) + val httpsCredentialProvider = HttpsCredentialsProvider(CredentialFinder(callingActivity, AuthMode.Password)) + registerAuthProviders(SshAuthMethod.Password(callingActivity), httpsCredentialProvider) } AuthMode.None -> { } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/sshj/OpenKeychainKeyProvider.kt b/app/src/main/java/com/zeapo/pwdstore/git/sshj/OpenKeychainKeyProvider.kt index 5cb1b006..cdaa0342 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/sshj/OpenKeychainKeyProvider.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/sshj/OpenKeychainKeyProvider.kt @@ -39,7 +39,7 @@ import org.openintents.ssh.authentication.response.Response import org.openintents.ssh.authentication.response.SigningResponse import org.openintents.ssh.authentication.response.SshPublicKeyResponse -class OpenKeychainKeyProvider private constructor(private val activity: FragmentActivity) : KeyProvider, Closeable { +class OpenKeychainKeyProvider private constructor(activity: FragmentActivity) : KeyProvider, Closeable { companion object { diff --git a/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshjSessionFactory.kt b/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshjSessionFactory.kt index a507d50b..f064df33 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshjSessionFactory.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/sshj/SshjSessionFactory.kt @@ -10,6 +10,8 @@ import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.w import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.runCatching +import com.zeapo.pwdstore.git.config.AuthMode +import com.zeapo.pwdstore.git.operation.CredentialFinder import java.io.File import java.io.IOException import java.io.InputStream @@ -28,6 +30,8 @@ import net.schmizz.sshj.common.SecurityUtils import net.schmizz.sshj.connection.channel.direct.Session import net.schmizz.sshj.transport.verification.FingerprintVerifier import net.schmizz.sshj.transport.verification.HostKeyVerifier +import net.schmizz.sshj.userauth.method.AuthPassword +import net.schmizz.sshj.userauth.method.AuthPublickey import net.schmizz.sshj.userauth.password.PasswordFinder import net.schmizz.sshj.userauth.password.Resource import org.eclipse.jgit.transport.CredentialsProvider @@ -36,10 +40,10 @@ import org.eclipse.jgit.transport.SshSessionFactory import org.eclipse.jgit.transport.URIish import org.eclipse.jgit.util.FS -sealed class SshAuthData { - class Password(val passwordFinder: InteractivePasswordFinder) : SshAuthData() - class SshKey(val passphraseFinder: InteractivePasswordFinder) : SshAuthData() - class OpenKeychain(val activity: FragmentActivity) : SshAuthData() +sealed class SshAuthMethod(val activity: FragmentActivity) { + class Password(activity: FragmentActivity) : SshAuthMethod(activity) + class SshKey(activity: FragmentActivity) : SshAuthMethod(activity) + class OpenKeychain(activity: FragmentActivity) : SshAuthMethod(activity) } abstract class InteractivePasswordFinder : PasswordFinder { @@ -62,12 +66,12 @@ abstract class InteractivePasswordFinder : PasswordFinder { final override fun shouldRetry(resource: Resource<*>?) = true } -class SshjSessionFactory(private val authData: SshAuthData, private val hostKeyFile: File) : SshSessionFactory() { +class SshjSessionFactory(private val authMethod: SshAuthMethod, private val hostKeyFile: File) : SshSessionFactory() { private var currentSession: SshjSession? = null override fun getSession(uri: URIish, credentialsProvider: CredentialsProvider?, fs: FS?, tms: Int): RemoteSession { - return currentSession ?: SshjSession(uri, uri.user, authData, hostKeyFile).connect().also { + return currentSession ?: SshjSession(uri, uri.user, authMethod, hostKeyFile).connect().also { d { "New SSH connection created" } currentSession = it } @@ -100,7 +104,7 @@ private fun makeTofuHostKeyVerifier(hostKeyFile: File): HostKeyVerifier { } } -private class SshjSession(uri: URIish, private val username: String, private val authData: SshAuthData, private val hostKeyFile: File) : RemoteSession { +private class SshjSession(uri: URIish, private val username: String, private val authMethod: SshAuthMethod, private val hostKeyFile: File) : RemoteSession { private lateinit var ssh: SSHClient private var currentCommand: Session? = null @@ -124,17 +128,20 @@ private class SshjSession(uri: URIish, private val username: String, private val ssh.connect(uri.host, uri.port.takeUnless { it == -1 } ?: 22) if (!ssh.isConnected) throw IOException() - when (authData) { - is SshAuthData.Password -> { - ssh.authPassword(username, authData.passwordFinder) + val passwordAuth = AuthPassword(CredentialFinder(authMethod.activity, AuthMode.Password)) + when (authMethod) { + is SshAuthMethod.Password -> { + ssh.auth(username, passwordAuth) } - is SshAuthData.SshKey -> { - ssh.authPublickey(username, SshKey.provide(ssh, authData.passphraseFinder)) + is SshAuthMethod.SshKey -> { + val pubkeyAuth = AuthPublickey(SshKey.provide(ssh, CredentialFinder(authMethod.activity, AuthMode.SshKey))) + ssh.auth(username, pubkeyAuth, passwordAuth) } - is SshAuthData.OpenKeychain -> { + is SshAuthMethod.OpenKeychain -> { runBlocking { - OpenKeychainKeyProvider.prepareAndUse(authData.activity) { provider -> - ssh.authPublickey(username, provider) + OpenKeychainKeyProvider.prepareAndUse(authMethod.activity) { provider -> + val openKeychainAuth = AuthPublickey(provider) + ssh.auth(username, openKeychainAuth, passwordAuth) } } } -- cgit v1.2.3