go.crypto/ssh: implement keyboard-interactive auth (RFC 4256), both

on client and server-side.

R=dave, agl
CC=gobot, golang-dev
https://golang.org/cl/9853050
This commit is contained in:
Han-Wen Nienhuys
2013-06-06 10:44:12 -04:00
committed by Adam Langley
parent 36bf31eb71
commit c7df565dd4
5 changed files with 284 additions and 0 deletions
+60
View File
@@ -112,6 +112,16 @@ func (p password) Password(user string) (string, error) {
return string(p), nil
}
type keyboardInteractive map[string]string
func (cr *keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) {
var answers []string
for _, q := range questions {
answers = append(answers, (*cr)[q])
}
return answers, nil
}
// reused internally by tests
var (
rsakey *rsa.PrivateKey
@@ -128,6 +138,18 @@ var (
algoname := algoName(key)
return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected)
},
KeyboardInteractiveCallback: func(conn *ServerConn, user string, client ClientKeyboardInteractive) bool {
ans, err := client.Challenge("user",
"instruction",
[]string{"question1", "question2"},
[]bool{true, true})
if err != nil {
return false
}
ok := user == "testuser" && ans[0] == "answer1" && ans[1] == "answer2"
client.Challenge("user", "motd", nil, nil)
return ok
},
}
)
@@ -221,6 +243,44 @@ func TestClientAuthWrongPassword(t *testing.T) {
c.Close()
}
func TestClientAuthKeyboardInteractive(t *testing.T) {
answers := keyboardInteractive(map[string]string{
"question1": "answer1",
"question2": "answer2",
})
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
ClientAuthKeyboardInteractive(&answers),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
if err != nil {
t.Fatalf("unable to dial remote side: %s", err)
}
c.Close()
}
func TestClientAuthWrongKeyboardInteractive(t *testing.T) {
answers := keyboardInteractive(map[string]string{
"question1": "answer1",
"question2": "WRONG",
})
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
ClientAuthKeyboardInteractive(&answers),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
if err == nil {
c.Close()
t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive")
}
}
// the mock server will only authenticate ssh-rsa keys
func TestClientAuthInvalidPublickey(t *testing.T) {
kc := new(keychain)