@@ -16,6 +16,7 @@ import (
16
16
"runtime"
17
17
"strings"
18
18
"testing"
19
+ "time"
19
20
)
20
21
21
22
type keyboardInteractive map [string ]string
@@ -1293,3 +1294,97 @@ func TestCertAuthOpenSSHCompat(t *testing.T) {
1293
1294
t .Fatalf ("unable to dial remote side: %s" , err )
1294
1295
}
1295
1296
}
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