From 6e9705faf516f655226a90c6392e8f770feff9e5 Mon Sep 17 00:00:00 2001
From: Andrey Petrov <andrey.petrov@shazow.net>
Date: Thu, 16 Apr 2020 12:32:12 -0400
Subject: [PATCH] main: Clarify passphrase shenanigans

---
 cmd/ssh-chat/cmd.go | 57 ++++++++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 29 deletions(-)

diff --git a/cmd/ssh-chat/cmd.go b/cmd/ssh-chat/cmd.go
index 91813de..1a2134b 100644
--- a/cmd/ssh-chat/cmd.go
+++ b/cmd/ssh-chat/cmd.go
@@ -120,37 +120,36 @@ func main() {
 		if options.Whitelist != "" {
 			logger.Warning("Passphrase is disabled while whitelist is enabled.")
 		}
-		{
-			cb := config.KeyboardInteractiveCallback
-			config.KeyboardInteractiveCallback = func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
-				perm, err := cb(conn, challenge)
-				if err != nil {
-					return perm, err
-				}
-				answers, err := challenge("", "", []string{"Passphrase required to connect: "}, []bool{true})
-				if err != nil {
-					return nil, err
-				}
-				if len(answers) == 1 && answers[0] == options.Passphrase {
-					// Success
-					return perm, nil
-				}
-				// It's not gonna do much but may as well throttle brute force attempts a little
-				time.Sleep(2 * time.Second)
-
-				return nil, errors.New("incorrect passphrase")
-			}
+		if config.KeyboardInteractiveCallback != nil {
+			fail(1, "Passphrase authentication conflicts with existing KeyboardInteractive setup.") // This should not happen
 		}
-		{
-			// We also need to override the PublicKeyCallback to prevent rando pubkeys from bypassing
-			cb := config.PublicKeyCallback
-			config.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
-				perms, err := cb(conn, key)
-				if err == nil {
-					err = errors.New("passphrase authentication required")
-				}
-				return perms, err
+
+		// We use KeyboardInteractiveCallback instead of PasswordCallback to
+		// avoid preventing the client from including a pubkey in the user
+		// identification.
+		config.KeyboardInteractiveCallback = func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
+			answers, err := challenge("", "", []string{"Passphrase required to connect: "}, []bool{true})
+			if err != nil {
+				return nil, err
 			}
+			if len(answers) == 1 && answers[0] == options.Passphrase {
+				// Success
+				return nil, nil
+			}
+			// It's not gonna do much but may as well throttle brute force attempts a little
+			time.Sleep(2 * time.Second)
+
+			return nil, errors.New("incorrect passphrase")
+		}
+
+		// We also need to override the PublicKeyCallback to prevent rando pubkeys from bypassing
+		cb := config.PublicKeyCallback
+		config.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
+			perms, err := cb(conn, key)
+			if err == nil {
+				err = errors.New("passphrase authentication required")
+			}
+			return perms, err
 		}
 
 	}