summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Henneke <FabianHenneke@users.noreply.github.com>2020-09-08 12:08:06 +0200
committerGitHub <noreply@github.com>2020-09-08 15:38:06 +0530
commit9e0fb93f910bd1e890103d44fda56f52ab5c821e (patch)
treec0697532439964efb11cf3369629fc92457d79e4
parentff780b02de77ae0adde23e57d9cc3245552c0f1c (diff)
Support multiple authentication methods (#825)
* Offer password SSH authentication after publickey * git: re-add back button handling Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Hide unsupported authentication methods Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * GitCommandExecutor: cleanup and address build warning Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Address review comments Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * DecryptActivity: hide menu items until decrypt finishes Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Add changelog entry Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt20
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/sshj/OpenKeychainKeyProvider.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/sshj/SshjSessionFactory.kt37
4 files changed, 32 insertions, 28 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04ac0340..01ad9680 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Add [Bromite](https://www.bromite.org/) and [Ungoogled Chromium](https://git.droidware.info/wchen342/ungoogled-chromium-android) to supported browsers list for Autofill
- Add ability to view the Git commit log
- Allow generating ECDSA and ED25519 keys for SSH
+- Add support for multiple/fallback authentication methods for SSH
### Changed
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<TransportCommand<*, *>>().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)
}
}
}