@@ -16,6 +16,7 @@ import (
1616 "runtime"
1717 "strings"
1818 "testing"
19+ "time"
1920)
2021
2122type keyboardInteractive map [string ]string
@@ -1293,3 +1294,97 @@ func TestCertAuthOpenSSHCompat(t *testing.T) {
12931294 t .Fatalf ("unable to dial remote side: %s" , err )
12941295 }
12951296}
1297+
1298+ func TestKeyboardInteractiveAuthEarlyFail (t * testing.T ) {
1299+ const maxAuthTries = 2
1300+
1301+ c1 , c2 , err := netPipe ()
1302+ if err != nil {
1303+ t .Fatalf ("netPipe: %v" , err )
1304+ }
1305+ defer c1 .Close ()
1306+ defer c2 .Close ()
1307+
1308+ // Start testserver
1309+ serverConfig := & ServerConfig {
1310+ MaxAuthTries : maxAuthTries ,
1311+ KeyboardInteractiveCallback : func (c ConnMetadata ,
1312+ client KeyboardInteractiveChallenge ) (* Permissions , error ) {
1313+ // Fail keyboard-interactive authentication early before
1314+ // any prompt is sent to client.
1315+ return nil , errors .New ("keyboard-interactive auth failed" )
1316+ },
1317+ PasswordCallback : func (c ConnMetadata ,
1318+ pass []byte ) (* Permissions , error ) {
1319+ if string (pass ) == clientPassword {
1320+ return nil , nil
1321+ }
1322+ return nil , errors .New ("password auth failed" )
1323+ },
1324+ }
1325+ serverConfig .AddHostKey (testSigners ["rsa" ])
1326+
1327+ serverDone := make (chan struct {})
1328+ go func () {
1329+ defer func () { serverDone <- struct {}{} }()
1330+ conn , chans , reqs , err := NewServerConn (c2 , serverConfig )
1331+ if err != nil {
1332+ return
1333+ }
1334+ _ = conn .Close ()
1335+
1336+ discarderDone := make (chan struct {})
1337+ go func () {
1338+ defer func () { discarderDone <- struct {}{} }()
1339+ DiscardRequests (reqs )
1340+ }()
1341+ for newChannel := range chans {
1342+ newChannel .Reject (Prohibited ,
1343+ "testserver not accepting requests" )
1344+ }
1345+
1346+ <- discarderDone
1347+ }()
1348+
1349+ // Connect to testserver, expect KeyboardInteractive() to be not called,
1350+ // PasswordCallback() to be called and connection to succeed.
1351+ passwordCallbackCalled := false
1352+ clientConfig := & ClientConfig {
1353+ User : "testuser" ,
1354+ Auth : []AuthMethod {
1355+ RetryableAuthMethod (KeyboardInteractive (func (name ,
1356+ instruction string , questions []string ,
1357+ echos []bool ) ([]string , error ) {
1358+ t .Errorf ("unexpected call to KeyboardInteractive()" )
1359+ return []string {clientPassword }, nil
1360+ }), maxAuthTries ),
1361+ RetryableAuthMethod (PasswordCallback (func () (secret string ,
1362+ err error ) {
1363+ t .Logf ("PasswordCallback()" )
1364+ passwordCallbackCalled = true
1365+ return clientPassword , nil
1366+ }), maxAuthTries ),
1367+ },
1368+ HostKeyCallback : InsecureIgnoreHostKey (),
1369+ }
1370+
1371+ conn , _ , _ , err := NewClientConn (c1 , "" , clientConfig )
1372+ if err != nil {
1373+ t .Errorf ("unexpected NewClientConn() error: %v" , err )
1374+ }
1375+ if conn != nil {
1376+ conn .Close ()
1377+ }
1378+
1379+ // Wait for server to finish. Fail test eventually in case server does not
1380+ // finish in reasonable time.
1381+ select {
1382+ case <- serverDone :
1383+ case <- time .After (60 * time .Second ):
1384+ t .Fatalf ("server did not finish" )
1385+ }
1386+
1387+ if ! passwordCallbackCalled {
1388+ t .Errorf ("expected PasswordCallback() to be called" )
1389+ }
1390+ }
0 commit comments